Contributing
We’d love you to contribute to Pydantic AI!
Pydantic AI is maintained by a small team. We set our own priorities based on what benefits the most users, and we work through issues and PRs in that order — not in the order they arrive.
- Found a bug? Open an issue with a clear description and a minimal reproducible example. Including a Logfire trace link helps us debug dramatically faster.
- Want a feature or API change? Open an issue describing the problem you’re solving. Do not start with code.
- Want to help build a feature? Comment on the issue explaining why you need it and what context you bring. We call this being a “champion” — more on that below.
- Have a fix or code to share? Make sure a maintainer has agreed to the approach on the issue and assigned you. Then open a PR.
The rest of this page explains why we work this way and what to expect.
For anything non-trivial, align with a maintainer on the approach before writing code. A pre-aligned PR is much faster to land than one we’re seeing cold.
Typos, broken links, small doc improvements, obvious one-line fixes: just open a PR. No issue needed.
If the fix could reasonably go more than one way, or you’re unsure it’s actually a bug: open an issue first. Include a minimal reproducible example and ideally a Logfire trace link showing the problem. For well-scoped bugs, we may generate a fix internally — the most valuable thing you can do is file a clear report and then validate that the fix works for your use case.
Before writing code, ask whether the change needs to live in core at all. Most new agent behaviors belong in Pydantic AI Harness, the official capability library — not in this repo. Pydantic AI core is for the agent loop, model providers, and capabilities that require model-specific support or are fundamental to the agent experience. Standalone capabilities — guardrails, memory, context management, file system access, etc. — belong in the harness, where they can iterate faster. See What goes where? for the full distinction.
If your idea is a capability, open an issue on pydantic-ai-harness instead. You can also publish capabilities as your own package using the pydantic-ai-<name> convention — see Publishing capability packages. Once a capability has real users and a stable API, we can talk about upstreaming to harness or core.
If it does belong in core:
- Search first. If an existing issue covers your need, comment there. If the closest match is only related, open a new issue and link it.
- Describe the problem, not just the solution. Tell us what you’re building, what’s blocking you, and what you’ve tried. This context matters more than code.
- Propose a plan before building. Post a short plan on the issue, or open a draft PR with just a
PLAN.md. For larger features, we do short video calls with contributors to iterate on the design — a 20-minute call often saves weeks of async review cycles. - Wait for assignment. A maintainer needs to agree on the approach and assign the issue to you before you open a PR. Unassigned PRs may be auto-closed.
A “champion” is someone who needs a feature, has context on the problem, and is willing to invest time to help us get it right. If you want to champion a feature:
- Comment on the issue explaining: what you’re building, why you need this, and what you can contribute (domain knowledge, testing, validation).
- We prioritize features where one or more champions with production use cases have stepped up. A feature with no champion stays in the backlog until either we prioritize it ourselves or someone with real context shows up.
- Being a champion doesn’t mean writing the code. It means shaping the plan and validating the result. For significant features, we’ll set up a call to iterate on the design together.
Champions are credited as co-authors when the feature ships.
We do not automatically triage every new PR. PRs on issues we have not pre-aligned on are not in our review queue, regardless of how well written they are. If no maintainer has agreed to the change on an issue and assigned it to you, assume we have not seen your PR.
Even for PRs with code we’ve previously engaged with: we treat all contributed code as a starting point, not a finished product. We review and prioritize PRs based on the feature’s importance to the project, not on how much effort went into the code. This is a change from how open source traditionally worked, and we’d rather be honest about it than leave PRs sitting with no signal.
If you want to know where your PR stands, the best thing to do is ping #pydantic-ai on Pydantic Slack.
We treat contributed code as illustrative: a starting point that shows the proposed change and proves the approach works, not the final form we merge. The most useful thing you can give us for a non-trivial change is a plan plus a working example — not a polished, merge-ready implementation.
On any PR, we may push commits to your branch, open a follow-up PR that supersedes yours, or rewrite from scratch. For security reasons, we lean toward rewriting contributed code rather than merging as-is. You will still be credited as the original author.
Please don’t spend effort chasing green CI, addressing every automated review comment, or rebasing for merge conflicts on a PR we haven’t pre-aligned on. If we take the change forward, that polish gets thrown away when we rewrite. Get the approach working, then stop and ping us on Slack.
Do not force-push updates to an open PR. Rewriting its commits invalidates previous reviews; push follow-up commits instead. We will squash them when merging.
PRs are automatically reviewed by Devin and our own tooling. These reviews are advisory:
- A bot approval does not mean your PR is ready to merge. Only a human maintainer’s review counts.
- A bot finding does not mean you must act on it. If you disagree, say so.
- If automated review is generating noise on your PR, tell us. We use that feedback to retune the tooling.
We receive far more contributions than we can review, and we focus where it has the most impact. We cannot promise to get to every PR, even good ones, and we’d rather say so up front than leave your work open indefinitely with no signal.
How we weigh priorities:
- User demand — features that more users need get priority. Champion-backed features with production use cases outrank speculative additions.
- Provider significance — work that affects frontier providers (Anthropic, OpenAI, Google) or providers we know are heavily used gets priority. A model integration for a niche provider will wait; a fix for Anthropic won’t.
- Roadmap alignment — features that align with our current focus areas get priority. Right now that includes the capabilities/hooks API, provider-adaptive tools, and the Pydantic AI Harness capability library.
- Capabilities over core — features that could live as a capability should go to Pydantic AI Harness or ship as your own package — that’s often the fastest path. Once it has traction, come back and we can talk about upstreaming.
- Ping
#pydantic-aion Pydantic Slack with a link. - Say what you need: “Can you take a look?”, “I’m blocked — is this on your radar?”, or “Should I close this?” are all fine.
- If you’ve been waiting weeks without any human response, flag it. That’s a process failure on our side and we want to know.
Clone your fork and cd into the repo directory
git clone git@github.com:<your username>/pydantic-ai.git
cd pydantic-ai
Install uv. The minimum supported uv version is set by tool.uv.required-version in the repository’s pyproject.toml.
Install pydantic-ai, all dependencies, and pre-commit hooks. If pre-commit is not available, this also installs it with uv:
make install
We use make to manage most commands you’ll need to run.
For details on available commands, run:
make help
To run code formatting, linting, static type checks, and tests with coverage report generation, run:
make
make typecheck runs Pyright over every file in the project.
The pre-commit hook runs make typecheck-changed instead, which checks only the files whose content
changed since Pyright last passed plus everything that transitively imports them. It records what
passed under your git directory, so the record is per-worktree and never committed.
CI runs that same hook, and whenever it runs it checks everything: GitHub Actions always sets CI,
and on seeing it make typecheck-changed narrows nothing and hands the whole project to
make typecheck-pyright. A fresh runner has no record to narrow against in the first place. CI still
skips the hook outright on a pull request that touches nothing Pyright reads, as it did before.
Locally it follows the imports Pyright resolves statically, and never checks a file under tests/
that did not itself change since the run it recorded. Tests are two thirds of the project’s lines
and most of them import pydantic_ai, so checking them here would put the whole project back on the
command line for any change to the library; CI is the gate for a source change that breaks a test
file’s typing.
It gives up on narrowing whenever something could leave that set incomplete: a first run; a new
Pyright or Python version, including one asked for through PYRIGHT_PYTHON; a change to
pyproject.toml, uv.lock or the Makefile; an import that would now resolve to a different file
or a new top-level module that could shadow an installed one; or a change reaching more than half the
project. It then runs Pyright over every tracked file Pyright reports on, minus the tests/ files
that did not change; a first run has no record to compare them against, so it checks all of them.
Only three things hand the whole project to make typecheck-pyright: CI, an interpreter older
than Python 3.11, which is what it needs to read pyproject.toml, and a Pyright configuration it
cannot reproduce. Every other run considers tracked files only, so run make typecheck yourself
before relying on a green hook for a file you have not added.
A full run is single-process unless PYRIGHT_THREADS says otherwise, and CI sets it to auto.
The variable turns on Pyright’s parallel check phase, which reaches the same diagnostics in less
wall time: auto is up to one worker per logical core, and a positive integer caps them. Only
make typecheck-pyright reads it, so the hook picks it up only on a run that hands the whole
project over: CI, an interpreter older than Python 3.11, or a Pyright configuration it cannot
reproduce. A run it narrows, or runs itself over the reduced set, stays single-process.
export PYRIGHT_THREADS=auto
Export it rather than setting it per command, so every make typecheck picks it up. Every worker is
a full Node process, so they pay for themselves only on a machine with the memory to hold them; one
already near its limit swaps and comes out slower than the default. Unset the variable or set it to
1 to go back to a single process: anything Pyright cannot read as a positive integer, 0 and
off included, means auto.
docs/navigation.yml
owns the sidebar, public routes, and redirects for
Pydantic AI’s documentation. Update it when adding, removing,
or moving a page.
All routes in docs/navigation.yml are relative to the Pydantic AI documentation root. Give each
page its complete canonical route in slug; use aliases only for redirect sources. Do not prefix
either value with /ai or a leading slash.
To validate navigation changes, ask a maintainer to add the trigger:docs label to the pull request.
This checks the navigation manifest, referenced Markdown files, routes, aliases, and redirects in
pydantic/unified-docs and posts the result on the PR. It does not build a rendered preview.
CI checks that every link between doc pages resolves, anchor included, and fails on Cannot find fragment. A heading’s anchor is generated from its text, so renaming one silently breaks every link
pointing at it. Where a heading is linked to, pin its anchor with {#custom-id} — the heading text
is then free to change without moving the anchor.
To avoid an excessive workload for the maintainers of Pydantic AI, we can’t accept all model contributions, so we’re setting the following rules for when we’ll accept new models and when we won’t. This should hopefully reduce the chances of disappointment and wasted work.
- To add a new model with an extra dependency, that dependency needs > 500k monthly downloads from PyPI consistently over 3 months or more
- To add a new model which uses another model’s logic internally and has no extra dependencies, that model’s GitHub org needs > 20k stars in total
- For any other model that’s just a custom URL and API key, we’re happy to add a one-paragraph description with a link and instructions on the URL to use
- For any other model that requires more logic, we recommend you release your own Python package
pydantic-ai-xxx, which depends onpydantic-ai-slimand implements a model that inherits from ourModelABC
If you’re unsure about adding a model, please create an issue.