Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Portable core, native edges

Portable does not mean every provider has the same API. It means you can express the common intent once and let the selected adapter handle the provider's wire format.

What the portable layer does

Provider adapters recognize the common conversation fields they support. They translate message roles and content, common generation controls, tool definitions and results, image blocks, streaming events, and unified reasoning controls.

The request remains a serde_json::Value, but it is not arbitrary pass-through JSON. Each adapter builds a new native request from understood fields. An unknown top-level field is not automatically forwarded to every provider.

Provider differences have three possible outcomes:

  • Translated: the provider supports the intent under another field or structure.
  • Clamped or omitted: the provider has a smaller vocabulary or cannot accept that control.
  • Sent natively: the caller deliberately uses a provider extension.

Reasoning illustrates the choice:

flowchart LR
    U[Unified effort + mode] --> P{Native override present?}
    P -- Yes --> N[x-provider native config]
    P -- No --> F[Model-family mapping]
    F --> C[Clamp to supported tier]
    C --> W[Provider-native reasoning control]

The exact reasoning mappings belong in the reasoning guide. The important concept is precedence: an explicit native control wins; otherwise llmshim maps the portable intent to what the selected model accepts.

Native extensions

Three built-in adapters expose explicit native namespaces:

NamespaceDestination
x-openaiFields in the OpenAI Responses request body
x-anthropicAnthropic Messages fields plus llmshim-specific header controls
x-geminiGemini request fields, including thinkingConfig handling

There is no x-xai namespace. xAI's supported common controls are translated directly to its Responses API shape.

In the Rust contract, an extension is a top-level request key:

{
  "x-openai": {
    "reasoning": { "effort": "high", "summary": "auto" }
  }
}

In the proxy contract, put that same key inside provider_config because the proxy merges provider_config into the core request:

{
  "provider_config": {
    "x-openai": {
      "reasoning": { "effort": "high", "summary": "auto" }
    }
  }
}

Using a native extension intentionally reduces portability. A request that depends on x-openai should not be expected to retain that behavior after its model is changed to Anthropic or Gemini.