[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6BYVKN2HSZJY4NCVB9KRTZ2":3,"skill-same:01M6BYVKN2HSZJY4NCVB9KRTZ2":91},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":31,"publishedAt":61,"linkOnly":62,"markdownContent":63,"markdownBody":64,"frontMatter":65,"refName":68,"commitSha":69,"skillDirectory":70,"forksCount":71,"repositoryHtmlUrl":72,"repositoryHomepageUrl":73,"ownerHtmlUrl":74,"ownerBlogUrl":75,"ownerDescription":76,"updatedAt":77,"install":78,"resources":90},"01M6BYVKN2HSZJY4NCVB9KRTZ2","adding-a-provider-api-feature",null,"Add a new provider API capability (prompt caching, strict\u002Fstructured tool calling, thinking\u002Freasoning effort, service tier, safety settings, logprobs, etc.) to Pydantic AI. Use when wiring a provider feature through the library — it enforces reasoning from the existing cross-provider abstraction before designing anything, and picking default-on vs opt-in deliberately. Not for adding a new model id (that's a different flow) or a bug fix.","pydantic\u002Fpydantic-ai","pydantic","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F110818415?v=4","01M6BYVCCAN7VAVS8DFHJF4GY1","Pydantic","01M6BYVCGHJCX3C0VZF6J7BA7V",21193,"MIT","https:\u002F\u002Fgithub.com\u002Fpydantic\u002Fpydantic-ai\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002Fadding-a-provider-api-feature\u002FSKILL.md",[18,23,26],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000010","后端与接口","SCENARIO","使用场景",{"categoryNo":24,"categoryName":25,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000023","Skill与智能体开发",{"categoryNo":27,"categoryName":28,"taxonomyType":29,"taxonomyTypeDesc":30},"01JBSEEDCAT000000000000030","开发工程师","PROFESSION","适用职业",[32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,12,57,58,59,60],"provider api","api feature","feature","api","provider","adding a provider api feature","new","reasoning","when","wiring","ls","caching","thinking","default","uv","fix","read","grep","gh","model","tool","bug fix","Bash","Safety","pydantic ai","prompt caching","flow","library","git","2026-11-10 10:12:30",false,"---\nname: adding-a-provider-api-feature\ndescription: Add a new provider API capability (prompt caching, strict\u002Fstructured tool calling, thinking\u002Freasoning effort, service tier, safety settings, logprobs, etc.) to Pydantic AI. Use when wiring a provider feature through the library — it enforces reasoning from the existing cross-provider abstraction before designing anything, and picking default-on vs opt-in deliberately. Not for adding a new model id (that's a different flow) or a bug fix.\nuser-invocable: true\nallowed-tools: Bash(git:*), Bash(gh:*), Bash(rg:*), Bash(ls:*), Bash(uv:*), Read, Write, Edit, Glob, Grep, WebFetch, AskUserQuestion, Agent\n---\n\n# Adding a provider API feature\n\nUse this when exposing a **new provider API capability** through Pydantic AI — prompt caching, strict\u002Fstructured tool calling, thinking\u002Freasoning effort, service tier, safety settings, logprobs, cache breakpoints, and the like. The output is a change that is *consistent with how sibling providers already expose the same concept*, defaults deliberately, and gates support with a capability flag.\n\nNot for: adding a new model id (that's `add-new-model`), a bug fix, or a refactor.\n\n## The one rule that prevents the most rework\n\n**Before designing anything, find the existing cross-provider abstraction that governs this capability and let its shape decide the API.** Most \"how should I expose this?\" questions are already answered by an abstraction the codebase has — reaching for a new provider-specific knob when one exists is the single most common thing maintainers reject. When a feature routes through an existing abstraction, the abstraction's shape pre-decides the API surface, the opt-out, and often the default.\n\nThe tell that you skipped this: you find yourself listing 2-3 \"options\" for how a user controls the feature. If one of those options duplicates an existing cross-provider control, it isn't a real option — the existing abstraction wins.\n\n## Step 0 — Enumerate sibling precedent\n\nFor the capability you're adding, list **how every provider that already has an analog exposes it**, and name the governing existing abstraction. It is one of:\n\n- a **per-tool flag** — `ToolDefinition.strict: bool | None` (`tools.py`), resolved in `models\u002F__init__.py::_customize_tool_def`;\n- a **shared `ModelSettings` field** — `thinking`, `service_tier` (`settings.py`), each with per-model resolvers mapping to native concepts;\n- a **provider-prefixed `{Provider}ModelSettings` field** — `anthropic_cache`, `openai_prompt_cache_key`, `groq_reasoning_effort`;\n- a **message-stream marker** — `CachePoint` in `UserPromptPart.content` (`messages.py`);\n- a **`ModelProfile` capability flag** — `openai_supports_strict_tool_definition`, `bedrock_supports_prompt_caching`.\n\nRead the actual sibling implementations and any review threads on the PRs that added them (`gh pr view \u003Cn> --comments`). Contributors who skipped this got redirected: raw `google_tool_config` → use `strict` ([#5366](https:\u002F\u002Fgithub.com\u002Fpydantic\u002Fpydantic-ai\u002Fissues\u002F5366#issuecomment-4909047000)); string-prefix model detection → use a profile flag ([#4604](https:\u002F\u002Fgithub.com\u002Fpydantic\u002Fpydantic-ai\u002Fpull\u002F4604#discussion_r2967152134)). **Only open a design fork if no existing abstraction covers the capability.**\n\n## Step 1 — Pick the API shape\n\n1. **An existing cross-provider abstraction covers it → reuse it.** Add a provider mapping (a `_translate_*` resolver, a `JsonSchemaTransformer` subclass, a `CachePoint` translation). Do **not** add a provider-prefixed knob for something the shared abstraction already expresses.\n2. **No shared abstraction, but ≥3 providers now have the concept → promote to a shared `ModelSettings` field** with a deliberately narrow common vocabulary and per-model resolvers, keeping per-provider fields underneath as precedence-winning escape hatches (the `service_tier` promotion, [#4926](https:\u002F\u002Fgithub.com\u002Fpydantic\u002Fpydantic-ai\u002Fpull\u002F4926#discussion_r3028738692)). Don't delete provider knobs; deprecate only genuinely-misnamed ones with a `TODO(v3)`.\n3. **The provider's native values can't be expressed by the shared enum** (family-disjoint value sets, platform-only request shaping) → a `{provider}_*` knob is justified, and it **coexists with and outranks** the unified field (`groq_reasoning_effort`, [#5797](https:\u002F\u002Fgithub.com\u002Fpydantic\u002Fpydantic-ai\u002Fpull\u002F5797)).\n4. **Choose the locus by \"can the user naturally point at it?\":** a boundary *inside* the message stream → a marker (`CachePoint`); a structural region (system prompt, tool defs, whole-request setting) → a setting ([#3363](https:\u002F\u002Fgithub.com\u002Fpydantic\u002Fpydantic-ai\u002Fpull\u002F3363#discussion_r2505366576)).\n5. **Wire it into every request path** the provider has (chat *and* responses APIs); put a setting shared across a provider's APIs on the **base** settings class ([#3678](https:\u002F\u002Fgithub.com\u002Fpydantic\u002Fpydantic-ai\u002Fpull\u002F3678#discussion_r2602911025)).\n6. **Type it.** Reuse the provider SDK's own types where they exist; type knobs as `Literal`, never `extra_body` or untyped `**kwargs` (`models\u002FAGENTS.md`; *\"kwargs are a big no no… I'd rather be repetitive but type safe\"* — DouweM, [#3457](https:\u002F\u002Fgithub.com\u002Fpydantic\u002Fpydantic-ai\u002Fpull\u002F3457#discussion_r2547862149)).\n\n## Step 2 — Default: default-on (silent) vs opt-in\n\nDefault the feature **on** only when enabling it **cannot change observable behavior and cannot cost the user** — a pure, backward-compatible improvement (caching only *lowers* cost; a validation mode that needs no schema rewrite and can't reject a previously-valid request). A backward-compatible, purely-better default is welcome and needs no opt-in.\n\nKeep it **opt-in** when it:\n\n- changes observable output or wire behavior;\n- is a **preview** feature (provider may change semantics);\n- can *raise* cost — and choose the default *value* so a shared field never silently upgrades anyone to a pricier tier (`service_tier` `'auto'` vs `'default'`, [#4926](https:\u002F\u002Fgithub.com\u002Fpydantic\u002Fpydantic-ai\u002Fpull\u002F4926));\n- can hit provider limits at scale (auto-promoting strict silently broke agents with >20 tools; reverted in [#5580](https:\u002F\u002Fgithub.com\u002Fpydantic\u002Fpydantic-ai\u002Fpull\u002F5580));\n- applies lossy schema rewrites (OpenAI\u002FAnthropic strict transforms).\n\nWhen the default isn't obvious, **decide it with a live probe, not an opinion** ([#5897](https:\u002F\u002Fgithub.com\u002Fpydantic\u002Fpydantic-ai\u002Fpull\u002F5897) flipped a default on 0\u002F5 → 5\u002F5 recovery). Beware \"automatic\" language — verify whether it means a Pydantic AI default or just provider-side management of an opt-in feature.\n\n## Step 3 — Capability gating\n\nDetect support with a provider-prefixed **`ModelProfile` flag**. Where the feature is used, read only the flag or the adapter's own declaration — never an inline `isinstance`, model-name or `base_url` check (`profiles\u002FAGENTS.md`). Gate at the layer(s) that actually vary:\n\n- **model\u002Ffamily** → a flag in the family's profile function (`google_supports_strict_tool_definition` in `profiles\u002Fgoogle.py`);\n- **one provider's API** → a flag set in that provider's `Provider.model_profile()` (`bedrock_supports_prompt_caching` in `providers\u002Fbedrock.py`);\n- **transport** (the Bedrock, Vertex and Foundry client classes of one SDK, or a client's own mode such as `genai.Client.vertexai`) → a flag set in the model's `_instance_profile_defaults()`, or in its `profile` property when the client can't send the feature at all. `Provider.model_profile()` is static and sees only the model name (`models\u002FAGENTS.md`, the client-class bullet);\n- **per-schema** → the `JsonSchemaTransformer.is_strict_compatible` signal (default conservative unless the mode needs no rewrites);\n- **SDK version** → probe the SDK shape and degrade with a `UserWarning` ([#5580](https:\u002F\u002Fgithub.com\u002Fpydantic\u002Fpydantic-ai\u002Fpull\u002F5580), botocore strict param);\n- **unknowable client-side** → defer to the runtime API error.\n\n**Unsupported → silently ignore** the setting (best-effort so as many requests as possible succeed), documented in the docstring — never hard-error. Conflicting user settings → `UserWarning`, not `UserError`. Profiles are layered: developer-keyed base + thin provider overlay resolved per family, with the provider overlay layering **last** ([#5934](https:\u002F\u002Fgithub.com\u002Fpydantic\u002Fpydantic-ai\u002Fissues\u002F5934), [#6231](https:\u002F\u002Fgithub.com\u002Fpydantic\u002Fpydantic-ai\u002Fpull\u002F6231)).\n\n## Step 3b — Where the wire mapping goes\n\nRead `agent_docs\u002Fprovider-adapters.md` before you edit an adapter's mapper. Place the feature by what it adds:\n\n- **a new part type** → one arm in each message kind's dispatch, calling the part's own method on the model class. When most APIs can't render the part, lower it in `Model.prepare_messages` instead; an adapter that doesn't render it raises `_unprepared_part_error(part)` in its one arm.\n- **a new native tool kind** → one entry in the adapter's `kind`-to-wire-name table. Add a branch only where the wire shape differs.\n- **a new response wire item** → one function, called from both `_process_response` and the stream.\n- **a new flag or setting that changes how an existing part maps** → read it inside that part's method. Where the adapter still maps that part inline in its loop, read it where the part is mapped today, and leave extracting the method to its own structural PR.\n\n## Step 4 — Tests\n\nPyright does not flag a `Literal`-keyed dict that misses a key; the lookup raises `KeyError` only at request time. Add every new mapping keyed by `ThinkingEffort` to `test_every_effort_level_resolves` in `tests\u002Ftest_thinking.py`, which runs each level through each mapping. When you add a value to another shared `Literal`, write the same kind of test for it.\n\nLive-recorded **wire-contract cassette** asserting the exact outbound body (mode\u002Ffield is sent for supported models, absent\u002Fignored for unsupported), plus a test exercising the new setting. Prefer case-based parametrized VCR over mocks; a unit test is still right for asserting an internal request shape a cassette matcher wouldn't catch. This is also where a review bot's \"this will fail\" claim gets refuted with a recorded cassette.\n\n## Step 5 — Docs & skills\n\nThe new public symbol's docstring lists **which providers support it** and how each interprets the value. Add\u002Frefresh the `docs\u002F**.md` section and any sibling docstrings that now under-claim (\"only OpenAI\" → the full provider list). Describe the mechanism only as far as the provider documents it — don't assert a mechanism a provider's docs leave unstated. Update the relevant agent skill.\n\n`ModelSettings`' `Supported by:` lists are enforced: `tests\u002Fmodels\u002Ftest_model_settings_support.py` probes each model class's outgoing request and asserts every list names exactly the classes that send the field. Forwarding a new setting means editing its list, and a new `Model` class means adding a `Case` there. `tool_choice` and `thinking` are exempt via `HAND_MAINTAINED` and stay hand-maintained.\n\n## Recurring maintainer principles (quoted)\n\n1. **Reuse the cross-provider abstraction over a provider knob** — *\"this maps to what the Anthropic and OpenAI APIs call `strict`… already represented on `ToolDefinition`… a more complete, consistent, 'doesn't require the user to do something special' implementation would be to automatically use this mode.\"* ([#5366](https:\u002F\u002Fgithub.com\u002Fpydantic\u002Fpydantic-ai\u002Fissues\u002F5366#issuecomment-4909047000))\n2. **Promote to a shared setting once several providers have the concept; keep per-provider overrides underneath.** ([#4926](https:\u002F\u002Fgithub.com\u002Fpydantic\u002Fpydantic-ai\u002Fpull\u002F4926#discussion_r3028738692))\n3. **Best-effort — silently ignore unsupported settings, don't error.** *\"we typically do a 'best effort' so that as many requests as possible succeed.\"* ([#3438](https:\u002F\u002Fgithub.com\u002Fpydantic\u002Fpydantic-ai\u002Fpull\u002F3438#issuecomment-3553448605))\n4. **Hide provider complexity** — the feature should be useful to people who don't want to become experts in that provider's limitations.\n5. **Capability facts belong on `ModelProfile` flags, layered base + overlay.** ([#5934](https:\u002F\u002Fgithub.com\u002Fpydantic\u002Fpydantic-ai\u002Fissues\u002F5934#issuecomment-4858653237))\n6. **Type safety over repetition; no untyped kwargs.** ([#3457](https:\u002F\u002Fgithub.com\u002Fpydantic\u002Fpydantic-ai\u002Fpull\u002F3457#discussion_r2547862149))\n7. **Put shared fields on the base settings class; cover every API surface.** ([#3678](https:\u002F\u002Fgithub.com\u002Fpydantic\u002Fpydantic-ai\u002Fpull\u002F3678#discussion_r2602911025))\n8. **Verify against the real API; defer validation to runtime.** *\"If they're allowed by the SDK types and we can try it out and it doesn't fail, I'm fine with it.\"* ([#3678](https:\u002F\u002Fgithub.com\u002Fpydantic\u002Fpydantic-ai\u002Fpull\u002F3678#discussion_r2604746387))\n9. **Name the eventual unification even when deferring it** — a `{provider}_*` knob today can note the future `Caching`\u002F`Thinking`-style capability it should fold into. ([#4604](https:\u002F\u002Fgithub.com\u002Fpydantic\u002Fpydantic-ai\u002Fpull\u002F4604#discussion_r3277672350))\n\n## Precedent map\n\n| Capability | Reused abstraction | Default | Gating | PR |\n|---|---|---|---|---|\n| service tier (cross-provider) | promoted to `ModelSettings.service_tier` | opt-in, never silent upgrade | map-and-drop | [#4926](https:\u002F\u002Fgithub.com\u002Fpydantic\u002Fpydantic-ai\u002Fpull\u002F4926) |\n| strict — OpenAI (origin) | `ToolDefinition.strict` | auto-promote per compatible schema | per-schema `is_strict_compatible` | [#1304](https:\u002F\u002Fgithub.com\u002Fpydantic\u002Fpydantic-ai\u002Fpull\u002F1304) |\n| strict — Anthropic | reused `strict` | conservative opt-in | transformer + profile flag | [#3457](https:\u002F\u002Fgithub.com\u002Fpydantic\u002Fpydantic-ai\u002Fpull\u002F3457) |\n| strict — Bedrock (+ fix) | reused `strict` | opt-in (auto-promote reverted) | transformer + profile + SDK probe | [#4237](https:\u002F\u002Fgithub.com\u002Fpydantic\u002Fpydantic-ai\u002Fpull\u002F4237), [#5580](https:\u002F\u002Fgithub.com\u002Fpydantic\u002Fpydantic-ai\u002Fpull\u002F5580) |\n| strict — Gemini `VALIDATED` | reused `strict`; rejected raw `google_tool_config` | **default-on** (mode needs no schema rewrite) | profile flag; `is_strict_compatible = True` | [#6353](https:\u002F\u002Fgithub.com\u002Fpydantic\u002Fpydantic-ai\u002Fpull\u002F6353) |\n| thinking (cross-provider) | `ModelSettings.thinking` + per-provider maps | opt-in, graceful degradation | `supports_thinking` flags | [#4640](https:\u002F\u002Fgithub.com\u002Fpydantic\u002Fpydantic-ai\u002Fpull\u002F4640) |\n| reasoning effort — Groq | provider knob coexists with `thinking`, outranks it | opt-in | per-family profile flag | [#5797](https:\u002F\u002Fgithub.com\u002Fpydantic\u002Fpydantic-ai\u002Fpull\u002F5797), [#6231](https:\u002F\u002Fgithub.com\u002Fpydantic\u002Fpydantic-ai\u002Fpull\u002F6231) |\n| prompt caching — Anthropic → Bedrock\u002FOpenRouter | `CachePoint` marker + settings; breakpoint limits through `models\u002F_prompt_cache.py` | opt-in | `{provider}_supports_prompt_caching` | [#3363](https:\u002F\u002Fgithub.com\u002Fpydantic\u002Fpydantic-ai\u002Fpull\u002F3363), [#3438](https:\u002F\u002Fgithub.com\u002Fpydantic\u002Fpydantic-ai\u002Fpull\u002F3438), [#4604](https:\u002F\u002Fgithub.com\u002Fpydantic\u002Fpydantic-ai\u002Fpull\u002F4604) |\n","# Adding a provider API feature\n\nUse this when exposing a **new provider API capability** through Pydantic AI — prompt caching, strict\u002Fstructured tool calling, thinking\u002Freasoning effort, service tier, safety settings, logprobs, cache breakpoints, and the like. The output is a change that is *consistent with how sibling providers already expose the same concept*, defaults deliberately, and gates support with a capability flag.\n\nNot for: adding a new model id (that's `add-new-model`), a bug fix, or a refactor.\n\n## The one rule that prevents the most rework\n\n**Before designing anything, find the existing cross-provider abstraction that governs this capability and let its shape decide the API.** Most \"how should I expose this?\" questions are already answered by an abstraction the codebase has — reaching for a new provider-specific knob when one exists is the single most common thing maintainers reject. When a feature routes through an existing abstraction, the abstraction's shape pre-decides the API surface, the opt-out, and often the default.\n\nThe tell that you skipped this: you find yourself listing 2-3 \"options\" for how a user controls the feature. If one of those options duplicates an existing cross-provider control, it isn't a real option — the existing abstraction wins.\n\n## Step 0 — Enumerate sibling precedent\n\nFor the capability you're adding, list **how every provider that already has an analog exposes it**, and name the governing existing abstraction. It is one of:\n\n- a **per-tool flag** — `ToolDefinition.strict: bool | None` (`tools.py`), resolved in `models\u002F__init__.py::_customize_tool_def`;\n- a **shared `ModelSettings` field** — `thinking`, `service_tier` (`settings.py`), each with per-model resolvers mapping to native concepts;\n- a **provider-prefixed `{Provider}ModelSettings` field** — `anthropic_cache`, `openai_prompt_cache_key`, `groq_reasoning_effort`;\n- a **message-stream marker** — `CachePoint` in `UserPromptPart.content` (`messages.py`);\n- a **`ModelProfile` capability flag** — `openai_supports_strict_tool_definition`, `bedrock_supports_prompt_caching`.\n\nRead the actual sibling implementations and any review threads on the PRs that added them (`gh pr view \u003Cn> --comments`). Contributors who skipped this got redirected: raw `google_tool_config` → use `strict` ([#5366](https:\u002F\u002Fgithub.com\u002Fpydantic\u002Fpydantic-ai\u002Fissues\u002F5366#issuecomment-4909047000)); string-prefix model detection → use a profile flag ([#4604](https:\u002F\u002Fgithub.com\u002Fpydantic\u002Fpydantic-ai\u002Fpull\u002F4604#discussion_r2967152134)). **Only open a design fork if no existing abstraction covers the capability.**\n\n## Step 1 — Pick the API shape\n\n1. **An existing cross-provider abstraction covers it → reuse it.** Add a provider mapping (a `_translate_*` resolver, a `JsonSchemaTransformer` subclass, a `CachePoint` translation). Do **not** add a provider-prefixed knob for something the shared abstraction already expresses.\n2. **No shared abstraction, but ≥3 providers now have the concept → promote to a shared `ModelSettings` field** with a deliberately narrow common vocabulary and per-model resolvers, keeping per-provider fields underneath as precedence-winning escape hatches (the `service_tier` promotion, [#4926](https:\u002F\u002Fgithub.com\u002Fpydantic\u002Fpydantic-ai\u002Fpull\u002F4926#discussion_r3028738692)). Don't delete provider knobs; deprecate only genuinely-misnamed ones with a `TODO(v3)`.\n3. **The provider's native values can't be expressed by the shared enum** (family-disjoint value sets, platform-only request shaping) → a `{provider}_*` knob is justified, and it **coexists with and outranks** the unified field (`groq_reasoning_effort`, [#5797](https:\u002F\u002Fgithub.com\u002Fpydantic\u002Fpydantic-ai\u002Fpull\u002F5797)).\n4. **Choose the locus by \"can the user naturally point at it?\":** a boundary *inside* the message stream → a marker (`CachePoint`); a structural region (system prompt, tool defs, whole-request setting) → a setting ([#3363](https:\u002F\u002Fgithub.com\u002Fpydantic\u002Fpydantic-ai\u002Fpull\u002F3363#discussion_r2505366576)).\n5. **Wire it into every request path** the provider has (chat *and* responses APIs); put a setting shared across a provider's APIs on the **base** settings class ([#3678](https:\u002F\u002Fgithub.com\u002Fpydantic\u002Fpydantic-ai\u002Fpull\u002F3678#discussion_r2602911025)).\n6. **Type it.** Reuse the provider SDK's own types where they exist; type knobs as `Literal`, never `extra_body` or untyped `**kwargs` (`models\u002FAGENTS.md`; *\"kwargs are a big no no… I'd rather be repetitive but type safe\"* — DouweM, [#3457](https:\u002F\u002Fgithub.com\u002Fpydantic\u002Fpydantic-ai\u002Fpull\u002F3457#discussion_r2547862149)).\n\n## Step 2 — Default: default-on (silent) vs opt-in\n\nDefault the feature **on** only when enabling it **cannot change observable behavior and cannot cost the user** — a pure, backward-compatible improvement (caching only *lowers* cost; a validation mode that needs no schema rewrite and can't reject a previously-valid request). A backward-compatible, purely-better default is welcome and needs no opt-in.\n\nKeep it **opt-in** when it:\n\n- changes observable output or wire behavior;\n- is a **preview** feature (provider may change semantics);\n- can *raise* cost — and choose the default *value* so a shared field never silently upgrades anyone to a pricier tier (`service_tier` `'auto'` vs `'default'`, [#4926](https:\u002F\u002Fgithub.com\u002Fpydantic\u002Fpydantic-ai\u002Fpull\u002F4926));\n- can hit provider limits at scale (auto-promoting strict silently broke agents with >20 tools; reverted in [#5580](https:\u002F\u002Fgithub.com\u002Fpydantic\u002Fpydantic-ai\u002Fpull\u002F5580));\n- applies lossy schema rewrites (OpenAI\u002FAnthropic strict transforms).\n\nWhen the default isn't obvious, **decide it with a live probe, not an opinion** ([#5897](https:\u002F\u002Fgithub.com\u002Fpydantic\u002Fpydantic-ai\u002Fpull\u002F5897) flipped a default on 0\u002F5 → 5\u002F5 recovery). Beware \"automatic\" language — verify whether it means a Pydantic AI default or just provider-side management of an opt-in feature.\n\n## Step 3 — Capability gating\n\nDetect support with a provider-prefixed **`ModelProfile` flag**. Where the feature is used, read only the flag or the adapter's own declaration — never an inline `isinstance`, model-name or `base_url` check (`profiles\u002FAGENTS.md`). Gate at the layer(s) that actually vary:\n\n- **model\u002Ffamily** → a flag in the family's profile function (`google_supports_strict_tool_definition` in `profiles\u002Fgoogle.py`);\n- **one provider's API** → a flag set in that provider's `Provider.model_profile()` (`bedrock_supports_prompt_caching` in `providers\u002Fbedrock.py`);\n- **transport** (the Bedrock, Vertex and Foundry client classes of one SDK, or a client's own mode such as `genai.Client.vertexai`) → a flag set in the model's `_instance_profile_defaults()`, or in its `profile` property when the client can't send the feature at all. `Provider.model_profile()` is static and sees only the model name (`models\u002FAGENTS.md`, the client-class bullet);\n- **per-schema** → the `JsonSchemaTransformer.is_strict_compatible` signal (default conservative unless the mode needs no rewrites);\n- **SDK version** → probe the SDK shape and degrade with a `UserWarning` ([#5580](https:\u002F\u002Fgithub.com\u002Fpydantic\u002Fpydantic-ai\u002Fpull\u002F5580), botocore strict param);\n- **unknowable client-side** → defer to the runtime API error.\n\n**Unsupported → silently ignore** the setting (best-effort so as many requests as possible succeed), documented in the docstring — never hard-error. Conflicting user settings → `UserWarning`, not `UserError`. Profiles are layered: developer-keyed base + thin provider overlay resolved per family, with the provider overlay layering **last** ([#5934](https:\u002F\u002Fgithub.com\u002Fpydantic\u002Fpydantic-ai\u002Fissues\u002F5934), [#6231](https:\u002F\u002Fgithub.com\u002Fpydantic\u002Fpydantic-ai\u002Fpull\u002F6231)).\n\n## Step 3b — Where the wire mapping goes\n\nRead `agent_docs\u002Fprovider-adapters.md` before you edit an adapter's mapper. Place the feature by what it adds:\n\n- **a new part type** → one arm in each message kind's dispatch, calling the part's own method on the model class. When most APIs can't render the part, lower it in `Model.prepare_messages` instead; an adapter that doesn't render it raises `_unprepared_part_error(part)` in its one arm.\n- **a new native tool kind** → one entry in the adapter's `kind`-to-wire-name table. Add a branch only where the wire shape differs.\n- **a new response wire item** → one function, called from both `_process_response` and the stream.\n- **a new flag or setting that changes how an existing part maps** → read it inside that part's method. Where the adapter still maps that part inline in its loop, read it where the part is mapped today, and leave extracting the method to its own structural PR.\n\n## Step 4 — Tests\n\nPyright does not flag a `Literal`-keyed dict that misses a key; the lookup raises `KeyError` only at request time. Add every new mapping keyed by `ThinkingEffort` to `test_every_effort_level_resolves` in `tests\u002Ftest_thinking.py`, which runs each level through each mapping. When you add a value to another shared `Literal`, write the same kind of test for it.\n\nLive-recorded **wire-contract cassette** asserting the exact outbound body (mode\u002Ffield is sent for supported models, absent\u002Fignored for unsupported), plus a test exercising the new setting. Prefer case-based parametrized VCR over mocks; a unit test is still right for asserting an internal request shape a cassette matcher wouldn't catch. This is also where a review bot's \"this will fail\" claim gets refuted with a recorded cassette.\n\n## Step 5 — Docs & skills\n\nThe new public symbol's docstring lists **which providers support it** and how each interprets the value. Add\u002Frefresh the `docs\u002F**.md` section and any sibling docstrings that now under-claim (\"only OpenAI\" → the full provider list). Describe the mechanism only as far as the provider documents it — don't assert a mechanism a provider's docs leave unstated. Update the relevant agent skill.\n\n`ModelSettings`' `Supported by:` lists are enforced: `tests\u002Fmodels\u002Ftest_model_settings_support.py` probes each model class's outgoing request and asserts every list names exactly the classes that send the field. Forwarding a new setting means editing its list, and a new `Model` class means adding a `Case` there. `tool_choice` and `thinking` are exempt via `HAND_MAINTAINED` and stay hand-maintained.\n\n## Recurring maintainer principles (quoted)\n\n1. **Reuse the cross-provider abstraction over a provider knob** — *\"this maps to what the Anthropic and OpenAI APIs call `strict`… already represented on `ToolDefinition`… a more complete, consistent, 'doesn't require the user to do something special' implementation would be to automatically use this mode.\"* ([#5366](https:\u002F\u002Fgithub.com\u002Fpydantic\u002Fpydantic-ai\u002Fissues\u002F5366#issuecomment-4909047000))\n2. **Promote to a shared setting once several providers have the concept; keep per-provider overrides underneath.** ([#4926](https:\u002F\u002Fgithub.com\u002Fpydantic\u002Fpydantic-ai\u002Fpull\u002F4926#discussion_r3028738692))\n3. **Best-effort — silently ignore unsupported settings, don't error.** *\"we typically do a 'best effort' so that as many requests as possible succeed.\"* ([#3438](https:\u002F\u002Fgithub.com\u002Fpydantic\u002Fpydantic-ai\u002Fpull\u002F3438#issuecomment-3553448605))\n4. **Hide provider complexity** — the feature should be useful to people who don't want to become experts in that provider's limitations.\n5. **Capability facts belong on `ModelProfile` flags, layered base + overlay.** ([#5934](https:\u002F\u002Fgithub.com\u002Fpydantic\u002Fpydantic-ai\u002Fissues\u002F5934#issuecomment-4858653237))\n6. **Type safety over repetition; no untyped kwargs.** ([#3457](https:\u002F\u002Fgithub.com\u002Fpydantic\u002Fpydantic-ai\u002Fpull\u002F3457#discussion_r2547862149))\n7. **Put shared fields on the base settings class; cover every API surface.** ([#3678](https:\u002F\u002Fgithub.com\u002Fpydantic\u002Fpydantic-ai\u002Fpull\u002F3678#discussion_r2602911025))\n8. **Verify against the real API; defer validation to runtime.** *\"If they're allowed by the SDK types and we can try it out and it doesn't fail, I'm fine with it.\"* ([#3678](https:\u002F\u002Fgithub.com\u002Fpydantic\u002Fpydantic-ai\u002Fpull\u002F3678#discussion_r2604746387))\n9. **Name the eventual unification even when deferring it** — a `{provider}_*` knob today can note the future `Caching`\u002F`Thinking`-style capability it should fold into. ([#4604](https:\u002F\u002Fgithub.com\u002Fpydantic\u002Fpydantic-ai\u002Fpull\u002F4604#discussion_r3277672350))\n\n## Precedent map\n\n| Capability | Reused abstraction | Default | Gating | PR |\n|---|---|---|---|---|\n| service tier (cross-provider) | promoted to `ModelSettings.service_tier` | opt-in, never silent upgrade | map-and-drop | [#4926](https:\u002F\u002Fgithub.com\u002Fpydantic\u002Fpydantic-ai\u002Fpull\u002F4926) |\n| strict — OpenAI (origin) | `ToolDefinition.strict` | auto-promote per compatible schema | per-schema `is_strict_compatible` | [#1304](https:\u002F\u002Fgithub.com\u002Fpydantic\u002Fpydantic-ai\u002Fpull\u002F1304) |\n| strict — Anthropic | reused `strict` | conservative opt-in | transformer + profile flag | [#3457](https:\u002F\u002Fgithub.com\u002Fpydantic\u002Fpydantic-ai\u002Fpull\u002F3457) |\n| strict — Bedrock (+ fix) | reused `strict` | opt-in (auto-promote reverted) | transformer + profile + SDK probe | [#4237](https:\u002F\u002Fgithub.com\u002Fpydantic\u002Fpydantic-ai\u002Fpull\u002F4237), [#5580](https:\u002F\u002Fgithub.com\u002Fpydantic\u002Fpydantic-ai\u002Fpull\u002F5580) |\n| strict — Gemini `VALIDATED` | reused `strict`; rejected raw `google_tool_config` | **default-on** (mode needs no schema rewrite) | profile flag; `is_strict_compatible = True` | [#6353](https:\u002F\u002Fgithub.com\u002Fpydantic\u002Fpydantic-ai\u002Fpull\u002F6353) |\n| thinking (cross-provider) | `ModelSettings.thinking` + per-provider maps | opt-in, graceful degradation | `supports_thinking` flags | [#4640](https:\u002F\u002Fgithub.com\u002Fpydantic\u002Fpydantic-ai\u002Fpull\u002F4640) |\n| reasoning effort — Groq | provider knob coexists with `thinking`, outranks it | opt-in | per-family profile flag | [#5797](https:\u002F\u002Fgithub.com\u002Fpydantic\u002Fpydantic-ai\u002Fpull\u002F5797), [#6231](https:\u002F\u002Fgithub.com\u002Fpydantic\u002Fpydantic-ai\u002Fpull\u002F6231) |\n| prompt caching — Anthropic → Bedrock\u002FOpenRouter | `CachePoint` marker + settings; breakpoint limits through `models\u002F_prompt_cache.py` | opt-in | `{provider}_supports_prompt_caching` | [#3363](https:\u002F\u002Fgithub.com\u002Fpydantic\u002Fpydantic-ai\u002Fpull\u002F3363), [#3438](https:\u002F\u002Fgithub.com\u002Fpydantic\u002Fpydantic-ai\u002Fpull\u002F3438), [#4604](https:\u002F\u002Fgithub.com\u002Fpydantic\u002Fpydantic-ai\u002Fpull\u002F4604) |\n",{"name":5,"description":7,"allowed-tools":66,"user-invocable":67},"Bash(git:*), Bash(gh:*), Bash(rg:*), Bash(ls:*), Bash(uv:*), Read, Write, Edit, Glob, Grep, WebFetch, AskUserQuestion, Agent",true,"main","133a75127f7b938567b8af7da1d59b95fe5a92fd",".agents\u002Fskills\u002Fadding-a-provider-api-feature",3165,"https:\u002F\u002Fgithub.com\u002Fpydantic\u002Fpydantic-ai","https:\u002F\u002Fpydantic.dev\u002Fpydantic-ai","https:\u002F\u002Fgithub.com\u002Fpydantic","https:\u002F\u002Fpydantic.dev","Pydantic, Pydantic Logfire and Pydantic AI","2026-11-14 03:24:23",{"source":8,"skillName":5,"commands":79,"prompt":89,"zipAvailable":67},[80,83,86],{"tool":81,"command":82},"npx","npx skills add pydantic\u002Fpydantic-ai --skill adding-a-provider-api-feature",{"tool":84,"command":85},"bunx","bunx skills add pydantic\u002Fpydantic-ai --skill adding-a-provider-api-feature",{"tool":87,"command":88},"pnpm","pnpm dlx skills add pydantic\u002Fpydantic-ai --skill adding-a-provider-api-feature","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fpydantic\u002Fpydantic-ai\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002Fadding-a-provider-api-feature\u002FSKILL.md",[],[92,119,129,149,168,184],{"skillNo":93,"name":94,"nameZh":6,"description":95,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":96,"categories":97,"keywords":98,"publishedAt":61},"01M6BYX16NP31D2ACFSNDNNJEV","migrating-agno-to-pydantic-ai","Migrate Python Agno applications to Pydantic AI and, only when needed, Pydantic AI Harness. Use when source code imports `agno` or relies on Agno agents, teams, workflows, sessions, memory, knowledge, tools, hooks, guardrails, approvals, skills, streaming, or AgentOS.","https:\u002F\u002Fgithub.com\u002Fpydantic\u002Fpydantic-ai\u002Fblob\u002Fmain\u002Fpydantic_ai_slim\u002Fpydantic_ai\u002F.agents\u002Fskills\u002Fmigrating-agno-to-pydantic-ai\u002FSKILL.md",[],[56,12,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,40,118],"migrating agno to pydantic ai","agno","harness","memory","applications","Guardrails","python","sessions","teams","knowledge","workflows","migrate","Tools","code","streaming","hooks","only","ai harness","use","pydantic ai harness",{"skillNo":120,"name":121,"nameZh":6,"description":122,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":123,"categories":124,"keywords":125,"publishedAt":61},"01M6BYY08B7335R8J3EKFM2GX7","reviewer","Review changes.","https:\u002F\u002Fgithub.com\u002Fpydantic\u002Fpydantic-ai\u002Fblob\u002Fmain\u002Ftests\u002Fharness\u002Fskills\u002Ftemporal_workspace\u002Fskills\u002Freviewer\u002FSKILL.md",[],[121,126,127,128],"review changes","review","changes",{"skillNo":130,"name":131,"nameZh":6,"description":132,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":133,"categories":134,"keywords":139,"publishedAt":61},"01M6BYXW6FV6Z0BB3ARTAJ0VKM","pydantic-ai-harness","Extend Pydantic AI agents with capabilities from pydantic-ai-harness -- the Coder coding agent, file and shell tools in local or sandboxed workspaces (Modal, E2B, Render, Sprites, Daytona, boxd, smolvm, BoxLite), Code Mode, sub-agents and planning, memory and skills, context compaction, guardrails and spend limits, web research and browsers, hosted SaaS integrations, step persistence, and background agents on Render Workflows. Use when the user mentions pydantic-ai-harness or pydantic_ai_harness, imports a harness capability such as Coder, CodeMode, FileSystem, Shell, SubAgents, Memory, or ToolGuardrail, or wants a Pydantic AI agent that edits files, runs commands or agent-written Python, delegates, remembers, manages long context, stays within limits, or runs model and tool operations as chained Render task runs.","https:\u002F\u002Fgithub.com\u002Fpydantic\u002Fpydantic-ai\u002Fblob\u002Fmain\u002Fsrc\u002Fpydantic_ai_harness\u002Fpydantic_ai_harness\u002F.agents\u002Fskills\u002Fpydantic-ai-harness\u002FSKILL.md",[135,138],{"categoryNo":136,"categoryName":137,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000021","资料检索与研究",{"categoryNo":24,"categoryName":25,"taxonomyType":21,"taxonomyTypeDesc":22},[101,56,118,12,116,51,102,104,140,109,112,141,142,143,144,145,146,147,148,40],"ai agent","e2b","workspaces","render","local","files","smolvm","render workflows","mode",{"skillNo":150,"name":151,"nameZh":6,"description":152,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":153,"categories":154,"keywords":155,"publishedAt":61},"01M6BYXS3HEQYWXFJXYACRCA4H","migrating-deep-agents-to-pydantic-ai-harness","Migrate Python LangChain Deep Agents applications to Pydantic AI and Pydantic AI Harness. Use when the source uses the upstream `deepagents` package or demonstrably reproduces its middleware, backends, skills, memory, subagents, or sandbox contracts. Use `migrating-langchain-to-pydantic-ai` for plain LangChain, LangGraph, or LCEL migrations and `pydantic-ai-harness` for greenfield Harness usage.","https:\u002F\u002Fgithub.com\u002Fpydantic\u002Fpydantic-ai\u002Fblob\u002Fmain\u002Fsrc\u002Fpydantic_ai_harness\u002Fpydantic_ai_harness\u002F.agents\u002Fskills\u002Fmigrating-deep-agents-to-pydantic-ai-harness\u002FSKILL.md",[],[101,56,12,156,157,116,158,118,102,159,103,105,160,161,162,110,163,164,165,166,167,117,40],"migrating deep agents to pydantic ai harness","deep","deep agents","langgraph","sandbox","package","langchain","usage","for","deepagents","middleware","upstream",{"skillNo":169,"name":170,"nameZh":6,"description":171,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":172,"categories":173,"keywords":174,"publishedAt":61},"01M6BYXP4FFSW83DR07RZ789DR","migrating-vercel-ai-sdk-and-eve-to-pydantic-ai","Migrate TypeScript Vercel AI SDK or Eve applications to Python with Pydantic AI and, only when needed, Pydantic AI Harness. Use when source code imports `ai`, `@ai-sdk\u002F*`, or `eve`, or relies on `generateText`, `streamText`, `ToolLoopAgent`, `useChat`, Eve agents, tools, skills, subagents, approvals, sessions, channels, schedules, or sandboxes.","https:\u002F\u002Fgithub.com\u002Fpydantic\u002Fpydantic-ai\u002Fblob\u002Fmain\u002Fpydantic_ai_slim\u002Fpydantic_ai\u002F.agents\u002Fskills\u002Fmigrating-vercel-ai-sdk-and-eve-to-pydantic-ai\u002FSKILL.md",[],[56,12,175,176,177,178,179,180,101,103,105,181,106,110,111,112,182,115,116,117,183,40,118],"sdk","ai sdk","eve","migrating vercel ai sdk and eve to pydantic ai","vercel","vercel ai sdk","typescript","sandboxes","schedules",{"skillNo":185,"name":186,"nameZh":6,"description":187,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":188,"categories":189,"keywords":190,"publishedAt":61},"01M6BYXJXKEK0384TZNH0R1ZVY","migrating-pi-to-pydantic-ai","Migrate TypeScript Pi coding-agent applications, extensions, or packages to Python with Pydantic AI and Pydantic AI Harness. Use when source code imports `@earendil-works\u002Fpi-*`, calls `createAgentSession`, registers Pi extensions, or relies on Pi tools, hooks, skills, sessions, compaction, providers, TUI, RPC, or packages.","https:\u002F\u002Fgithub.com\u002Fpydantic\u002Fpydantic-ai\u002Fblob\u002Fmain\u002Fpydantic_ai_slim\u002Fpydantic_ai\u002F.agents\u002Fskills\u002Fmigrating-pi-to-pydantic-ai\u002FSKILL.md",[],[56,12,191,192,101,193,103,105,181,106,194,110,111,112,195,196,114,40,197,118,198,199,200,201,202],"migrating pi to pydantic ai","pi","packages","rpc","coding","Providers","pi extensions","imports","source","Coding-Agent","source code","compaction"]