Provider Reference
A provider is a ModelProvider. TramAI never guesses which one to use: routing is always explicit, either through @Operation(provider = ...), through a model-to-provider mapping, or through a configured default provider.
All provider adapters in 0.6.0 are at preview maturity — see API Stability. The behavior below is the shipped behavior, not a stability promise.
Published Adapters
| Module | Class | Provider id | Transport | Tools | Vision | Structured | Streaming | Spring properties |
|---|---|---|---|---|---|---|---|---|
tramai-openai | OpenAiProvider | openai | HTTP | ✓ | ✓ | ✓ | ✓ | tramai.providers.openai.* |
tramai-openai | OpenAiCompatibleProvider | providerName (default openai-compatible) | HTTP | ✓ | ✓ | ✓ | ✓ | tramai.providers.openai-compatible.* |
tramai-anthropic | AnthropicProvider | anthropic | HTTP | ✓ | ✓ | ✓ | ✓ | tramai.providers.anthropic.* |
tramai-ollama | OllamaProvider | ollama | HTTP | ✗ | ✓ | ✗ | ✓ | tramai.providers.ollama.* |
tramai-gemini | GeminiProvider | gemini | HTTP | ✓ | ✓ | ✓ | ✓ | — |
tramai-azure-openai | AzureOpenAiProvider | azure-openai | HTTP | ✓ | ✓ | ✓ | ✓ | — |
tramai-bedrock | BedrockProvider | bedrock | AWS SDK | ✓ | ✓ | ✓ | ✓ | — |
tramai-deepseek | DeepSeekProvider | deepseek | HTTP | ✓ | ✓ | ✓ | ✓ | — |
The capability columns are the sets pinned by the shared provider TCK, not marketing claims — a provider cannot widen its advertised capabilities without a reviewer changing the TCK harness. Ollama pins vision and streaming only: tool calling and structured output are not claimed for it.
Minimal Snippets
OpenAI
val tramai = Tramai {
provider(OpenAiProvider(System.getenv("OPENAI_API_KEY")), name = "openai")
model("gpt-4o", "openai")
}
Auth modes on OpenAiProvider: a static API key, an explicit bearer token via OpenAiProvider.bearerToken(...), or the experimental Codex auth file via OpenAiProvider.codexAuth(...). Under Spring, credentials can come from a secret reference instead of a literal value.
OpenAI-compatible
Use this for any backend that exposes an OpenAI-shaped /chat/completions endpoint.
val tramai = Tramai {
provider(
OpenAiCompatibleProvider.bearerToken(
bearerToken = System.getenv("COMPATIBLE_API_TOKEN"),
baseUrl = "https://compatible.example.com/v1",
providerName = "compatible",
),
name = "compatible",
)
model("my-compatible-model", "compatible")
}
Anthropic
val tramai = Tramai {
provider(AnthropicProvider(System.getenv("ANTHROPIC_API_KEY")), name = "anthropic")
model("claude-sonnet-4-20250514", "anthropic")
}
Ollama
val tramai = Tramai {
provider(OllamaProvider("http://localhost:11434"), name = "ollama")
model("llama3.2", "ollama")
}
Ollama is the local-first option and the usual target for the sovereign runtime, where the trust zone for a local model provider is LOCAL.
Gemini
val tramai = Tramai {
provider(GeminiProvider(System.getenv("GEMINI_API_KEY")), name = "gemini")
model("gemini-2.0-flash", "gemini")
}
Azure OpenAI
val tramai = Tramai {
provider(
AzureOpenAiProvider(
resourceName = System.getenv("AZURE_OPENAI_RESOURCE"),
deploymentId = System.getenv("AZURE_OPENAI_DEPLOYMENT"),
apiKey = System.getenv("AZURE_OPENAI_API_KEY"),
),
name = "azure-openai",
)
model("gpt-4o", "azure-openai")
}
Either an API key or an Entra ID access-token source must be supplied; startup checks that.
Bedrock
val tramai = Tramai {
provider(BedrockProvider(region = "eu-west-1"), name = "bedrock")
model("anthropic.claude-3-sonnet-20240229-v1:0", "bedrock")
}
The default model id is anthropic.claude-3-sonnet-20240229-v1:0; pass modelId = ... to override it.
Bedrock uses the AWS SDK credential chain. The SDK transport is driven through its client-factory seam in the TCK: HTTP wire-level behavior such as status mapping and Retry-After does not apply, and AWS SDK types never enter TramAI's stable public API.
DeepSeek
val tramai = Tramai {
provider(DeepSeekProvider(System.getenv("DEEPSEEK_API_KEY")), name = "deepseek")
model("deepseek-chat", "deepseek")
}
Provider Registration In Spring
tramai-spring-provider-openai, -anthropic, and -ollama register providers from properties. Gemini, Azure OpenAI, Bedrock, and DeepSeek are standalone adapters: in Spring, register them as ModelProvider beans — each bean provider is registered under its own providerId(), and tramai.models references that name.
Provider beans and property-defined providers are merged into one registry; when a name collides with a unique bean provider, the bean wins.
Registry-Based Resolution
The engine resolves every operation against an immutable ProviderRoutingPlan, exposed through dev.tramai.core.provider.ProviderRegistry.
Resolution order:
- explicit
@Operation(provider = "...")— bypasses model resolution - the model-to-provider route, plus any configured fallback routes in order
- the default provider
Standalone code builds the registry through the standalone builder. If you need the registry directly:
val registry = ProviderRegistry.builder()
.provider("openai", OpenAiProvider(System.getenv("OPENAI_API_KEY")), default = true)
.model("gpt-4o", "openai")
.fallbackModel("gpt-4o", "gpt-4o-mini", "openai")
.build()
ProviderRegistry.singleProvider(provider) is the one-provider shorthand, and ProviderRegistry.from(plan) wraps an existing plan.
Routing validation is fail-closed at build time:
- every provider and model name must be non-blank and free of surrounding whitespace
- a duplicate provider name or a second primary route for the same model fails
- a model with fallback routes must have a primary route
- every primary and fallback route must target a registered provider
- a fallback route may not duplicate the primary route
There is no model-prefix inference. gpt-*, claude-*, and llama-* mean nothing to the router; you map names explicitly. That is what makes aliases and compatible endpoints predictable.
Trust Zones And Classification Routing
dev.tramai.security.ProviderTrustZone classifies where a provider runs:
| Zone | Meaning |
|---|---|
LOCAL | Same host or isolated network boundary (Ollama, vLLM, llama.cpp) |
EU_CLOUD | Cloud providers inside the EU trust boundary |
GLOBAL_CLOUD | Cloud providers outside EU jurisdiction |
ProviderRoutingConfiguration(providerZones, rules, enabled) maps provider ids to zones and each data classification to which zones may serve it, for primary invocation and for fallback. ClassificationRoutingRule(allowedZones, allowedFallbackZones) must keep the fallback zones a subset of the primary zones — an empty fallback set denies all fallback for that classification, which is the default for restricted data.
The sovereign runtime wires this through its own allowlist block; see Configuration.
Experimental Surface
Two things in the provider layer are explicitly outside any stability promise:
- the Codex/ChatGPT auth-file path —
@ExperimentalCodexAuth,OpenAiProvider.codexAuth(...),OpenAiCompatibleProvider.codexAuth(...), and the matching Spring properties. It reads a token from a local Codexauth.json; use it for local experiments, not production credentials. - transport helpers in
dev.tramai.core.provider.transport, marked@ExperimentalProviderTransportApi. They are public only because the built-in adapters consume them across module boundaries, and they may move or change in any release.
What Is Not Implemented Across The Provider Layer
- provider-native structured output modes — structured output goes through TramAI's schema-in-prompt pipeline for every provider
- externalized provider-level retry-policy configuration
- externalized provider-level timeout-policy configuration
- per-provider timeouts — the per-attempt timeout comes from
@Operation(timeoutMillis = ...)
