Provider Specification

February 28, 2026 · View on GitHub

Providers translate between the platform's unified message format and vendor-specific LLM APIs.

Source of Truth

The authoritative definitions live in code:

WhatWhereKey Classes
Provider Protocolamplifier_core/interfaces.pyProvider
Request/Responseamplifier_core/message_models.pyChatRequest, ChatResponse, Message
Content Blocks (envelope)amplifier_core/message_models.pyTextBlock, ThinkingBlock, ToolCallBlock, etc.
Content Blocks (events)amplifier_core/content_models.pyContentBlock, TextContent, ThinkingContent, ToolCallContent
Tool Callsamplifier_core/message_models.pyToolCall (used in ChatResponse.tool_calls and parse_tool_calls())
Metadata Modelsamplifier_core/models.pyProviderInfo, ModelInfo, ConfigField

Note: message_models.py provides Pydantic models for request/response envelopes. content_models.py provides dataclass types for event emission and streaming UI.

Read the code docstrings. This spec covers implementation guidance that code cannot express.

Protocol Summary

class Provider(Protocol):
    @property
    def name(self) -> str: ...
    def get_info(self) -> ProviderInfo: ...
    async def list_models(self) -> list[ModelInfo]: ...
    async def complete(self, request: ChatRequest, **kwargs) -> ChatResponse: ...
    def parse_tool_calls(self, response: ChatResponse) -> list[ToolCall]: ...

Module Entry Point

Providers are loaded via Python entry points. See MOUNT_PLAN_SPECIFICATION.md for how modules are configured.

Required: mount() Function

async def mount(coordinator: ModuleCoordinator, config: dict) -> Provider | None:
    """
    Initialize and return provider instance.

    Returns None for graceful degradation (e.g., missing API key).
    """
    api_key = config.get("api_key") or os.environ.get("MY_API_KEY")
    if not api_key:
        logger.warning("No API key - provider not mounted")
        return None

    provider = MyProvider(api_key=api_key, config=config, coordinator=coordinator)
    await coordinator.mount("providers", provider, name="my-provider")

    # Optional: Register cleanup
    async def cleanup():
        await provider.client.close()
    return cleanup

Required: Entry Point Registration

# pyproject.toml
[project.entry-points."amplifier.modules"]
my-provider = "my_provider:mount"

Implementation Requirements

Content Preservation (Critical)

All content block types must round-trip without loss. Key gotchas:

BlockPreservation Requirement
ThinkingBlockPreserve signature field (required for multi-turn)
ReasoningBlockPreserve content and summary arrays
ToolCallBlockPreserve id for result correlation

Role Conversion

Platform Role    → Common Vendor Mapping
─────────────────────────────────────────
system           → system / instructions parameter
developer        → user (XML-wrapped for context separation)
user             → user
assistant        → assistant
tool             → user (with tool_result blocks)

Tool Sequence Validation

Validate that all ToolCallBlock entries have corresponding ToolResultBlock with matching tool_call_id. If missing:

  1. Log warning (indicates context management bug)
  2. Either synthesize placeholder result OR let API error

Auto-Continuation

Some APIs return truncated responses. Handle transparently:

while response.status == "incomplete" and iterations < MAX:
    response = await self._continue(accumulated_output)
    accumulated_output.extend(response.output)

Configuration

Via Mount Plan

providers:
  - module: my-provider
    source: git+https://github.com/org/my-provider@main
    config:
      api_key: "${MY_API_KEY}"
      default_model: model-v1
      debug: true

ConfigField for Interactive Setup

Providers declare configuration needs via get_info().config_fields:

ConfigField(
    id="api_key",
    field_type="secret",
    env_var="MY_API_KEY",
    prompt="Enter API key",
)

Conditional fields: Use show_when and requires_model for model-dependent configuration.

Observability

Event Emission

Orchestrators emit standard provider:request/response/error events. Providers may emit additional events via contribution channels (see CONTRIBUTION_CHANNELS.md):

coordinator.register_contributor(
    "observability.events",
    "my-provider",
    lambda: ["my-provider:rate_limit", "my-provider:retry"]
)

Debug Levels

Support via config flags:

FlagEventsContent
(default)llm:request, llm:responseSummary only
debug: truellm:request:debug, llm:response:debugTruncated payloads
debug: true, raw_debug: truellm:request:raw, llm:response:rawComplete API I/O

Quick Reference Checklist

Required

  • Implement Provider protocol (5 methods)
  • mount() function with entry point in pyproject.toml
  • Preserve all content block types
  • Report Usage (input/output/total tokens)
  • Graceful degradation on missing config (return None from mount)
  • Validate tool call/result sequences
  • Support debug configuration flags
  • Register cleanup function

Content Block Reference

TypeKey FieldsNotes
texttextStandard text content
thinkingthinking, signatureVendor thinking (signature critical)
redacted_thinkingdataRedacted by vendor policy
reasoningcontent, summaryReasoning chain (o-series style)
tool_callid, name, inputCorrelate with results via id
tool_resulttool_call_id, outputMust match tool_call.id
imagesourceVendor-specific source format

All blocks support visibility field and extra="allow" for vendor extensions.

Capabilities Taxonomy

Model capabilities are declared in ModelInfo.capabilities as a list of strings. To ensure consistency across providers, amplifier_core.capabilities defines well-known capability constants.

Tier 1: Universal Capabilities

Core capabilities that most LLM providers can report on:

ConstantString ValueDescription
TOOLS"tools"Function/tool calling support
STREAMING"streaming"Streaming response support
THINKING"thinking"Extended reasoning / chain-of-thought (see naming note below)
VISION"vision"Image input understanding
JSON_MODE"json_mode"Structured JSON output mode

Tier 2: Specialized Capabilities

Extended capabilities for specialized model features:

ConstantString ValueDescription
FAST"fast"Optimized for low latency
CODE_EXECUTION"code_execution"Server-side code execution
WEB_SEARCH"web_search"Built-in web search
DEEP_RESEARCH"deep_research"Extended multi-step research
LOCAL"local"Runs locally (e.g., Ollama)
AUDIO"audio"Audio input/output support
IMAGE_GENERATION"image_generation"Image generation support
COMPUTER_USE"computer_use"Desktop/browser automation
EMBEDDINGS"embeddings"Text embedding generation
LONG_CONTEXT"long_context"Extended context window support
BATCH"batch"Batch processing API support

Naming Decision: thinking is Canonical

The canonical capability string for extended reasoning is "thinking", not "reasoning". This applies regardless of vendor terminology:

  • Anthropic's "extended thinking" → "thinking"
  • OpenAI's "reasoning" (o-series) → should be mapped to "thinking"

Providers SHOULD map vendor-specific terminology to "thinking" when populating ModelInfo.capabilities. The MODEL_CLASS_CAPABILITIES mapping in amplifier_core.capabilities handles normalization of provider-reported model classes.

Extensibility

Providers MAY include capability strings not in the well-known set. The taxonomy is intentionally open — ALL_WELL_KNOWN_CAPABILITIES is a frozenset for validation, not a closed enum. Custom capabilities should use descriptive lowercase strings with underscores (e.g., "my_custom_feature").

Cost Tiers

Cost tiers categorize models by relative pricing. Providers SHOULD set metadata["cost_tier"] on ModelInfo to one of:

ConstantString ValueDescription
COST_TIER_FREE"free"No-cost models (local, free-tier)
COST_TIER_LOW"low"Budget-friendly (e.g., Haiku-class)
COST_TIER_MEDIUM"medium"Standard pricing (e.g., Sonnet-class)
COST_TIER_HIGH"high"Premium pricing (e.g., Opus-class)
COST_TIER_EXTREME"extreme"Highest-cost (e.g., deep research)

All cost tier constants are available in amplifier_core.capabilities and collected in ALL_COST_TIERS (frozenset).