Skip to content

Commit 593e02f

Browse files
authored
Merge branch 'main' into handle-streamed-thinking-over-multiple-chunks
2 parents 2674084 + 86b645f commit 593e02f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

71 files changed

+2601
-729
lines changed

docs/agents.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -904,14 +904,15 @@ You should use:
904904

905905
In general, we recommend using `instructions` instead of `system_prompt` unless you have a specific reason to use `system_prompt`.
906906

907-
Instructions, like system prompts, fall into two categories:
907+
Instructions, like system prompts, can be specified at different times:
908908

909909
1. **Static instructions**: These are known when writing the code and can be defined via the `instructions` parameter of the [`Agent` constructor][pydantic_ai.Agent.__init__].
910910
2. **Dynamic instructions**: These rely on context that is only available at runtime and should be defined using functions decorated with [`@agent.instructions`][pydantic_ai.Agent.instructions]. Unlike dynamic system prompts, which may be reused when `message_history` is present, dynamic instructions are always reevaluated.
911+
3. **Runtime instructions*: These are additional instructions for a specific run that can be passed to one of the [run methods](#running-agents) using the `instructions` argument.
911912

912-
Both static and dynamic instructions can be added to a single agent, and they are appended in the order they are defined at runtime.
913+
All three types of instructions can be added to a single agent, and they are appended in the order they are defined at runtime.
913914

914-
Here's an example using both types of instructions:
915+
Here's an example using a static instruction as well as dynamic instructions:
915916

916917
```python {title="instructions.py"}
917918
from datetime import date

docs/api/models/function.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,14 @@ async def model_function(
3636
print(info)
3737
"""
3838
AgentInfo(
39-
function_tools=[], allow_text_output=True, output_tools=[], model_settings=None
39+
function_tools=[],
40+
allow_text_output=True,
41+
output_tools=[],
42+
model_settings=None,
43+
model_request_parameters=ModelRequestParameters(
44+
function_tools=[], builtin_tools=[], output_tools=[]
45+
),
46+
instructions=None,
4047
)
4148
"""
4249
return ModelResponse(parts=[TextPart('hello world')])

docs/builtin-tools.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ making it ideal for queries that require up-to-date data.
3131
|----------|-----------|-------|
3232
| OpenAI Responses || Full feature support. To include search results on the [`BuiltinToolReturnPart`][pydantic_ai.messages.BuiltinToolReturnPart] that's available via [`ModelResponse.builtin_tool_calls`][pydantic_ai.messages.ModelResponse.builtin_tool_calls], enable the [`OpenAIResponsesModelSettings.openai_include_web_search_sources`][pydantic_ai.models.openai.OpenAIResponsesModelSettings.openai_include_web_search_sources] [model setting](agents.md#model-run-settings). |
3333
| Anthropic || Full feature support |
34-
| Google || No parameter support. No [`BuiltinToolCallPart`][pydantic_ai.messages.BuiltinToolCallPart] or [`BuiltinToolReturnPart`][pydantic_ai.messages.BuiltinToolReturnPart] is generated when streaming. Using built-in tools and user tools (including [output tools](output.md#tool-output)) at the same time is not supported; to use structured output, use [`PromptedOutput`](output.md#prompted-output) instead. |
34+
| Google || No parameter support. No [`BuiltinToolCallPart`][pydantic_ai.messages.BuiltinToolCallPart] or [`BuiltinToolReturnPart`][pydantic_ai.messages.BuiltinToolReturnPart] is generated when streaming. Using built-in tools and function tools (including [output tools](output.md#tool-output)) at the same time is not supported; to use structured output, use [`PromptedOutput`](output.md#prompted-output) instead. |
3535
| Groq || Limited parameter support. To use web search capabilities with Groq, you need to use the [compound models](https://console.groq.com/docs/compound). |
3636
| OpenAI Chat Completions || Not supported |
3737
| Bedrock || Not supported |
@@ -123,7 +123,7 @@ in a secure environment, making it perfect for computational tasks, data analysi
123123
| Provider | Supported | Notes |
124124
|----------|-----------|-------|
125125
| OpenAI || To include code execution output on the [`BuiltinToolReturnPart`][pydantic_ai.messages.BuiltinToolReturnPart] that's available via [`ModelResponse.builtin_tool_calls`][pydantic_ai.messages.ModelResponse.builtin_tool_calls], enable the [`OpenAIResponsesModelSettings.openai_include_code_execution_outputs`][pydantic_ai.models.openai.OpenAIResponsesModelSettings.openai_include_code_execution_outputs] [model setting](agents.md#model-run-settings). If the code execution generated images, like charts, they will be available on [`ModelResponse.images`][pydantic_ai.messages.ModelResponse.images] as [`BinaryImage`][pydantic_ai.messages.BinaryImage] objects. The generated image can also be used as [image output](output.md#image-output) for the agent run. |
126-
| Google || Using built-in tools and user tools (including [output tools](output.md#tool-output)) at the same time is not supported; to use structured output, use [`PromptedOutput`](output.md#prompted-output) instead. |
126+
| Google || Using built-in tools and function tools (including [output tools](output.md#tool-output)) at the same time is not supported; to use structured output, use [`PromptedOutput`](output.md#prompted-output) instead. |
127127
| Anthropic || |
128128
| Groq || |
129129
| Bedrock || |
@@ -315,7 +315,7 @@ allowing it to pull up-to-date information from the web.
315315

316316
| Provider | Supported | Notes |
317317
|----------|-----------|-------|
318-
| Google || No [`BuiltinToolCallPart`][pydantic_ai.messages.BuiltinToolCallPart] or [`BuiltinToolReturnPart`][pydantic_ai.messages.BuiltinToolReturnPart] is currently generated; please submit an issue if you need this. Using built-in tools and user tools (including [output tools](output.md#tool-output)) at the same time is not supported; to use structured output, use [`PromptedOutput`](output.md#prompted-output) instead. |
318+
| Google || No [`BuiltinToolCallPart`][pydantic_ai.messages.BuiltinToolCallPart] or [`BuiltinToolReturnPart`][pydantic_ai.messages.BuiltinToolReturnPart] is currently generated; please submit an issue if you need this. Using built-in tools and function tools (including [output tools](output.md#tool-output)) at the same time is not supported; to use structured output, use [`PromptedOutput`](output.md#prompted-output) instead. |
319319
| OpenAI || |
320320
| Anthropic || |
321321
| Groq || |

docs/deferred-tools.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -142,18 +142,18 @@ print(result.all_messages())
142142
),
143143
ModelRequest(
144144
parts=[
145-
ToolReturnPart(
146-
tool_name='delete_file',
147-
content='Deleting files is not allowed',
148-
tool_call_id='delete_file',
149-
timestamp=datetime.datetime(...),
150-
),
151145
ToolReturnPart(
152146
tool_name='update_file',
153147
content="File '.env' updated: ''",
154148
tool_call_id='update_file_dotenv',
155149
timestamp=datetime.datetime(...),
156150
),
151+
ToolReturnPart(
152+
tool_name='delete_file',
153+
content='Deleting files is not allowed',
154+
tool_call_id='delete_file',
155+
timestamp=datetime.datetime(...),
156+
),
157157
]
158158
),
159159
ModelResponse(

docs/durable_execution/temporal.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ As workflows and activities run in separate processes, any values passed between
172172

173173
To account for these limitations, tool functions and the [event stream handler](#streaming) running inside activities receive a limited version of the agent's [`RunContext`][pydantic_ai.tools.RunContext], and it's your responsibility to make sure that the [dependencies](../dependencies.md) object provided to [`TemporalAgent.run()`][pydantic_ai.durable_exec.temporal.TemporalAgent.run] can be serialized using Pydantic.
174174

175-
Specifically, only the `deps`, `retries`, `tool_call_id`, `tool_name`, `tool_call_approved`, `retry`, `max_retries` and `run_step` fields are available by default, and trying to access `model`, `usage`, `prompt`, `messages`, or `tracer` will raise an error.
175+
Specifically, only the `deps`, `retries`, `tool_call_id`, `tool_name`, `tool_call_approved`, `retry`, `max_retries`, `run_step` and `partial_output` fields are available by default, and trying to access `model`, `usage`, `prompt`, `messages`, or `tracer` will raise an error.
176176
If you need one or more of these attributes to be available inside activities, you can create a [`TemporalRunContext`][pydantic_ai.durable_exec.temporal.TemporalRunContext] subclass with custom `serialize_run_context` and `deserialize_run_context` class methods and pass it to [`TemporalAgent`][pydantic_ai.durable_exec.temporal.TemporalAgent] as `run_context_type`.
177177

178178
### Streaming

docs/logfire.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,7 @@ The following providers have dedicated documentation on Pydantic AI:
263263
- [Agenta](https://docs.agenta.ai/observability/integrations/pydanticai)
264264
- [Confident AI](https://documentation.confident-ai.com/docs/llm-tracing/integrations/pydanticai)
265265
- [LangWatch](https://docs.langwatch.ai/integration/python/integrations/pydantic-ai)
266+
- [Braintrust](https://www.braintrust.dev/docs/integrations/sdk-integrations/pydantic-ai)
266267

267268
## Advanced usage
268269

docs/models/overview.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,12 @@ You can use [`FallbackModel`][pydantic_ai.models.fallback.FallbackModel] to atte
8686
in sequence until one successfully returns a result. Under the hood, Pydantic AI automatically switches
8787
from one model to the next if the current model returns a 4xx or 5xx status code.
8888

89+
!!! note
90+
91+
The provider SDKs on which Models are based (like OpenAI, Anthropic, etc.) often have built-in retry logic that can delay the `FallbackModel` from activating.
92+
93+
When using `FallbackModel`, it's recommended to disable provider SDK retries to ensure immediate fallback, for example by setting `max_retries=0` on a [custom OpenAI client](openai.md#custom-openai-client).
94+
8995
In the following example, the agent first makes a request to the OpenAI model (which fails due to an invalid API key),
9096
and then falls back to the Anthropic model.
9197

docs/output.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -470,6 +470,40 @@ print(result.output)
470470

471471
_(This example is complete, it can be run "as is")_
472472

473+
#### Handling partial output in output validators {#partial-output}
474+
475+
You can use the `partial_output` field on `RunContext` to handle validation differently for partial outputs during streaming (e.g. skip validation altogether).
476+
477+
```python {title="partial_validation_streaming.py" line_length="120"}
478+
from pydantic_ai import Agent, ModelRetry, RunContext
479+
480+
agent = Agent('openai:gpt-5')
481+
482+
@agent.output_validator
483+
def validate_output(ctx: RunContext, output: str) -> str:
484+
if ctx.partial_output:
485+
return output
486+
else:
487+
if len(output) < 50:
488+
raise ModelRetry('Output is too short.')
489+
return output
490+
491+
492+
async def main():
493+
async with agent.run_stream('Write a long story about a cat') as result:
494+
async for message in result.stream_text():
495+
print(message)
496+
#> Once upon a
497+
#> Once upon a time, there was
498+
#> Once upon a time, there was a curious cat
499+
#> Once upon a time, there was a curious cat named Whiskers who
500+
#> Once upon a time, there was a curious cat named Whiskers who loved to explore
501+
#> Once upon a time, there was a curious cat named Whiskers who loved to explore the world around
502+
#> Once upon a time, there was a curious cat named Whiskers who loved to explore the world around him...
503+
```
504+
505+
_(This example is complete, it can be run "as is" — you'll need to add `asyncio.run(main())` to run `main`)_
506+
473507
## Image output
474508

475509
Some models can generate images as part of their response, for example those that support the [Image Generation built-in tool](builtin-tools.md#image-generation-tool) and OpenAI models using the [Code Execution built-in tool](builtin-tools.md#code-execution-tool) when told to generate a chart.

docs/thinking.md

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,64 @@ agent = Agent(model, model_settings=settings)
6666

6767
## Bedrock
6868

69+
Although Bedrock Converse doesn't provide a unified API to enable thinking, you can still use [`BedrockModelSettings.bedrock_additional_model_requests_fields`][pydantic_ai.models.bedrock.BedrockModelSettings.bedrock_additional_model_requests_fields] [model setting](agents.md#model-run-settings) to pass provider-specific configuration:
70+
71+
=== "Claude"
72+
73+
```python {title="bedrock_claude_thinking_part.py"}
74+
from pydantic_ai import Agent
75+
from pydantic_ai.models.bedrock import BedrockConverseModel, BedrockModelSettings
76+
77+
model = BedrockConverseModel('us.anthropic.claude-sonnet-4-5-20250929-v1:0')
78+
model_settings = BedrockModelSettings(
79+
bedrock_additional_model_requests_fields={
80+
'thinking': {'type': 'enabled', 'budget_tokens': 1024}
81+
}
82+
)
83+
agent = Agent(model=model, model_settings=model_settings)
84+
85+
```
86+
=== "OpenAI"
87+
88+
89+
```python {title="bedrock_openai_thinking_part.py"}
90+
from pydantic_ai import Agent
91+
from pydantic_ai.models.bedrock import BedrockConverseModel, BedrockModelSettings
92+
93+
model = BedrockConverseModel('openai.gpt-oss-120b-1:0')
94+
model_settings = BedrockModelSettings(
95+
bedrock_additional_model_requests_fields={'reasoning_effort': 'low'}
96+
)
97+
agent = Agent(model=model, model_settings=model_settings)
98+
99+
```
100+
=== "Qwen"
101+
102+
103+
```python {title="bedrock_qwen_thinking_part.py"}
104+
from pydantic_ai import Agent
105+
from pydantic_ai.models.bedrock import BedrockConverseModel, BedrockModelSettings
106+
107+
model = BedrockConverseModel('qwen.qwen3-32b-v1:0')
108+
model_settings = BedrockModelSettings(
109+
bedrock_additional_model_requests_fields={'reasoning_config': 'high'}
110+
)
111+
agent = Agent(model=model, model_settings=model_settings)
112+
113+
```
114+
115+
=== "Deepseek"
116+
Reasoning is [always enabled](https://docs.aws.amazon.com/bedrock/latest/userguide/inference-reasoning.html) for Deepseek model
117+
118+
```python {title="bedrock_deepseek_thinking_part.py"}
119+
from pydantic_ai import Agent
120+
from pydantic_ai.models.bedrock import BedrockConverseModel
121+
122+
model = BedrockConverseModel('us.deepseek.r1-v1:0')
123+
agent = Agent(model=model)
124+
125+
```
126+
69127
## Groq
70128

71129
Groq supports different formats to receive thinking parts:

examples/pydantic_ai_examples/rag.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ async def build_search_db():
115115
async with httpx.AsyncClient() as client:
116116
response = await client.get(DOCS_JSON)
117117
response.raise_for_status()
118-
sections = sessions_ta.validate_json(response.content)
118+
sections = sections_ta.validate_json(response.content)
119119

120120
openai = AsyncOpenAI()
121121
logfire.instrument_openai(openai)
@@ -183,7 +183,7 @@ def embedding_content(self) -> str:
183183
return '\n\n'.join((f'path: {self.path}', f'title: {self.title}', self.content))
184184

185185

186-
sessions_ta = TypeAdapter(list[DocsSection])
186+
sections_ta = TypeAdapter(list[DocsSection])
187187

188188

189189
# pyright: reportUnknownMemberType=false

0 commit comments

Comments
 (0)