pyproject.toml•10.3 kB
[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 = "Agent Framework / shim to use Pydantic with LLMs"
authors = [
    { name = "Samuel Colvin", email = "samuel@pydantic.dev" },
    { name = "Marcelo Trylesinski", email = "marcelotryle@gmail.com" },
    { name = "David Montague", email = "david@pydantic.dev" },
    { name = "Alex Hall", email = "alex@pydantic.dev" },
    { name = "Douwe Maan", email = "douwe@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",
    "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,vertexai,google,groq,anthropic,mistral,cohere,bedrock,huggingface,cli,mcp,fastmcp,evals,ag-ui,retries,temporal,logfire,ui]=={{ version }}",
]
[tool.hatch.metadata.hooks.uv-dynamic-versioning.optional-dependencies]
examples = ["pydantic-ai-examples=={{ version }}"]
a2a = ["fasta2a>=0.4.1"]
dbos = ["pydantic-ai-slim[dbos]=={{ version }}"]
prefect = ["pydantic-ai-slim[prefect]=={{ version }}"]
outlines-transformers = ["pydantic-ai-slim[outlines-transformers]=={{ version }}"]
outlines-llamacpp = ["pydantic-ai-slim[outlines-llamacpp]=={{ version }}"]
outlines-mlxlm = ["pydantic-ai-slim[outlines-mlxlm]=={{ version }}"]
outlines-sglang = ["pydantic-ai-slim[outlines-sglang]=={{ version }}"]
outlines-vllm-offline = ["pydantic-ai-slim[outlines-vllm-offline]=={{ 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 }
[tool.uv.workspace]
members = [
    "pydantic_ai_slim",
    "pydantic_evals",
    "pydantic_graph",
    "clai",
    "examples",
]
[tool.uv]
default-groups = ["dev", "lint", "docs"]
[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",
    "inline-snapshot>=0.19.3",
    "pytest>=8.3.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]",
    "strict-no-cover @ git+https://github.com/pydantic/strict-no-cover.git@7fc59da2c4dff919db2095a0f0e47101b657131d",
    "pytest-xdist>=3.6.1",
    # Needed for PyCharm users
    "pip>=25.2",
    "genai-prices>=0.0.28",
    "mcp-run-python>=0.0.20",
]
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-redirects>=1.2.2',
    "mkdocs-material[imaging]>=9.5.45",
    "mkdocstrings-python>=1.12.2",
    "griffe-warnings-deprecated>=1.1.0",
]
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
target-version = "py310"
include = [
    "pydantic_ai_slim/**/*.py",
    "pydantic_evals/**/*.py",
    "pydantic_graph/**/*.py",
    "examples/**/*.py",
    "clai/**/*.py",
    "tests/**/*.py",
    "docs/**/*.py",
]
[tool.ruff.lint]
extend-select = [
    "Q",
    "RUF100",
    "RUF018", # https://docs.astral.sh/ruff/rules/assignment-in-assert/
    "C90",
    "UP",
    "I",
    "D",
    "TID251",
]
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"]
# weird issue with ruff thinking fasta2a is still editable
known-third-party = ["fasta2a"]
[tool.ruff.lint.pydocstyle]
convention = "google"
[tool.ruff.lint.flake8-tidy-imports.banned-api]
"typing.TypedDict".msg = "Use typing_extensions.TypedDict 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"]
[tool.pyright]
pythonVersion = "3.12"
typeCheckingMode = "strict"
reportMissingTypeStubs = false
reportUnnecessaryIsInstance = false
reportUnnecessaryTypeIgnoreComment = true
reportMissingModuleSource = false
include = [
    "pydantic_ai_slim",
    "pydantic_evals",
    "pydantic_graph",
    "tests",
    "examples",
    "clai",
]
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 },
]
exclude = [
    "examples/pydantic_ai_examples/weather_agent_gradio.py",
    "pydantic_ai_slim/pydantic_ai/ext/aci.py",               # aci-sdk is too niche to be added as an (optional) dependency
]
[tool.mypy]
files = "tests/typed_agent.py,tests/typed_graph.py"
strict = true
[tool.pytest.ini_options]
testpaths = ["tests", "docs/.hooks"]
xfail_strict = true
filterwarnings = [
    "error",
    # Issue with python-multipart - we don't want to bump the minimum version of starlette.
    "ignore::PendingDeprecationWarning:starlette",
    # mistralai accesses model_fields on the instance, which is deprecated in Pydantic 2.11.
    "ignore:Accessing the 'model_fields' attribute",
    # boto3
    "ignore::DeprecationWarning:botocore.*",
    "ignore::RuntimeWarning:pydantic_ai.mcp",
    # 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 event loop:ResourceWarning",
]
# addopts = ["--inline-snapshot=create,fix"]
# 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/test_live.py",
    "tests/example_modules/*.py",
    "pydantic_ai_slim/pydantic_ai/ext/aci.py", # aci-sdk is too niche to be added as an (optional) dependency
]
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',
]
[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,aci'