Agent Skillspydantic/pydantic-ai › adding-a-provider-api-feature

adding-a-provider-api-feature

GitHub

指导在Pydantic AI中新增Provider API功能(如缓存、结构化调用等),强调复用现有跨Provider抽象以保持一致性,避免重复造轮子。

.agents/skills/adding-a-provider-api-feature/SKILL.md pydantic/pydantic-ai

Trigger Scenarios

需要为Pydantic AI添加新的Provider API特性 设计API时需遵循跨Provider一致性规范

Install

npx skills add pydantic/pydantic-ai --skill adding-a-provider-api-feature -g -y
More Options

Non-standard path

npx skills add https://github.com/pydantic/pydantic-ai/tree/main/.agents/skills/adding-a-provider-api-feature -g -y

Use without installing

npx skills use pydantic/pydantic-ai@adding-a-provider-api-feature

指定 Agent (Claude Code)

npx skills add pydantic/pydantic-ai --skill adding-a-provider-api-feature -a claude-code -g -y

安装 repo 全部 skill

npx skills add pydantic/pydantic-ai --all -g -y

预览 repo 内 skill

npx skills add pydantic/pydantic-ai --list

SKILL.md

Frontmatter
{
    "name": "adding-a-provider-api-feature",
    "description": "Add a new provider API capability (prompt caching, strict\/structured tool calling, thinking\/reasoning 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.",
    "allowed-tools": "Bash(git:*), Bash(gh:*), Bash(rg:*), Bash(ls:*), Bash(uv:*), Read, Write, Edit, Glob, Grep, WebFetch, AskUserQuestion, Agent",
    "user-invocable": true
}

Adding a provider API feature

Use this when exposing a new provider API capability through Pydantic AI — prompt caching, strict/structured tool calling, thinking/reasoning 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.

Not for: adding a new model id (that's add-new-model), a bug fix, or a refactor.

The one rule that prevents the most rework

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.

The 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.

Step 0 — Enumerate sibling precedent

For 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:

  • a per-tool flagToolDefinition.strict: bool | None (tools.py), resolved in models/__init__.py::_customize_tool_def;
  • a shared ModelSettings fieldthinking, service_tier (settings.py), each with per-model resolvers mapping to native concepts;
  • a provider-prefixed {Provider}ModelSettings fieldanthropic_cache, openai_prompt_cache_key, groq_reasoning_effort;
  • a message-stream markerCachePoint in UserPromptPart.content (messages.py);
  • a ModelProfile capability flagopenai_supports_strict_tool_definition, bedrock_supports_prompt_caching.

Read the actual sibling implementations and any review threads on the PRs that added them (gh pr view <n> --comments). Contributors who skipped this got redirected: raw google_tool_config → use strict (#5366); string-prefix model detection → use a profile flag (#4604). Only open a design fork if no existing abstraction covers the capability.

Step 1 — Pick the API shape

  1. 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.
  2. 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). Don't delete provider knobs; deprecate only genuinely-misnamed ones with a TODO(v3).
  3. 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).
  4. 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).
  5. 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).
  6. Type it. Reuse the provider SDK's own types where they exist; type knobs as Literal, never extra_body or untyped **kwargs (models/AGENTS.md; "kwargs are a big no no… I'd rather be repetitive but type safe" — DouweM, #3457).

Step 2 — Default: default-on (silent) vs opt-in

Default 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.

Keep it opt-in when it:

  • changes observable output or wire behavior;
  • is a preview feature (provider may change semantics);
  • 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);
  • can hit provider limits at scale (auto-promoting strict silently broke agents with >20 tools; reverted in #5580);
  • applies lossy schema rewrites (OpenAI/Anthropic strict transforms).

When the default isn't obvious, decide it with a live probe, not an opinion (#5897 flipped a default on 0/5 → 5/5 recovery). Beware "automatic" language — verify whether it means a Pydantic AI default or just provider-side management of an opt-in feature.

Step 3 — Capability gating

Detect support with a provider-prefixed ModelProfile flag set in Provider.model_profile() — never inline isinstance/model-name checks (profiles/AGENTS.md). Gate at the layer(s) that actually vary:

  • model/family → profile flag (google_supports_strict_tool_definition, bedrock_supports_prompt_caching);
  • per-schema → the JsonSchemaTransformer.is_strict_compatible signal (default conservative unless the mode needs no rewrites);
  • SDK version → probe the SDK shape and degrade with a UserWarning (#5580, botocore strict param);
  • unknowable client-side → defer to the runtime API error.

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, #6231).

Step 4 — Tests

Live-recorded wire-contract cassette asserting the exact outbound body (mode/field is sent for supported models, absent/ignored 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.

Step 5 — Docs & skills

The new public symbol's docstring lists which providers support it and how each interprets the value. Add/refresh the docs/**.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.

ModelSettings' Supported by: lists are enforced: tests/models/test_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.

Recurring maintainer principles (quoted)

  1. 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)
  2. Promote to a shared setting once several providers have the concept; keep per-provider overrides underneath. (#4926)
  3. Best-effort — silently ignore unsupported settings, don't error. "we typically do a 'best effort' so that as many requests as possible succeed." (#3438)
  4. Hide provider complexity — the feature should be useful to people who don't want to become experts in that provider's limitations.
  5. Capability facts belong on ModelProfile flags, layered base + overlay. (#5934)
  6. Type safety over repetition; no untyped kwargs. (#3457)
  7. Put shared fields on the base settings class; cover every API surface. (#3678)
  8. 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)
  9. Name the eventual unification even when deferring it — a {provider}_* knob today can note the future Caching/Thinking-style capability it should fold into. (#4604)

Precedent map

Capability Reused abstraction Default Gating PR
service tier (cross-provider) promoted to ModelSettings.service_tier opt-in, never silent upgrade map-and-drop #4926
strict — OpenAI (origin) ToolDefinition.strict auto-promote per compatible schema per-schema is_strict_compatible #1304
strict — Anthropic reused strict conservative opt-in transformer + profile flag #3457
strict — Bedrock (+ fix) reused strict opt-in (auto-promote reverted) transformer + profile + SDK probe #4237, #5580
strict — Gemini VALIDATED reused strict; rejected raw google_tool_config default-on (mode needs no schema rewrite) profile flag; is_strict_compatible = True #6353
thinking (cross-provider) ModelSettings.thinking + per-provider maps opt-in, graceful degradation supports_thinking flags #4640
reasoning effort — Groq provider knob coexists with thinking, outranks it opt-in per-family profile flag #5797, #6231
prompt caching — Anthropic → Bedrock/OpenRouter CachePoint marker + settings opt-in {provider}_supports_prompt_caching #3363, #3438, #4604

Version History

  • 43f982f Current 2026-08-19 23:48

    修正ModelSettings的Supported by列表并用探针测试固定(#7443)

  • 20cdf45 2026-07-25 05:33

Same Skill Collection

.agents/skills/add-new-model/SKILL.md
.agents/skills/complete-partial-pr/SKILL.md
.agents/skills/i-have-adhd/SKILL.md
.agents/skills/pushing-commits-to-the-repo/SKILL.md
.claude/skills/address-feedback/SKILL.md
.claude/skills/pre-push-review/SKILL.md
.claude/skills/testing-skill/SKILL.md
pydantic_ai_slim/pydantic_ai/.agents/skills/building-pydantic-ai-agents/SKILL.md
.agents/skills/poweruser-feature-audit/SKILL.md

Metadata

Files
0
Version
43f982f
Hash
be35009b
Indexed
2026-07-25 05:33

Главная - Вики-сайт
Copyright © 2011-2026 iteam. Current version is 2.155.2. UTC+08:00, 2026-08-21 04:41
浙ICP备14020137号-1 $Гость$