You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Support Agent(output_type=str | None) for optional output (#4042)
Co-authored-by: pavan <pavanmanikanta98@gmail.com>
Co-authored-by: David <64162682+dsfaccini@users.noreply.github.com>
Co-authored-by: Douwe Maan <douwe@pydantic.dev>
Co-authored-by: Douwe Maan <hi@douwe.me>
Some agents perform their work entirely through tool calls and don't need to produce a final output — for example, an agent that updates a record via a tool and then stops. Certain models (notably [Anthropic](models/anthropic.md)) will return an empty response in this case, which by default causes Pydantic AI to retry until the model produces content.
681
+
682
+
To instead treat an empty response as a successful run, include `None` in the `output_type`:
result = agent.run_sync('Mark task 1 as done, then stop without saying anything.')
697
+
print(result.output)
698
+
#> None
699
+
```
700
+
701
+
When the model returns an empty response and `None` is an allowed output type, the agent will return `None` instead of retrying. [Output validator functions](#output-validator-functions) still run with `None` as the argument, so you can raise [`ModelRetry`][pydantic_ai.exceptions.ModelRetry] to reject it if needed.
702
+
703
+
`output_type=str | None` is the canonical case: it's handled as regular text output, and the **only** way the model signals `None` is by returning an empty response — there's no output tool or structured schema involved. This mirrors how plain `str` is already treated specially as free-form text output rather than a structured tool call.
704
+
705
+
`None` is also supported in the other output modes, with an extra structured commit path in addition to (or in place of) the empty-response fallback:
706
+
707
+
-**Bare unions including `None` that use tool mode** — e.g. `output_type=int | None`, `output_type=[int, float, None]`, or `output_type=[ToolOutput(Foo), None]`: a dedicated `final_result_NoneType` output tool is exposed alongside the other output tools, so the model can commit to `None` through a tool call. An empty model response is still also treated as `None`, as with `str | None`.
708
+
-**Explicit output mode markers** — e.g. `output_type=ToolOutput(int | None)`, `output_type=NativeOutput([int, None])`, or `output_type=PromptedOutput([int, None])`: `None` is included as a branch of the structured schema the wrapper generates. The model commits by calling the tool with `null` (for `ToolOutput`) or by selecting the `NoneType` branch of the discriminated schema (for `NativeOutput`/`PromptedOutput`). An empty response is **not** accepted — once you've opted into an explicit structured output mode, the model is expected to commit through the schema.
709
+
710
+
!!! note
711
+
`output_type=None` on its own is not valid — at least one other output type must be provided alongside `None`.
712
+
713
+
!!! note
714
+
When using [`agent.run_stream()`][pydantic_ai.Agent.run_stream] with an optional output type, an empty model response has no intermediate values to yield, so [`stream_output()`][pydantic_ai.result.StreamedRunResult.stream_output] produces an empty iterator in this case. Use [`get_output()`][pydantic_ai.result.StreamedRunResult.get_output] to retrieve the final `None` value instead.
0 commit comments