Skip to content

Commit 66ec4a1

Browse files
committed
Merge branch 'main' into pr/ethanabrooks/2557
# Conflicts: # tests/models/test_google.py
2 parents 8f38d24 + 0787eac commit 66ec4a1

File tree

280 files changed

+18048
-13188
lines changed

Some content is hidden

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

280 files changed

+18048
-13188
lines changed
File renamed without changes.

.github/workflows/after-ci.yml

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,12 @@ jobs:
1717
- uses: astral-sh/setup-uv@v5
1818
with:
1919
enable-cache: true
20-
python-version: '3.12'
20+
python-version: "3.12"
2121

2222
- uses: dawidd6/action-download-artifact@v6
2323
with:
2424
workflow: ci.yml
25-
name: '(diff-)?coverage-html.*'
25+
name: "(diff-)?coverage-html.*"
2626
name_is_regexp: true
2727
commit: ${{ github.event.workflow_run.head_sha }}
2828
allow_forks: true
@@ -39,16 +39,6 @@ jobs:
3939
SMOKESHOW_GITHUB_PR_HEAD_SHA: ${{ github.event.workflow_run.head_sha }}
4040
SMOKESHOW_AUTH_KEY: ${{ secrets.SMOKESHOW_AUTH_KEY }}
4141

42-
- run: uvx smokeshow upload diff-coverage-html
43-
if: hashFiles('diff-coverage-html/*.html') != ''
44-
env:
45-
SMOKESHOW_GITHUB_STATUS_DESCRIPTION: Diff coverage {coverage-percentage}
46-
SMOKESHOW_GITHUB_COVERAGE_THRESHOLD: 95
47-
SMOKESHOW_GITHUB_CONTEXT: diff-coverage
48-
SMOKESHOW_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
49-
SMOKESHOW_GITHUB_PR_HEAD_SHA: ${{ github.event.workflow_run.head_sha }}
50-
SMOKESHOW_AUTH_KEY: ${{ secrets.SMOKESHOW_AUTH_KEY }}
51-
5242
deploy-docs-preview:
5343
runs-on: ubuntu-latest
5444
if: github.event.workflow_run.event == 'pull_request'
@@ -65,7 +55,7 @@ jobs:
6555
- uses: astral-sh/setup-uv@v5
6656
with:
6757
enable-cache: true
68-
python-version: '3.12'
58+
python-version: "3.12"
6959

7060
- uses: dawidd6/action-download-artifact@v6
7161
with:

.github/workflows/ci.yml

Lines changed: 17 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,6 @@ jobs:
2929
- name: Install dependencies
3030
run: uv sync --all-extras --all-packages --group lint
3131

32-
- uses: denoland/setup-deno@v2
33-
with:
34-
deno-version: v2.x
35-
3632
- uses: pre-commit/[email protected]
3733
with:
3834
extra_args: --all-files --verbose
@@ -137,7 +133,7 @@ jobs:
137133
strategy:
138134
fail-fast: false
139135
matrix:
140-
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
136+
python-version: ["3.10", "3.11", "3.12", "3.13"]
141137
install:
142138
- name: pydantic-ai-slim
143139
command: "--package pydantic-ai-slim"
@@ -162,14 +158,14 @@ jobs:
162158

163159
- run: mkdir .coverage
164160

161+
- run: uv run mcp-run-python example --deps=numpy
165162
- run: uv sync --only-dev
166163
- run: uv run ${{ matrix.install.command }} coverage run -m pytest --durations=100 -n auto --dist=loadgroup
167164
env:
168165
COVERAGE_FILE: .coverage/.coverage.${{ matrix.python-version }}-${{ matrix.install.name }}
169166

170167
- name: store coverage files
171168
uses: actions/upload-artifact@v4
172-
if: matrix.python-version != '3.9'
173169
with:
174170
name: coverage-${{ matrix.python-version }}-${{ matrix.install.name }}
175171
path: .coverage
@@ -201,6 +197,7 @@ jobs:
201197
- run: mkdir .coverage
202198

203199
- run: uv sync --group dev
200+
- run: uv run mcp-run-python example --deps=numpy
204201

205202
- run: unset UV_FROZEN
206203

@@ -233,10 +230,6 @@ jobs:
233230
with:
234231
enable-cache: true
235232

236-
- uses: denoland/setup-deno@v2
237-
with:
238-
deno-version: v2.x
239-
240233
- run: uv run --all-extras python tests/import_examples.py
241234

242235
coverage:
@@ -266,33 +259,25 @@ jobs:
266259
env:
267260
COVERAGE_FILE: .coverage/.coverage
268261

269-
test-mcp-run-python:
270-
runs-on: ubuntu-latest
271-
timeout-minutes: 5
272-
env:
273-
UV_PYTHON: "3.12"
274-
steps:
275-
- uses: actions/checkout@v4
276-
277-
- uses: astral-sh/setup-uv@v5
278-
with:
279-
enable-cache: true
280-
281-
- uses: denoland/setup-deno@v2
262+
- run: uv run coverage html --show-contexts --title "Pydantic AI coverage for ${{ github.sha }}"
263+
- uses: actions/upload-artifact@v4
282264
with:
283-
deno-version: v2.x
284-
285-
- run: make lint-js
286-
287-
- run: uv run --package mcp-run-python pytest mcp-run-python -v --durations=100
288-
289-
- run: deno task dev warmup
290-
working-directory: mcp-run-python
265+
name: coverage-html
266+
path: htmlcov
267+
include-hidden-files: true
291268

292269
# https://github.com/marketplace/actions/alls-green#why used for branch protection checks
293270
check:
294271
if: always()
295-
needs: [lint, mypy, docs, test-live, test, test-lowest-versions, test-examples, coverage, test-mcp-run-python]
272+
needs:
273+
- lint
274+
- mypy
275+
- docs
276+
- test-live
277+
- test
278+
- test-lowest-versions
279+
- test-examples
280+
- coverage
296281
runs-on: ubuntu-latest
297282

298283
steps:

.pre-commit-config.yaml

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -44,14 +44,6 @@ repos:
4444
types: [python]
4545
language: system
4646
pass_filenames: false
47-
- id: lint-js
48-
name: Lint JS
49-
entry: make
50-
args: [lint-js]
51-
language: system
52-
types_or: [javascript, ts, json]
53-
files: "^mcp-run-python/"
54-
pass_filenames: false
5547
- id: clai-help
5648
name: clai help output
5749
entry: uv

AGENTS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
CLAUDE.md

CLAUDE.md

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -83,15 +83,14 @@ This is a uv workspace with multiple packages:
8383
- **`pydantic_graph/`**: Graph execution engine
8484
- **`examples/`**: Example applications
8585
- **`clai/`**: CLI tool
86-
- **`mcp-run-python/`**: MCP server implementation (Deno/TypeScript)
8786

8887
## Testing Strategy
8988

9089
- **Unit tests**: `tests/` directory with comprehensive model and component coverage
9190
- **VCR cassettes**: `tests/cassettes/` for recorded LLM API interactions
9291
- **Test models**: Use `TestModel` for deterministic testing
9392
- **Examples testing**: `tests/test_examples.py` validates all documentation examples
94-
- **Multi-version testing**: Python 3.9-3.13 support
93+
- **Multi-version testing**: Python 3.10-3.13 support
9594

9695
## Key Configuration Files
9796

@@ -134,7 +133,7 @@ from typing_extensions import deprecated
134133

135134
class NewClass: ... # This class was renamed from OldClass.
136135

137-
@deprecated("Use `NewClass` instead")
136+
@deprecated("Use `NewClass` instead.")
138137
class OldClass(NewClass): ...
139138
```
140139

@@ -143,7 +142,7 @@ deprecation warning:
143142

144143
```python
145144
def test_old_class_is_deprecated():
146-
with pytest.warns(DeprecationWarning, match="Use `NewClass` instead"):
145+
with pytest.warns(DeprecationWarning, match="Use `NewClass` instead."):
147146
OldClass()
148147
```
149148

Makefile

Lines changed: 3 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,13 @@
88
.pre-commit: ## Check that pre-commit is installed
99
@pre-commit -V || echo 'Please install pre-commit: https://pre-commit.com/'
1010

11-
.PHONY: .deno
12-
.deno: ## Check that deno is installed
13-
@deno --version > /dev/null 2>&1 || (printf "\033[0;31m✖ Error: deno is not installed, but is needed for mcp-run-python\033[0m\n Please install deno: https://docs.deno.com/runtime/getting_started/installation/\n" && exit 1)
14-
1511
.PHONY: install
16-
install: .uv .pre-commit .deno ## Install the package, dependencies, and pre-commit for local development
12+
install: .uv .pre-commit ## Install the package, dependencies, and pre-commit for local development
1713
uv sync --frozen --all-extras --all-packages --group lint --group docs
1814
pre-commit install --install-hooks
1915

2016
.PHONY: install-all-python
2117
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
2318
UV_PROJECT_ENVIRONMENT=.venv310 uv sync --python 3.10 --frozen --all-extras --all-packages --group lint --group docs
2419
UV_PROJECT_ENVIRONMENT=.venv311 uv sync --python 3.11 --frozen --all-extras --all-packages --group lint --group docs
2520
UV_PROJECT_ENVIRONMENT=.venv312 uv sync --python 3.12 --frozen --all-extras --all-packages --group lint --group docs
@@ -39,10 +34,6 @@ lint: ## Lint the code
3934
uv run ruff format --check
4035
uv run ruff check
4136

42-
.PHONY: lint-js
43-
lint-js: ## Lint JS and TS code
44-
cd mcp-run-python && deno task lint-format
45-
4637
.PHONY: typecheck-pyright
4738
typecheck-pyright:
4839
@# PYRIGHT_PYTHON_IGNORE_WARNINGS avoids the overhead of making a request to github on every invocation
@@ -60,13 +51,12 @@ typecheck-both: typecheck-pyright typecheck-mypy
6051

6152
.PHONY: test
6253
test: ## Run tests and collect coverage data
63-
uv run coverage run -m pytest -n auto --dist=loadgroup
54+
uv run coverage run -m pytest -n auto --dist=loadgroup --durations=20
6455
@uv run coverage combine
6556
@uv run coverage report
6657

6758
.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
59+
test-all-python: ## Run tests on Python 3.10 to 3.13
7060
UV_PROJECT_ENVIRONMENT=.venv310 uv run --python 3.10 --all-extras --all-packages coverage run -p -m pytest
7161
UV_PROJECT_ENVIRONMENT=.venv311 uv run --python 3.11 --all-extras --all-packages coverage run -p -m pytest
7262
UV_PROJECT_ENVIRONMENT=.venv312 uv run --python 3.12 --all-extras --all-packages coverage run -p -m pytest
@@ -79,11 +69,6 @@ testcov: test ## Run tests and generate an HTML coverage report
7969
@echo "building coverage html"
8070
@uv run coverage html
8171

82-
.PHONY: test-mrp
83-
test-mrp: ## Build and tests of mcp-run-python
84-
cd mcp-run-python && deno task build
85-
uv run --package mcp-run-python pytest mcp-run-python -v
86-
8772
.PHONY: update-examples
8873
update-examples: ## Update documentation examples
8974
uv run -m pytest --update-examples tests/test_examples.py

clai/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ Either way, running `clai` will start an interactive session where you can chat
4949
- `/exit`: Exit the session
5050
- `/markdown`: Show the last response in markdown format
5151
- `/multiline`: Toggle multiline input mode (use Ctrl+D to submit)
52+
- `/cp`: Copy the last response to clipboard
5253

5354
## Help
5455

@@ -61,6 +62,7 @@ Special prompts:
6162
* `/exit` - exit the interactive mode (ctrl-c and ctrl-d also work)
6263
* `/markdown` - show the last markdown output of the last question
6364
* `/multiline` - toggle multiline mode
65+
* `/cp` - copy the last response to clipboard
6466
6567
positional arguments:
6668
prompt AI Prompt, if omitted fall into interactive mode

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

0 commit comments

Comments
 (0)