From 9ff931ddf5b3a14847f53b579b5b091691e2f613 Mon Sep 17 00:00:00 2001 From: Magnus Hedemark Date: Thu, 9 Jul 2026 05:48:35 -0400 Subject: [PATCH] =?UTF-8?q?fix:=20SkillOpt=20Epoch=202=20=E2=80=94=20pydan?= =?UTF-8?q?ticai=20decision=20intelligence?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Edits accepted and merged: - Run method decision table (when to use run/run_sync/run_stream/run_stream_events/iter) - Graph API comparison table (BaseNode vs GraphBuilder trade-offs) - Error handling quick-pick with exception table and recovery patterns Version bumped from 1.0.2 to 1.0.3. All 3 validation tasks passed with no regressions (6/6, 10/10, 8/8 rubric items). Signed-off-by: Magnus Hedemark --- pydanticai/SKILL.md | 51 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/pydanticai/SKILL.md b/pydanticai/SKILL.md index e901f38..e501423 100644 --- a/pydanticai/SKILL.md +++ b/pydanticai/SKILL.md @@ -9,7 +9,7 @@ description: >- license: MIT metadata: source: https://pydantic.dev/docs/ai/overview/ - version: "1.0.1" + version: "1.0.3" compatibility: Python 3.10+; requires pydantic-ai or pydantic-ai-slim package --- @@ -109,6 +109,55 @@ class ProcessNode(BaseNode[MyState]): ``` → See `references/graph.md` for both BaseNode and GraphBuilder APIs, parallel execution, and join/reducer patterns. +### When to use which run method + +| When you need… | Use | Key behavior | +|---|---|---| +| A single answer, sync code | `run_sync()` | Blocks until complete, returns `RunResult` | +| A single answer, async code | `run()` | Async, returns `RunResult` | +| Stream text as it's generated | `run_stream()` | Async context manager, yields `stream_text()` / `stream_output()` | +| See granular events (tool calls, part starts, deltas) | `run_stream_events()` | Yields `AgentStreamEvent` types — `FunctionToolCallEvent`, `PartStartEvent`, `FinalResultEvent` | +| Manual control over each graph step | `iter()` | Iterate over agent's internal graph nodes (`UserPromptNode` → `ModelRequestNode` → `CallToolsNode`) | +| Tool calls to execute during streaming | `run_stream_events()` or `run(event_stream_handler=...)` | `run_stream()` stops at the first output that matches `output_type` and does NOT execute subsequent tool calls | + +*Details for each run method in `references/core-agents.md`.* + +### Graph API: BaseNode vs GraphBuilder + +| Factor | BaseNode (class-based) | GraphBuilder (function-based) | +|---|---|---| +| Style | Subclass `BaseNode[StateT]`, implement `async run()` | Decorate async functions with `@g.step` | +| State mutation | Via `ctx.state` inside `run()` method | Via `ctx.state` inside step function | +| Parallelism | Manual fork/join logic | Built-in `.map()` per-element fan-out and `.broadcast()` same-input-to-multiple | +| Joins / aggregation | Manual aggregation in return types | Built-in reducers: `reduce_list_append`, `reduce_sum`, `reduce_dict_update`, etc. | +| Edge declaration | Inferred from `run()` return type annotation | Explicit via `g.edge_from(source).to(target)` | +| When to use | Complex node logic, OO patterns, conditional edge logic | Simple linear flows, parallel data processing, concise syntax | + +*Both APIs in `references/graph.md`.* + +### Error handling quick-pick + +```python +from pydantic_ai import UnexpectedModelBehavior, capture_run_messages + +with capture_run_messages() as messages: + try: + result = agent.run_sync('Query') + except UnexpectedModelBehavior as e: + cause = e.__cause__ # Often ModelRetry('reason') + print(f"Root cause: {cause}") + print("Full conversation:", messages) # Inspect every message + # Common recovery: raise ModelRetry from tools with clear instructions +``` + +| Exception | Meaning | Recovery | +|---|---|---| +| `UnexpectedModelBehavior` | Retry limit exceeded or model gave unexpected response | Inspect `e.__cause__`, check messages, adjust instructions or tool retries | +| `ModelRetry` (raised from tools) | Tool wants model to retry with different args | Let it propagate — PydanticAI handles it automatically up to `retries` limit | +| `ModelAPIError` | Provider returned 4xx/5xx | Check API key, rate limits, model availability | +| `UsageLimitExceeded` | Token/request budget exhausted | Increase `UsageLimits` or optimize prompt | +| `HookTimeoutError` | A lifecycle hook timed out | Increase hook timeout or optimize hook logic | + ## Key CLI Commands ```bash