9201ef759e
CI / lint (push) Waiting to run
CI / mypy (push) Waiting to run
CI / docs (push) Waiting to run
CI / test on 3.10 (standard) (push) Waiting to run
CI / test on 3.11 (standard) (push) Waiting to run
CI / test on 3.12 (standard) (push) Waiting to run
CI / test on 3.10 (all-extras) (push) Waiting to run
CI / test on 3.11 (all-extras) (push) Waiting to run
CI / test on 3.12 (all-extras) (push) Waiting to run
CI / test on 3.13 (all-extras) (push) Waiting to run
CI / test on 3.14 (pydantic-evals) (push) Waiting to run
Harness Compat / harness compat (push) Waiting to run
CI / test on 3.13 (standard) (push) Waiting to run
CI / test on 3.14 (standard) (push) Waiting to run
CI / test on 3.14 (all-extras) (push) Waiting to run
CI / test on 3.10 (pydantic-ai-slim) (push) Waiting to run
CI / test on 3.11 (pydantic-ai-slim) (push) Waiting to run
CI / test on 3.12 (pydantic-ai-slim) (push) Waiting to run
CI / test on 3.13 (pydantic-ai-slim) (push) Waiting to run
CI / test on 3.14 (pydantic-ai-slim) (push) Waiting to run
CI / test on 3.10 (pydantic-evals) (push) Waiting to run
CI / test on 3.11 (pydantic-evals) (push) Waiting to run
CI / test on 3.12 (pydantic-evals) (push) Waiting to run
CI / test on 3.13 (pydantic-evals) (push) Waiting to run
CI / test on 3.10 (lowest-versions) (push) Waiting to run
CI / test on 3.11 (lowest-versions) (push) Waiting to run
CI / test on 3.12 (lowest-versions) (push) Waiting to run
CI / test on 3.13 (lowest-versions) (push) Waiting to run
CI / test on 3.14 (lowest-versions) (push) Waiting to run
CI / test examples on 3.11 (push) Waiting to run
CI / test examples on 3.12 (push) Waiting to run
CI / test examples on 3.13 (push) Waiting to run
CI / test examples on 3.14 (push) Waiting to run
CI / coverage (push) Blocked by required conditions
CI / check (push) Blocked by required conditions
CI / deploy-docs (push) Blocked by required conditions
CI / deploy-docs-preview (push) Blocked by required conditions
CI / build release artifacts (push) Blocked by required conditions
CI / publish to PyPI (push) Blocked by required conditions
CI / Send tweet (push) Blocked by required conditions
427 lines
16 KiB
TOML
427 lines
16 KiB
TOML
[build-system]
|
|
requires = ["hatchling", "uv-dynamic-versioning>=0.7.0"]
|
|
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 = "AI Agent Framework, the Pydantic way"
|
|
authors = [
|
|
{ name = "Douwe Maan", email = "douwe@pydantic.dev" },
|
|
{ name = "David Sanchez", email = "david.sanchez@pydantic.dev" },
|
|
{ name = "Aditya Vardhan", email = "aditya@pydantic.dev" },
|
|
{ name = "David Montague", email = "david@pydantic.dev" },
|
|
{ name = "Marcelo Trylesinski", email = "marcelotryle@gmail.com" },
|
|
{ name = "Alex Hall", email = "alex@pydantic.dev" },
|
|
{ name = "Samuel Colvin", email = "samuel@pydantic.dev" },
|
|
]
|
|
license = "MIT"
|
|
license-files = ["LICENSE"]
|
|
readme = "README.md"
|
|
classifiers = [
|
|
"Development Status :: 5 - Production/Stable",
|
|
"Programming Language :: Python",
|
|
"Programming Language :: Python :: 3",
|
|
"Programming Language :: Python :: 3 :: Only",
|
|
"Programming Language :: Python :: 3.10",
|
|
"Programming Language :: Python :: 3.11",
|
|
"Programming Language :: Python :: 3.12",
|
|
"Programming Language :: Python :: 3.13",
|
|
"Programming Language :: Python :: 3.14",
|
|
"Intended Audience :: Developers",
|
|
"Intended Audience :: Information Technology",
|
|
"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.10"
|
|
|
|
[tool.hatch.metadata.hooks.uv-dynamic-versioning]
|
|
dependencies = [
|
|
"pydantic-ai-slim[openai,anthropic,google,cli,mcp,evals,web,retries,logfire]=={{ version }}",
|
|
]
|
|
|
|
[tool.hatch.metadata.hooks.uv-dynamic-versioning.optional-dependencies]
|
|
examples = ["pydantic-ai-examples=={{ version }}"]
|
|
dbos = ["pydantic-ai-slim[dbos]=={{ version }}"]
|
|
prefect = ["pydantic-ai-slim[prefect]=={{ version }}"]
|
|
sentence-transformers = ["pydantic-ai-slim[sentence-transformers]=={{ version }}"]
|
|
voyageai = ["pydantic-ai-slim[voyageai]=={{ version }}"]
|
|
# Opt-in extras forwarded from `pydantic-ai-slim` (some previously included in the
|
|
# default `pydantic-ai` install, others newly exposed here for convenience).
|
|
# Exposed at the root so users (and CI's `uv run --all-extras` matrix entry) can
|
|
# pull them through `pydantic-ai[<name>]`.
|
|
cohere = ["pydantic-ai-slim[cohere]=={{ version }}"]
|
|
mistral = ["pydantic-ai-slim[mistral]=={{ version }}"]
|
|
bedrock = ["pydantic-ai-slim[bedrock]=={{ version }}"]
|
|
xai = ["pydantic-ai-slim[xai]=={{ version }}"]
|
|
groq = ["pydantic-ai-slim[groq]=={{ version }}"]
|
|
openrouter = ["pydantic-ai-slim[openrouter]=={{ version }}"]
|
|
huggingface = ["pydantic-ai-slim[huggingface]=={{ version }}"]
|
|
ag-ui = ["pydantic-ai-slim[ag-ui]=={{ version }}"]
|
|
ui = ["pydantic-ai-slim[ui]=={{ version }}"]
|
|
duckduckgo = ["pydantic-ai-slim[duckduckgo]=={{ version }}"]
|
|
tavily = ["pydantic-ai-slim[tavily]=={{ version }}"]
|
|
exa = ["pydantic-ai-slim[exa]=={{ version }}"]
|
|
web-fetch = ["pydantic-ai-slim[web-fetch]=={{ version }}"]
|
|
temporal = ["pydantic-ai-slim[temporal]=={{ version }}"]
|
|
spec = ["pydantic-ai-slim[spec]=={{ version }}"]
|
|
|
|
[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.uv.sources]
|
|
pydantic-ai = { workspace = true }
|
|
pydantic-ai-slim = { workspace = true }
|
|
pydantic-evals = { workspace = true }
|
|
pydantic-graph = { workspace = true }
|
|
pydantic-ai-examples = { workspace = true }
|
|
pydantic-docs = { git = "https://github.com/pydantic/pydantic-docs" }
|
|
|
|
[tool.uv.workspace]
|
|
members = [
|
|
"pydantic_ai_slim",
|
|
"pydantic_evals",
|
|
"pydantic_graph",
|
|
"clai",
|
|
"examples",
|
|
]
|
|
|
|
[tool.uv]
|
|
default-groups = ["dev", "lint", "docs"]
|
|
constraint-dependencies = [
|
|
"ray>=2.55.0",
|
|
"authlib>=1.6.7",
|
|
# Security-driven floors for purely transitive deps. Each line lifts the
|
|
# minimum to the first patched release flagged by Dependabot so we don't
|
|
# have to wait for upstream packages to bump.
|
|
"urllib3>=2.7.0",
|
|
"idna>=3.15",
|
|
"cryptography>=48.0.1",
|
|
"lxml>=6.1.0",
|
|
"mako>=1.3.12",
|
|
"pillow>=12.2.0",
|
|
"pyarrow>=23.0.1",
|
|
"pymdown-extensions>=10.21.3",
|
|
"python-dotenv>=1.2.2",
|
|
"pygments>=2.20.0",
|
|
"xgrammar>=0.1.32",
|
|
"langchain-core>=1.3.3",
|
|
"langchain-text-splitters>=1.1.2",
|
|
"langsmith>=0.8.18",
|
|
"joserfc>=1.6.7",
|
|
"pydantic-settings>=2.14.2",
|
|
"vcrpy>=8.2.1",
|
|
"starlette>=1.3.1",
|
|
"aiohttp>=3.14.1",
|
|
"python-multipart>=0.0.31",
|
|
"pyjwt>=2.13.0",
|
|
"torch>=2.12.1",
|
|
"transformers>=5.3.0",
|
|
]
|
|
|
|
[dependency-groups]
|
|
dev = [
|
|
"anyio>=4.5.0",
|
|
"asgi-lifespan>=2.1.0",
|
|
"devtools>=0.12.2",
|
|
"coverage[toml]>=7.10.7",
|
|
"dirty-equals>=0.9.0",
|
|
"duckduckgo-search>=7.0.0",
|
|
"pydocket>=0.20.2",
|
|
"exa-py>=2.0.0",
|
|
"tavily-python>=0.5.0",
|
|
"markdownify>=1.2",
|
|
"inline-snapshot>=0.32.5",
|
|
"pytest>=9.0.3",
|
|
"pytest-examples>=0.0.18",
|
|
"pytest-mock>=3.14.0",
|
|
"pytest-pretty>=1.3.0",
|
|
"pytest-recording>=0.13.2",
|
|
"diff-cover>=9.2.0",
|
|
"boto3-stubs[bedrock-runtime]>=1.42.63",
|
|
"strict-no-cover @ git+https://github.com/pydantic/strict-no-cover.git@7fc59da2c4dff919db2095a0f0e47101b657131d",
|
|
"pytest-xdist>=3.6.1",
|
|
# Needed for PyCharm users
|
|
"pip>=26.1",
|
|
"genai-prices>=0.0.69",
|
|
"brotli>=1.2.0",
|
|
# Tests use `FastMCP(...)` in-process servers via fastmcp's server module — the user-facing
|
|
# `[mcp]` extra ships `fastmcp-slim[client]` which lacks server support, so devs need the full
|
|
# `fastmcp` package alongside.
|
|
"fastmcp>=3.3.0",
|
|
]
|
|
lint = ["mypy>=1.11.2", "pyright>=1.1.408", "ruff>=0.14.14"]
|
|
docs = [
|
|
"black>=24.10.0",
|
|
"mkdocs>=1.6.1",
|
|
"mkdocs-glightbox>=0.4.0",
|
|
"mkdocs-llmstxt>=0.2.0",
|
|
"mkdocs-redirects>=1.2.2",
|
|
"mkdocs-material[imaging]>=9.7.0",
|
|
"mkdocstrings-python>=2.0.0",
|
|
"griffe-warnings-deprecated>=1.1.0",
|
|
"pydantic-docs",
|
|
]
|
|
docs-upload = ["algoliasearch>=4.12.0", "pydantic>=2.10.1"]
|
|
|
|
[tool.hatch.build.targets.wheel]
|
|
bypass-selection = true
|
|
exclude = ["/pydantic_ai_slim/pydantic_ai/.agents"]
|
|
|
|
[tool.hatch.build.targets.sdist]
|
|
include = ["/README.md", "/Makefile"]
|
|
# We are excluding the tests because we have 100MB of test data in this repository.
|
|
# Obviously that's a problem, but for now, we'll just exclude them.
|
|
exclude = ["/tests"]
|
|
|
|
[tool.ruff]
|
|
line-length = 120
|
|
target-version = "py310"
|
|
include = [
|
|
"pydantic_ai_slim/**/*.py",
|
|
"pydantic_evals/**/*.py",
|
|
"pydantic_graph/**/*.py",
|
|
"examples/**/*.py",
|
|
"clai/**/*.py",
|
|
"tests/**/*.py",
|
|
"docs/**/*.py",
|
|
".github/scripts/ci_duration.py",
|
|
".github/scripts/test_ci_duration.py",
|
|
".github/scripts/pydantic_ai_gh_aw_shim/**/*.py",
|
|
".github/scripts/test_pydantic_ai_runner.py",
|
|
]
|
|
|
|
[tool.ruff.lint]
|
|
preview = true # For `PLW1514`
|
|
explicit-preview-rules = true
|
|
select = ["E4", "E7", "E9", "F"]
|
|
extend-select = [
|
|
"Q",
|
|
"RUF100",
|
|
"RUF018", # https://docs.astral.sh/ruff/rules/assignment-in-assert/
|
|
"RUF043", # https://docs.astral.sh/ruff/rules/pytest-raises-ambiguous-pattern/
|
|
"C90",
|
|
"UP",
|
|
"I",
|
|
"D",
|
|
"TID251",
|
|
"PLW1514", # https://docs.astral.sh/ruff/rules/unspecified-encoding/
|
|
]
|
|
flake8-quotes = { inline-quotes = "single", multiline-quotes = "double" }
|
|
mccabe = { max-complexity = 15 }
|
|
ignore = [
|
|
"D100", # ignore missing docstring in module
|
|
"D102", # ignore missing docstring in public method
|
|
"D104", # ignore missing docstring in public package
|
|
"D105", # ignore missing docstring in magic methods
|
|
"D107", # ignore missing docstring in __init__ methods
|
|
]
|
|
|
|
[tool.ruff.lint.isort]
|
|
combine-as-imports = true
|
|
known-first-party = ["pydantic_ai", "pydantic_evals", "pydantic_graph"]
|
|
|
|
[tool.ruff.lint.pydocstyle]
|
|
convention = "google"
|
|
|
|
[tool.ruff.lint.flake8-tidy-imports.banned-api]
|
|
"typing.TypedDict".msg = "Use typing_extensions.TypedDict instead."
|
|
"typing.assert_never".msg = "Use typing_extensions.assert_never instead."
|
|
"asyncio.Lock".msg = "Use anyio.Lock instead."
|
|
|
|
[tool.ruff.format]
|
|
# don't format python in docstrings, pytest-examples takes care of it
|
|
docstring-code-format = false
|
|
quote-style = "single"
|
|
|
|
[tool.ruff.lint.per-file-ignores]
|
|
"examples/**/*.py" = ["D101", "D103"]
|
|
"tests/**/*.py" = ["D"]
|
|
"docs/**/*.py" = ["D"]
|
|
".github/scripts/ci_duration.py" = ["D"]
|
|
".github/scripts/test_ci_duration.py" = ["D"]
|
|
".github/scripts/test_pydantic_ai_runner.py" = ["D"]
|
|
|
|
[tool.pyright]
|
|
pythonVersion = "3.10"
|
|
typeCheckingMode = "strict"
|
|
reportMissingTypeStubs = false
|
|
reportUnnecessaryIsInstance = false
|
|
reportUnnecessaryTypeIgnoreComment = true
|
|
reportMissingModuleSource = false
|
|
include = [
|
|
"pydantic_ai_slim",
|
|
"pydantic_evals",
|
|
"pydantic_graph",
|
|
"tests",
|
|
"examples",
|
|
"clai",
|
|
".github/scripts/ci_duration.py",
|
|
".github/scripts/test_ci_duration.py",
|
|
".github/scripts/pydantic_ai_gh_aw_shim",
|
|
".github/scripts/test_pydantic_ai_runner.py",
|
|
]
|
|
venvPath = '.'
|
|
venv = ".venv"
|
|
# see https://github.com/microsoft/pyright/issues/7771 - we don't want to error on decorated functions in tests
|
|
# which are not otherwise used
|
|
executionEnvironments = [
|
|
{ root = "tests", reportUnusedFunction = false, reportPrivateImportUsage = false },
|
|
# `extraPaths` lets pyright resolve the shim package (which sits next to
|
|
# the test file, not under the project root) the same way the runtime
|
|
# `sys.path.insert(...)` at the top of the test does.
|
|
{ root = ".github/scripts/test_ci_duration.py", extraPaths = [".github/scripts"], reportUnusedFunction = false, reportPrivateImportUsage = false },
|
|
{ root = ".github/scripts/test_pydantic_ai_runner.py", extraPaths = [".github/scripts"], reportUnusedFunction = false, reportPrivateImportUsage = false },
|
|
]
|
|
exclude = [
|
|
"examples/pydantic_ai_examples/weather_agent_gradio.py",
|
|
"pydantic_ai_slim/pydantic_ai/embeddings/voyageai.py", # voyageai package has no type stubs
|
|
]
|
|
|
|
[tool.mypy]
|
|
files = "tests/typed_agent.py"
|
|
strict = true
|
|
|
|
[tool.pytest.ini_options]
|
|
testpaths = ["tests", "docs/.hooks", ".github/scripts/test_ci_duration.py"]
|
|
xfail_strict = true
|
|
filterwarnings = [
|
|
"error",
|
|
# boto3
|
|
"ignore::DeprecationWarning:botocore.*",
|
|
"ignore::RuntimeWarning:pydantic_ai.mcp",
|
|
# ag-ui-protocol >= 0.1.15 deprecated BinaryInputContent
|
|
"ignore:BinaryInputContent is deprecated:DeprecationWarning",
|
|
# uvicorn (mcp server)
|
|
"ignore:websockets.legacy is deprecated.*:DeprecationWarning:websockets.legacy",
|
|
"ignore:websockets.server.WebSocketServerProtocol is deprecated:DeprecationWarning",
|
|
# random resource warnings; I suspect these are coming from vendor SDKs when running examples..
|
|
"ignore:unclosed <socket:ResourceWarning",
|
|
"ignore:unclosed <ssl.SSLSocket:ResourceWarning",
|
|
"ignore:unclosed event loop:ResourceWarning",
|
|
# prefect has a Pydantic v2 compatibility issue in RRuleSchedule
|
|
"ignore:The 'default' attribute with value 'UTC' was provided.*:UserWarning",
|
|
# This is because of cohere.
|
|
"ignore: 'asyncio.iscoroutinefunction' is deprecated:DeprecationWarning:cohere",
|
|
# This is because of google.
|
|
"ignore: '_UnionGenericAlias' is deprecated and slated for removal:DeprecationWarning:google.genai",
|
|
# TODO(Marcelo): Drop this once voyageai stops using Pydantic V1 functionality.
|
|
"ignore: Core Pydantic V1 functionality:UserWarning",
|
|
# prefect has a Pydantic v2 compatibility issue in RRuleSchedule
|
|
"ignore:UnsupportedFieldAttributeWarning:UserWarning",
|
|
# coolname (prefect dep) uses codecs.open(), deprecated in Python 3.14
|
|
"ignore:codecs.open\\(\\) is deprecated:DeprecationWarning",
|
|
# outlines' llamacpp backend calls llama-cpp-python's `Llama.from_pretrained`, which forwards
|
|
# `local_dir_use_symlinks` to `hf_hub_download`; huggingface-hub >= 1.0 deprecated (and ignores)
|
|
# that arg and warns about it. The arg is a no-op and the call site isn't ours to change.
|
|
"ignore:The `local_dir_use_symlinks` argument is deprecated and ignored:UserWarning",
|
|
# fastmcp v3 does standard OTel distributed tracing (inject on client, extract on server).
|
|
# logfire's default config wraps the global propagator to warn on extract via warnings.warn(),
|
|
# which crashes tool execution when filterwarnings=["error"]. The proper upstream fix is for
|
|
# logfire to use logging.warning() instead of warnings.warn() for this diagnostic — it's
|
|
# informational, not a deprecation, and should never be able to crash execution.
|
|
"ignore:Found propagated trace context:RuntimeWarning",
|
|
]
|
|
|
|
# https://coverage.readthedocs.io/en/latest/config.html#run
|
|
[tool.coverage.run]
|
|
patch = ["subprocess"]
|
|
concurrency = ["multiprocessing", "thread"]
|
|
# We use a subdirectory for coverage data to avoid noisy coverage data files.
|
|
data_file = ".coverage/.coverage"
|
|
# required to avoid warnings about files created by create_module fixture
|
|
include = [
|
|
"pydantic_ai_slim/**/*.py",
|
|
"pydantic_evals/**/*.py",
|
|
"pydantic_graph/**/*.py",
|
|
"tests/**/*.py",
|
|
]
|
|
omit = [
|
|
"tests/example_modules/*.py",
|
|
"pydantic_ai_slim/pydantic_ai/common_tools/exa.py", # exa-py integration with external API calls
|
|
"tests/models/xai_proto_cassettes.py", # dev-only helpers for recording/replaying xAI gRPC protobuf cassettes
|
|
"tests/_inline_snapshot.py", # test utility wrapper; branches depend on CLI flags
|
|
"tests/providers/test_gateway_catalog.py", # live gateway catalog smoke test; execution depends on opt-in flag and credentials
|
|
]
|
|
branch = true
|
|
# Disable include-ignored warnings as --source is enabled automatically causing a self conflict as per:
|
|
# https://github.com/pytest-dev/pytest-cov/issues/532
|
|
# https://github.com/pytest-dev/pytest-cov/issues/369
|
|
# This prevents coverage being generated by pytest-cov which has direct editor support in VS Code,
|
|
# making it super useful to check coverage while writing tests.
|
|
disable_warnings = ["include-ignored"]
|
|
|
|
[tool.coverage.paths]
|
|
# Allow CI run assets to be downloaded an replicated locally.
|
|
source = [
|
|
".",
|
|
"/home/runner/work/pydantic-ai/pydantic-ai",
|
|
"/System/Volumes/Data/home/runner/work/pydantic-ai/pydantic-ai",
|
|
]
|
|
|
|
# https://coverage.readthedocs.io/en/latest/config.html#report
|
|
[tool.coverage.report]
|
|
fail_under = 100
|
|
skip_covered = true
|
|
show_missing = true
|
|
ignore_errors = true
|
|
precision = 2
|
|
exclude_lines = [
|
|
# `# pragma: no cover` is standard marker for code that's not covered, this will error if code is covered
|
|
'pragma: no cover',
|
|
# use `# pragma: lax no cover` if you want to ignore cases where (some of) the code is covered
|
|
'pragma: lax no cover',
|
|
'raise NotImplementedError',
|
|
'if TYPE_CHECKING:',
|
|
'if typing.TYPE_CHECKING:',
|
|
'@overload',
|
|
'@deprecated',
|
|
'@typing.overload',
|
|
'@abstractmethod',
|
|
'\(Protocol\):$',
|
|
'typing.assert_never',
|
|
'$\s*assert_never\(',
|
|
'if __name__ == .__main__.:',
|
|
'except ImportError as _import_error:',
|
|
'$\s*pass$',
|
|
'assert False',
|
|
'@pytest\.mark\.skip',
|
|
'@pytest\.mark\.xfail',
|
|
]
|
|
|
|
[tool.logfire]
|
|
ignore_no_config = true
|
|
|
|
[tool.inline-snapshot]
|
|
format-command = "ruff format --stdin-filename {filename}"
|
|
|
|
[tool.inline-snapshot.shortcuts]
|
|
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,fpr,aci'
|