Skip to content

⚡️ Speed up method JsonSchemaTransformer.walk by 730% #33

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 50 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
53cc7c8
update pyproject
aseembits93 Jul 21, 2025
2f4c3eb
add workflow
aseembits93 Jul 21, 2025
87871b3
Fix LLMJudge input handling to preserve BinaryContent as separate mes…
adtyavrdhn Jul 21, 2025
772af1d
validate OpenAI responses (#2226)
samuelcolvin Jul 22, 2025
c78fa3f
Remove duplicate field on GeminiModelSettings (#2269)
strawgate Jul 22, 2025
3ceff58
Fix AG-UI shared state example (#2272)
stevenh Jul 22, 2025
932c888
Correct code snippet for native output (#2271)
minhduc0711 Jul 22, 2025
2b180f3
chore: simplify output function call with model retry (#2273)
bitnahian Jul 22, 2025
8112002
Fix pydantic-evals panel rendering with evaluators (#2274)
dmontagu Jul 22, 2025
66f1868
Reduce duplication between StreamedRunResult and AgentStream (#2275)
DouweM Jul 22, 2025
e46fbae
⚡️ Speed up method `JsonSchemaTransformer.walk` by 730%
codeflash-ai[bot] Jul 22, 2025
0f46928
Fix mp3 handling (#2279)
dmontagu Jul 23, 2025
a589f30
fix: use `FileUrl.format` to find the extension (#2280)
Kludex Jul 23, 2025
07e46b3
chore: add `CLAUDE.md` (#2281)
Kludex Jul 23, 2025
1e18729
Handle built-in tool errors better in tool registration (#2252)
fswair Jul 23, 2025
f3ad3e6
Enable URL and binary PDF for Mistral (#2267)
pintaf Jul 23, 2025
0b3d020
Speed up function `_estimate_string_tokens` (#2156)
misrasaurabh1 Jul 23, 2025
903f11e
Ignore empty text alongside tool calls when streaming from Ollama (#2…
DouweM Jul 23, 2025
b686001
Handle `None` `created` timestamp coming from OpenRouter API (#2247)
R0boji Jul 23, 2025
e295e5e
Rename `MCPServer` `sse_read_timeout` to `read_timeout` and pass to `…
AntSan813 Jul 23, 2025
a0c3abb
Update cohere and MCP, add support for MCP ResourceLink returned from…
medaminezghal Jul 24, 2025
2af4db6
Add Vercel AI Gateway provider (#2277)
joshualipman123 Jul 24, 2025
7eb4491
Parse '<think>' tags in streamed text as thinking parts (#2290)
DouweM Jul 24, 2025
da80f5d
Fix AG-UI parallel tool calls (#2301)
DouweM Jul 24, 2025
fc6a2b2
Support passing files uploaded to Gemini Files API and setting custom…
dprov Jul 24, 2025
6d8a4df
Update MCP docs to show you can pass SSL/TLS options via the `http_cl…
assadyousuf Jul 24, 2025
7728c2a
Add MoonshotAI provider with Kimi-K2 model support (#2211)
zachmayer Jul 24, 2025
bc4facd
Include ThinkingPart in messages.md API documentation graph (#2299)
lfloeer Jul 24, 2025
94b4305
Fix docs build failure by adding MoonshotAIProvider to API docs (#2304)
DouweM Jul 24, 2025
4104aca
Fix initial tool call args not being streamed with AG-UI (#2303)
DouweM Jul 24, 2025
41dd069
Ignore leading whitespace when streaming text, fixing run_stream + Ol…
DouweM Jul 24, 2025
13ca4e3
dict.get is good enough
KRRT7 Jul 24, 2025
5cf372a
fix: close initialized MCP server if any MCP server fails to initaliz…
hartungstenio Jul 25, 2025
4941468
Add tenacity utilities/integration for improved retry handling (#2282)
dmontagu Jul 25, 2025
6e60677
Adding thinkingpart to otel_events in ModelResponse (#2237)
adtyavrdhn Jul 25, 2025
6207ac6
Add Claude Code action so we can tag @claude on issues and PRs (#2315)
DouweM Jul 25, 2025
806d56d
Fix: TypeError in MCPServerSSE due to improper initialization (#2319)
tradeqvest Jul 28, 2025
33aaef1
Pin tokenizers <= 0.21.2 as later 0.21.4 doesn't have required wheels…
DouweM Jul 28, 2025
86d70b5
Fix: AG-UI assistant text and tool call order (#2328)
ChuckJonas Jul 28, 2025
004d63b
Revert "Pin tokenizers <= 0.21.2 as later 0.21.4 doesn't have require…
DouweM Jul 28, 2025
0260a31
Allow `default` in tool schema with Gemini (#2309)
strawgate Jul 28, 2025
168680a
Fix AgentStream.stream_output and StreamedRunResult.stream_structured…
DouweM Jul 28, 2025
2fca506
Ensure AG-UI state is isolated between requests. (#2343)
DouweM Jul 28, 2025
ab92e67
Refine retry logic for parallel tool calling (#2317)
DouweM Jul 28, 2025
362dcbf
Set up environment for @claude, allow it make and uv, and reading CI …
DouweM Jul 29, 2025
8f20f9b
Remove older deprecated models of Anthropic (#2345)
medaminezghal Jul 29, 2025
c7a3591
Revert "Remove older deprecated models of Anthropic" (#2358)
DouweM Jul 29, 2025
3e1f634
Fix parallel tool calling with tools returning ToolReturn with conten…
DouweM Jul 29, 2025
bb97548
revert changes
misrasaurabh1 Jul 30, 2025
4f50c1b
Merge branch 'main' into codeflash/optimize-JsonSchemaTransformer.wal…
misrasaurabh1 Jul 30, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions .github/workflows/codeflash.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: Codeflash

on:
pull_request:
paths:
# So that this workflow only runs when code within the target module is modified
- 'pydantic_ai_slim/pydantic_ai/**'
workflow_dispatch:

concurrency:
# Any new push to the PR will cancel the previous run, so that only the latest code is optimized
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true


jobs:
optimize:
name: Optimize new Python code
# Don't run codeflash on codeflash-ai[bot] commits, prevent duplicate optimizations
if: ${{ github.actor != 'codeflash-ai[bot]' }}
runs-on: ubuntu-latest
env:
CODEFLASH_API_KEY: ${{ secrets.CODEFLASH_API_KEY }}

steps:
- name: 🛎️ Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: 🐍 Setup UV
uses: astral-sh/setup-uv@v6
with:
enable-cache: true
- name: 📦 Install Dependencies
run: uv sync --all-extras
- name: ⚡️Codeflash Optimization
run: uv run codeflash
14 changes: 9 additions & 5 deletions pydantic_ai_slim/pydantic_ai/profiles/_json_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,9 @@ def transform(self, schema: JsonSchema) -> JsonSchema:
return schema

def walk(self) -> JsonSchema:
schema = deepcopy(self.schema)
schema = {k: v for k, v in self.schema.items() if k != '$defs'}

# First, handle everything but $defs:
schema.pop('$defs', None)
handled = self._handle(schema)

if not self.prefer_inlined_defs and self.defs:
Expand Down Expand Up @@ -76,7 +75,10 @@ def _handle(self, schema: JsonSchema) -> JsonSchema:
nested_refs = 0
if self.prefer_inlined_defs:
while ref := schema.get('$ref'):
key = re.sub(r'^#/\$defs/', '', ref)
if ref.startswith('#/$defs/'):
key = ref[8:] # len('#/$defs/')
else:
key = re.sub(r'^#/\$defs/', '', ref)
if key in self.refs_stack:
self.recursive_refs.add(key)
break # recursive ref can't be unpacked
Expand All @@ -95,8 +97,10 @@ def _handle(self, schema: JsonSchema) -> JsonSchema:
elif type_ == 'array':
schema = self._handle_array(schema)
elif type_ is None:
schema = self._handle_union(schema, 'anyOf')
schema = self._handle_union(schema, 'oneOf')
if 'anyOf' in schema:
schema = self._handle_union(schema, 'anyOf')
if 'oneOf' in schema:
schema = self._handle_union(schema, 'oneOf')

# Apply the base transform
schema = self.transform(schema)
Expand Down
146 changes: 81 additions & 65 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,45 +5,6 @@ build-backend = "hatchling.build"
[tool.hatch.version]
source = "uv-dynamic-versioning"

[tool.uv-dynamic-versioning]
vcs = "git"
style = "pep440"
bump = true

[project]
name = "pydantic-ai"
dynamic = ["version", "dependencies", "optional-dependencies"]
description = "Agent Framework / shim to use Pydantic with LLMs"
authors = [
{ name = "Samuel Colvin", email = "[email protected]" },
{ name = "Marcelo Trylesinski", email = "[email protected]" },
{ name = "David Montague", email = "[email protected]" },
{ name = "Alex Hall", email = "[email protected]" },
]
license = "MIT"
readme = "README.md"
classifiers = [
"Development Status :: 4 - Beta",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Intended Audience :: Developers",
"Intended Audience :: Information Technology",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
"Topic :: Internet",
"Topic :: Scientific/Engineering :: Artificial Intelligence",
"Topic :: Software Development :: Libraries :: Python Modules",
"Framework :: Pydantic",
"Framework :: Pydantic :: 2",
]
requires-python = ">=3.9"

[tool.hatch.metadata.hooks.uv-dynamic-versioning]
dependencies = [
"pydantic-ai-slim[openai,vertexai,google,groq,anthropic,mistral,cohere,bedrock,cli,mcp,evals]=={{ version }}",
Expand All @@ -54,14 +15,18 @@ examples = ["pydantic-ai-examples=={{ version }}"]
logfire = ["logfire>=3.11.0"]
a2a = ["fasta2a>=0.4.1"]

[project.urls]
Homepage = "https://ai.pydantic.dev"
Source = "https://github.com/pydantic/pydantic-ai"
Documentation = "https://ai.pydantic.dev"
Changelog = "https://github.com/pydantic/pydantic-ai/releases"

[project.scripts]
pai = "pydantic_ai._cli:cli_exit" # TODO remove this when clai has been out for a while
[tool.hatch.build.targets.wheel]
only-include = ["/README.md"]

[tool.hatch.build.targets.sdist]
include = ["/README.md", "/Makefile", "/tests"]


[tool.uv-dynamic-versioning]
vcs = "git"
style = "pep440"
bump = true

[tool.uv.sources]
pydantic-ai-slim = { workspace = true }
Expand All @@ -79,25 +44,6 @@ members = [
"examples",
]

[dependency-groups]
# dev dependencies are defined in `pydantic-ai-slim/pyproject.toml` to allow for minimal testing
lint = ["mypy>=1.11.2", "pyright>=1.1.390", "ruff>=0.6.9"]
docs = [
"pydantic-ai[a2a]",
"black>=24.10.0",
"mkdocs>=1.6.1",
"mkdocs-glightbox>=0.4.0",
"mkdocs-llmstxt>=0.2.0",
"mkdocs-material[imaging]>=9.5.45",
"mkdocstrings-python>=1.12.2",
]
docs-upload = ["algoliasearch>=4.12.0", "pydantic>=2.10.1"]

[tool.hatch.build.targets.wheel]
only-include = ["/README.md"]

[tool.hatch.build.targets.sdist]
include = ["/README.md", "/Makefile", "/tests"]

[tool.ruff]
line-length = 120
Expand Down Expand Up @@ -152,6 +98,7 @@ quote-style = "single"
"tests/**/*.py" = ["D"]
"docs/**/*.py" = ["D"]


[tool.pyright]
pythonVersion = "3.12"
typeCheckingMode = "strict"
Expand Down Expand Up @@ -208,6 +155,7 @@ filterwarnings = [
]

# https://coverage.readthedocs.io/en/latest/config.html#run

[tool.coverage.run]
# required to avoid warnings about files created by create_module fixture
include = [
Expand Down Expand Up @@ -250,6 +198,7 @@ exclude_lines = [
'assert False',
]


[tool.logfire]
ignore_no_config = true

Expand All @@ -260,10 +209,77 @@ format-command = "ruff format --stdin-filename {filename}"
snap-fix = ["create", "fix"]
snap = ["create"]


[tool.codespell]
# Ref: https://github.com/codespell-project/codespell#using-a-config-file
skip = '.git*,*.svg,*.lock,*.css,*.yaml'
check-hidden = true
# Ignore "formatting" like **L**anguage
ignore-regex = '\*\*[A-Z]\*\*[a-z]+\b'
ignore-words-list = 'asend,aci'

[tool.codeflash]
# All paths are relative to this pyproject.toml's directory.
module-root = "pydantic_ai_slim/pydantic_ai"
tests-root = "tests"
test-framework = "pytest"
ignore-paths = []
formatter-cmds = ["disabled"]

[project]
name = "pydantic-ai"
dynamic = ["version", "dependencies", "optional-dependencies"]
description = "Agent Framework / shim to use Pydantic with LLMs"
authors = [
{ name = "Samuel Colvin", email = "[email protected]" },
{ name = "Marcelo Trylesinski", email = "[email protected]" },
{ name = "David Montague", email = "[email protected]" },
{ name = "Alex Hall", email = "[email protected]" },
]
license = "MIT"
readme = "README.md"
classifiers = [
"Development Status :: 4 - Beta",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Intended Audience :: Developers",
"Intended Audience :: Information Technology",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
"Topic :: Internet",
"Topic :: Scientific/Engineering :: Artificial Intelligence",
"Topic :: Software Development :: Libraries :: Python Modules",
"Framework :: Pydantic",
"Framework :: Pydantic :: 2",
]
requires-python = ">=3.9"

[project.urls]
Homepage = "https://ai.pydantic.dev"
Source = "https://github.com/pydantic/pydantic-ai"
Documentation = "https://ai.pydantic.dev"
Changelog = "https://github.com/pydantic/pydantic-ai/releases"

[project.scripts]
pai = "pydantic_ai._cli:cli_exit" # TODO remove this when clai has been out for a while

[dependency-groups]
# dev dependencies are defined in `pydantic-ai-slim/pyproject.toml` to allow for minimal testing
lint = ["mypy>=1.11.2", "pyright>=1.1.390", "ruff>=0.6.9"]
docs = [
"pydantic-ai[a2a]",
"black>=24.10.0",
"mkdocs>=1.6.1",
"mkdocs-glightbox>=0.4.0",
"mkdocs-llmstxt>=0.2.0",
"mkdocs-material[imaging]>=9.5.45",
"mkdocstrings-python>=1.12.2",
]
docs-upload = ["algoliasearch>=4.12.0", "pydantic>=2.10.1"]

Loading