-
-
Notifications
You must be signed in to change notification settings - Fork 0
Adapters
The internal/adapter package manages provider-specific differences in wire format, authentication, response transformation, and error classification through a clean Go interface. Nenya includes adapters for 24 built-in providers (OpenAI, Anthropic, Gemini, DeepSeek, Ollama, Groq, Together, SambaNova, Cerebras, NVIDIA, GitHub, Qwen, MiniMax, Moonshot, Mistral, xAI, OpenRouter, Azure, Perplexity, Cohere, DeepInfra, z.ai, and OpenCode Zen). For a user-facing provider guide with capabilities and special behaviors, see Providers.
type ProviderAdapter interface {
MutateRequest(body []byte, model string, stream bool) ([]byte, error)
InjectAuth(req *http.Request, apiKey string) error
MutateResponse(body []byte) ([]byte, error)
NormalizeError(statusCode int, body []byte) ErrorClass
}| Method | Purpose |
|---|---|
MutateRequest |
Transform OpenAI-format request body into provider-specific format. Strip unsupported params based on capabilities. |
InjectAuth |
Set authentication headers (Bearer, x-goog-api-key, none) on the upstream request. |
MutateResponse |
Normalize SSE response chunks (e.g., inject missing index fields in Gemini tool_calls). |
NormalizeError |
Classify HTTP errors into retryable categories for the circuit breaker. |
type ErrorClass int
const (
ErrorPermanent // Do not retry
ErrorRetryable // Retry with exponential backoff
ErrorRateLimited // Retry after delay (parse Retry-After header)
ErrorQuotaExhausted // Long cooldown (up to 30 minutes)
)Each adapter declares what the upstream provider supports at the wire-format level. Parameters not supported are automatically stripped from the request. These are provider-level capabilities, distinct from model-level capabilities (vision, reasoning, etc.) which are inferred dynamically via discovery.InferCapabilities().
type Capabilities struct {
StreamOptions bool // Provider supports stream_options.include_usage
AutoToolChoice bool // Provider supports tool_choice: "auto"
ContentArrays bool // Provider supports content as array of objects (vision)
}| Style | Header(s) | Used By |
|---|---|---|
bearer |
Authorization: Bearer <key> |
OpenAI, DeepSeek, Groq, Together, SambaNova, Cerebras, GitHub, z.ai, Mistral, xAI, Perplexity, Cohere, DeepInfra, Moonshot, Qwen |
bearer+x-goog |
Authorization: Bearer <key> + x-goog-api-key: <key>
|
Gemini |
anthropic |
x-api-key: <key> + anthropic-version: 2023-06-01
|
Anthropic |
azure |
api-key: <key> |
Azure OpenAI |
none |
(no auth) | Ollama |
Auth style is resolved in priority order:
- Adapter-specific
InjectAuth()(for registered providers) -
ProviderConfig.AuthStylefrom JSON config (for dynamic providers) - Default
BearerAuthfallback
Multi-account key selection: When a provider is configured with multiple credentials via the
accountsfield, the gateway uses LRU-based account selection per model. The auth style from the resolved adapter is applied to each selected account's credential. See Configuration#Multi-Account Per-Provider Keys for configuration details.
Identity transform for request/response. Capability-based parameter stripping:
-
StreamOptions: false→ stripsstream_optionsfrom request -
AutoToolChoice: false→ stripstool_choice: "auto"from request -
ContentArrays: false→ flattenscontent: [{type:"text",text:"..."}]tocontent: "..."
Used by: openai, deepseek, groq, together, github, sambanova, cerebras, nvidia, nvidia_free, qwen_free, minimax_free, moonshot
-
Request: Model name aliasing (e.g.,
gemini-flash→gemini-3-flash), orphaned tool_call cleanup (strips tool_calls missingextra_content/thought_signature plus their paired tool messages), thought_signature cache injection -
Response: Injects missing
indexfield ontool_calls[], cachesextra_contentfor multi-turn -
Auth:
bearer+x-goog -
Error: Gemini-specific retryable patterns (
resource_exhausted,quota exceeded,the response was blocked,content has no parts)
- Request: Orphaned tool message removal, consecutive user message merging, user bridge insertion between consecutive assistant messages, system bridge prepending
-
Auth:
bearer - Error: Standard classification
-
Request: Strips unsupported
tool_choicefield from request body (Ollama does not support tool choice control) - Auth: Optional Bearer (only if API key is non-empty)
- Error: Conservative — only 429/5xx are retryable
Full bidirectional conversion between OpenAI and Anthropic native API formats.
-
Request: Converts OpenAI format to Anthropic format:
- Messages:
system→user/assistantpair,tool→userwithtool_resultcontent block - Tools: OpenAI
functiontools → Anthropic tool format (withinput_schema) -
tool_choice:"auto"→{"type":"auto"},"required"→{"type":"required"}, named tool →{"type":"tool","name":"..."} - Parameters mapped:
max_tokens,temperature,top_p,stop→stop_sequences,user→metadata.user_id
- Messages:
-
Response: Converts Anthropic format back to OpenAI:
- Content blocks:
text→delta.content,tool_use→delta.tool_calls -
stop_reason:end_turn→stop,tool_use→tool_calls,max_tokens→length - Usage:
input_tokens/output_tokens→ OpenAI usage format
- Content blocks:
-
Auth:
x-api-keyheader +anthropic-versionheader - Error: 529 treated as rate-limited (Anthropic overloaded)
OpenAI-compatible with capability-based stripping.
- Request: Stream options stripped, content arrays preserved, auto tool choice preserved
- Auth: Bearer
- Error: Standard classification
OpenAI-compatible with extended capabilities.
- Request: Stream options and auto tool choice preserved, content arrays preserved
- Auth: Bearer
- Error: Standard classification
OpenAI-compatible with custom headers for OpenRouter's referral program.
-
Request: Adds
HTTP-RefererandX-Titleheaders on every request. Stream options and auto tool choice preserved. -
Auth: Bearer +
HTTP-Referer: https://github.com/nenya-project/nenya+X-Title: Nenya - Error: Standard classification
OpenAI-compatible for Azure OpenAI Service endpoints.
- Request: Standard capability-based stripping
-
Auth:
api-keyheader (notAuthorization: Bearer) - Error: Standard classification
OpenAI-compatible. Perplexity does not support function calling.
- Request: Content arrays preserved, auto tool choice stripped
- Auth: Bearer
- Error: Standard classification
OpenAI-compatible. Cohere uses a different content format internally.
- Request: Content arrays flattened, auto tool choice preserved
- Auth: Bearer
- Error: Standard classification
OpenAI-compatible with standard capabilities.
- Request: Content arrays preserved, auto tool choice preserved
- Auth: Bearer
- Error: Standard classification
Many providers are OpenAI-compatible and use the default OpenAIAdapter rather than a dedicated adapter:
| Provider | Adapter | Registration | Caps | Notes |
|---|---|---|---|---|
| DeepSeek | OpenAIAdapter |
Explicit | S:✓, A:✓, C:✓ |
Stream options enabled |
| Moonshot | OpenAIAdapter |
Explicit | S:✓, A:✓, C:✓ |
Full capabilities |
| OpenCode Zen | OpenAIAdapter |
Explicit | S:✓, A:✓, C:✓ |
Full capabilities |
| Qwen Free | OpenAIAdapter |
Explicit | S:✓, A:✗, C:✓ |
Auto tool choice stripped |
| MiniMax Free | OpenAIAdapter |
Explicit | S:✓, A:✓, C:✓ |
Full capabilities |
| NVIDIA | OpenAIAdapter |
Explicit | S:✗, A:✗, C:✓ |
Restricted capabilities |
| NVIDIA Free | OpenAIAdapter |
Explicit | S:✗, A:✗, C:✓ |
Same as NVIDIA |
| Qwen | OpenAIAdapter |
Default fallback | All disabled | Not in init(); user-config only |
| MiniMax | OpenAIAdapter |
Default fallback | All disabled | Not in init(); user-config only |
S = StreamOptions, A = AutoToolChoice, C = ContentArrays. ✓ = allowed, ✗ = stripped.
These providers use the OpenAIAdapter with provider-specific capability settings registered in internal/adapter/registry.go. Adding a new OpenAI-compatible provider typically requires zero Go code — only a JSON config entry and a secrets key.
Providers in the adapter registry fall into three categories:
| Category | Providers | Behavior |
|---|---|---|
| Custom adapter |
gemini, zai, anthropic, openrouter, mistral, xai, azure, perplexity, cohere, deepinfra, ollama
|
Dedicated adapter implementation registered via Register()
|
| OpenAIAdapter (explicit caps) |
openai, deepseek, groq, together, github, sambanova, cerebras, nvidia, nvidia_free, qwen_free, minimax_free, zen, moonshot
|
Registered via registerOpenAI() with provider-specific capability settings |
| OpenAIAdapter (default fallback) |
qwen, minimax, and any user-configured provider not in the registry |
Uses OpenAIAdapter{Caps: Capabilities{}} — all capabilities disabled, meaning stream_options, tool_choice: "auto", and content arrays are all stripped |
Runtime override:
zai-coding-planis initially registered as an OpenAIAdapter ininit()but is replaced by theZAIAdapterduring startup whenInitWithDeps()is called. This ensureszai-coding-planshares the same message sanitization logic aszai(orphaned tool removal, message merging, bridge insertion) and Zhipu-specific error classification.
Most providers (Groq, Together, Fireworks, Perplexity, etc.) use the OpenAI wire format. Add them via JSON config only:
{
"providers": {
"fireworks": {
"url": "https://api.fireworks.ai/inference/v1/chat/completions",
"auth_style": "bearer"
}
}
}The adapter registry falls back to OpenAIAdapter with conservative capabilities for unknown providers.
For providers with fundamentally different wire formats (Bedrock, Vertex, Anthropic, Cohere v1), create a new adapter file in internal/adapter/:
- Define a struct implementing
ProviderAdapter - Register it in
registry.goviaRegister() - Handle auth, request/response mutation, and error classification
See gemini.go or zai.go for examples.
The adapter registry is thread-safe with sync.RWMutex:
adapter.Register("my-provider", adapter.AdapterEntry{
Adapter: NewMyAdapter(),
})
a := adapter.ForProvider("my-provider")Unknown providers get the default OpenAIAdapter. Providers defined in JSON config (but not registered) get their adapter resolved by AuthStyle via AdapterForAuthStyle().
Getting Started
- Home — Project overview
- Quick Start — Install and run in 5 minutes
- Client Setup — OpenCode, Cursor, and other clients
- Deployment — Bare metal, container, Kubernetes
Core Concepts
- Configuration — Config reference and examples
- Providers — 24 providers, capabilities, special behaviors
- Routing — Latency-aware routing and fallback chains
- Architecture — Package overview and request lifecycle
- MCP Integration — MCP server integration
Reference
- Passthrough Proxy — Raw provider endpoint proxying
- Secrets — Systemd credentials and container secrets
- Model Discovery — Dynamic model catalog fetching
- API Endpoints — Endpoint reference
- Adapters — Provider adapter system
- Billing — Billing-aware routing and quota tracking
- Caching — Exact-match and semantic caching
- Provider Capabilities — Service kinds matrix
- Unknown MaxContext — Unknown context window behavior
Operations
- Demo — Test all pipeline tiers
- Troubleshooting — Common issues and solutions
- FAQ — Frequently asked questions
- Security — Security policy and vulnerability reporting
Project
- Roadmap — Planned features
- Disclaimer — Legal disclaimer