Skip to content

Commit 4edad0a

Browse files
committed
Merge branch 'main' into qian/dbos-agent
2 parents 6b1c20f + 46ba28f commit 4edad0a

File tree

193 files changed

+6657
-3413
lines changed

Some content is hidden

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

193 files changed

+6657
-3413
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ jobs:
137137
strategy:
138138
fail-fast: false
139139
matrix:
140-
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
140+
python-version: ["3.10", "3.11", "3.12", "3.13"]
141141
install:
142142
- name: pydantic-ai-slim
143143
command: "--package pydantic-ai-slim"
@@ -169,7 +169,6 @@ jobs:
169169

170170
- name: store coverage files
171171
uses: actions/upload-artifact@v4
172-
if: matrix.python-version != '3.9'
173172
with:
174173
name: coverage-${{ matrix.python-version }}-${{ matrix.install.name }}
175174
path: .coverage

CLAUDE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ This is a uv workspace with multiple packages:
9191
- **VCR cassettes**: `tests/cassettes/` for recorded LLM API interactions
9292
- **Test models**: Use `TestModel` for deterministic testing
9393
- **Examples testing**: `tests/test_examples.py` validates all documentation examples
94-
- **Multi-version testing**: Python 3.9-3.13 support
94+
- **Multi-version testing**: Python 3.10-3.13 support
9595

9696
## Key Configuration Files
9797

Makefile

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ install: .uv .pre-commit .deno ## Install the package, dependencies, and pre-com
1919

2020
.PHONY: install-all-python
2121
install-all-python: ## Install and synchronize an interpreter for every python version
22-
UV_PROJECT_ENVIRONMENT=.venv39 uv sync --python 3.9 --frozen --all-extras --all-packages --group lint --group docs
2322
UV_PROJECT_ENVIRONMENT=.venv310 uv sync --python 3.10 --frozen --all-extras --all-packages --group lint --group docs
2423
UV_PROJECT_ENVIRONMENT=.venv311 uv sync --python 3.11 --frozen --all-extras --all-packages --group lint --group docs
2524
UV_PROJECT_ENVIRONMENT=.venv312 uv sync --python 3.12 --frozen --all-extras --all-packages --group lint --group docs
@@ -60,13 +59,12 @@ typecheck-both: typecheck-pyright typecheck-mypy
6059

6160
.PHONY: test
6261
test: ## Run tests and collect coverage data
63-
uv run coverage run -m pytest -n auto --dist=loadgroup
62+
uv run coverage run -m pytest -n auto --dist=loadgroup --durations=20
6463
@uv run coverage combine
6564
@uv run coverage report
6665

6766
.PHONY: test-all-python
68-
test-all-python: ## Run tests on Python 3.9 to 3.13
69-
UV_PROJECT_ENVIRONMENT=.venv39 uv run --python 3.9 --all-extras --all-packages coverage run -p -m pytest
67+
test-all-python: ## Run tests on Python 3.10 to 3.13
7068
UV_PROJECT_ENVIRONMENT=.venv310 uv run --python 3.10 --all-extras --all-packages coverage run -p -m pytest
7169
UV_PROJECT_ENVIRONMENT=.venv311 uv run --python 3.11 --all-extras --all-packages coverage run -p -m pytest
7270
UV_PROJECT_ENVIRONMENT=.venv312 uv run --python 3.12 --all-extras --all-packages coverage run -p -m pytest

clai/pyproject.toml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ classifiers = [
2828
"Programming Language :: Python",
2929
"Programming Language :: Python :: 3",
3030
"Programming Language :: Python :: 3 :: Only",
31-
"Programming Language :: Python :: 3.9",
3231
"Programming Language :: Python :: 3.10",
3332
"Programming Language :: Python :: 3.11",
3433
"Programming Language :: Python :: 3.12",
@@ -44,7 +43,7 @@ classifiers = [
4443
"Topic :: Software Development :: Libraries :: Python Modules",
4544
"Topic :: Internet",
4645
]
47-
requires-python = ">=3.9"
46+
requires-python = ">=3.10"
4847

4948
[tool.hatch.metadata.hooks.uv-dynamic-versioning]
5049
dependencies = [

docs/ag-ui.md

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -43,24 +43,24 @@ This example uses [`run_ag_ui()`][pydantic_ai.ag_ui.run_ag_ui] and performs its
4343
This can be modified to work with any web framework.
4444

4545
```py {title="run_ag_ui.py"}
46+
import json
47+
from http import HTTPStatus
48+
4649
from ag_ui.core import RunAgentInput
4750
from fastapi import FastAPI
48-
from http import HTTPStatus
4951
from fastapi.requests import Request
5052
from fastapi.responses import Response, StreamingResponse
5153
from pydantic import ValidationError
52-
import json
5354

5455
from pydantic_ai import Agent
55-
from pydantic_ai.ag_ui import run_ag_ui, SSE_CONTENT_TYPE
56-
56+
from pydantic_ai.ag_ui import SSE_CONTENT_TYPE, run_ag_ui
5757

5858
agent = Agent('openai:gpt-4.1', instructions='Be fun!')
5959

6060
app = FastAPI()
6161

6262

63-
@app.post("/")
63+
@app.post('/')
6464
async def run_agent(request: Request) -> Response:
6565
accept = request.headers.get('accept', SSE_CONTENT_TYPE)
6666
try:
@@ -97,12 +97,11 @@ from starlette.responses import Response
9797
from pydantic_ai import Agent
9898
from pydantic_ai.ag_ui import handle_ag_ui_request
9999

100-
101100
agent = Agent('openai:gpt-4.1', instructions='Be fun!')
102101

103102
app = FastAPI()
104103

105-
@app.post("/")
104+
@app.post('/')
106105
async def run_agent(request: Request) -> Response:
107106
return await handle_ag_ui_request(agent, request)
108107
```
@@ -119,7 +118,7 @@ This will expose the agent as an AG-UI server, and your frontend can start sendi
119118

120119
This example uses [`Agent.to_ag_ui()`][pydantic_ai.agent.AbstractAgent.to_ag_ui] to turn the agent into a stand-alone ASGI application:
121120

122-
```py {title="agent_to_ag_ui.py" py="3.10" hl_lines="4"}
121+
```py {title="agent_to_ag_ui.py" hl_lines="4"}
123122
from pydantic_ai import Agent
124123

125124
agent = Agent('openai:gpt-4.1', instructions='Be fun!')
@@ -171,7 +170,7 @@ validate state contained in [`RunAgentInput.state`](https://docs.ag-ui.com/sdk/j
171170
If the `state` field's type is a Pydantic `BaseModel` subclass, the raw state dictionary on the request is automatically validated. If not, you can validate the raw value yourself in your dependencies dataclass's `__post_init__` method.
172171

173172

174-
```python {title="ag_ui_state.py" py="3.10"}
173+
```python {title="ag_ui_state.py"}
175174
from pydantic import BaseModel
176175

177176
from pydantic_ai import Agent
@@ -211,7 +210,7 @@ which returns a (subclass of)
211210
[`BaseEvent`](https://docs.ag-ui.com/sdk/python/core/events#baseevent), which allows
212211
for custom events and state updates.
213212

214-
```python {title="ag_ui_tool_events.py" py="3.10"}
213+
```python {title="ag_ui_tool_events.py"}
215214
from ag_ui.core import CustomEvent, EventType, StateSnapshotEvent
216215
from pydantic import BaseModel
217216

docs/agents.md

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ import asyncio
116116
from collections.abc import AsyncIterable
117117
from datetime import date
118118

119-
from pydantic_ai import Agent
119+
from pydantic_ai import Agent, RunContext
120120
from pydantic_ai.messages import (
121121
AgentStreamEvent,
122122
FinalResultEvent,
@@ -128,7 +128,6 @@ from pydantic_ai.messages import (
128128
ThinkingPartDelta,
129129
ToolCallPartDelta,
130130
)
131-
from pydantic_ai.tools import RunContext
132131

133132
weather_agent = Agent(
134133
'openai:gpt-4o',
@@ -215,10 +214,10 @@ Unlike `run_stream()`, it always runs the agent graph to completion even if text
215214
To get the best of both worlds, at the expense of some additional complexity, you can use [`agent.iter()`][pydantic_ai.agent.AbstractAgent.iter] as described in the next section, which lets you [iterate over the agent graph](#iterating-over-an-agents-graph) and [stream both events and output](#streaming-all-events-and-output) at every step.
216215

217216
```python {title="run_events.py" requires="run_stream_events.py"}
218-
from run_stream_events import weather_agent, event_stream_handler, output_messages
219-
220217
import asyncio
221218

219+
from run_stream_events import event_stream_handler, output_messages, weather_agent
220+
222221

223222
async def main():
224223
user_prompt = 'What will the weather be like in Paris on Tuesday?'
@@ -395,7 +394,7 @@ import asyncio
395394
from dataclasses import dataclass
396395
from datetime import date
397396

398-
from pydantic_ai import Agent
397+
from pydantic_ai import Agent, RunContext
399398
from pydantic_ai.messages import (
400399
FinalResultEvent,
401400
FunctionToolCallEvent,
@@ -406,7 +405,6 @@ from pydantic_ai.messages import (
406405
ThinkingPartDelta,
407406
ToolCallPartDelta,
408407
)
409-
from pydantic_ai.tools import RunContext
410408

411409

412410
@dataclass
@@ -548,9 +546,7 @@ You can apply these settings by passing the `usage_limits` argument to the `run{
548546
Consider the following example, where we limit the number of response tokens:
549547

550548
```py
551-
from pydantic_ai import Agent
552-
from pydantic_ai.exceptions import UsageLimitExceeded
553-
from pydantic_ai.usage import UsageLimits
549+
from pydantic_ai import Agent, UsageLimitExceeded, UsageLimits
554550

555551
agent = Agent('anthropic:claude-3-5-sonnet-latest')
556552

@@ -578,9 +574,7 @@ Restricting the number of requests can be useful in preventing infinite loops or
578574
```py
579575
from typing_extensions import TypedDict
580576

581-
from pydantic_ai import Agent, ModelRetry
582-
from pydantic_ai.exceptions import UsageLimitExceeded
583-
from pydantic_ai.usage import UsageLimits
577+
from pydantic_ai import Agent, ModelRetry, UsageLimitExceeded, UsageLimits
584578

585579

586580
class NeverOutputType(TypedDict):
@@ -636,9 +630,8 @@ For example, if you'd like to set the `temperature` setting to `0.0` to ensure l
636630
you can do the following:
637631

638632
```py
639-
from pydantic_ai import Agent
633+
from pydantic_ai import Agent, ModelSettings
640634
from pydantic_ai.models.openai import OpenAIChatModel
641-
from pydantic_ai.settings import ModelSettings
642635

643636
# 1. Model-level defaults
644637
model = OpenAIChatModel(

docs/api/output.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,4 @@
1010
- PromptedOutput
1111
- TextOutput
1212
- StructuredDict
13-
- DeferredToolCalls
13+
- DeferredToolRequests

docs/api/toolsets.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
members:
66
- AbstractToolset
77
- CombinedToolset
8-
- DeferredToolset
8+
- ExternalToolset
9+
- ApprovalRequiredToolset
910
- FilteredToolset
1011
- FunctionToolset
1112
- PrefixedToolset

docs/cli.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,13 @@ Both `Agent.to_cli()` and `Agent.to_cli_sync()` support a `message_history` para
115115

116116
```python {title="agent_with_history.py" test="skip"}
117117
from pydantic_ai import Agent
118-
from pydantic_ai.messages import ModelMessage, ModelRequest, ModelResponse, UserPromptPart, TextPart
118+
from pydantic_ai.messages import (
119+
ModelMessage,
120+
ModelRequest,
121+
ModelResponse,
122+
TextPart,
123+
UserPromptPart,
124+
)
119125

120126
agent = Agent('openai:gpt-4.1')
121127

docs/common-tools.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ Here's an example of how you can use the Tavily search tool with an agent:
106106
```py {title="tavily_search.py" test="skip"}
107107
import os
108108

109-
from pydantic_ai.agent import Agent
109+
from pydantic_ai import Agent
110110
from pydantic_ai.common_tools.tavily import tavily_search_tool
111111

112112
api_key = os.getenv('TAVILY_API_KEY')

0 commit comments

Comments
 (0)