Skip to content

Models and providers

The catalog is broader than the adapter set. A catalog entry is queryable metadata, not proof that this package can execute its protocol. Check SupportsProtocol or use GetRunnableModels before presenting a model as runnable. The current boundary is maintained in Protocol and provider status, together with provider IDs, model counts, and credential variables.

The catalog also contains metadata for additional compatible providers and models. Those entries can be queried and may work through one of the three protocol adapters, but they have not all been verified against live provider APIs and are not a support guarantee. Automated tests exercise all three adapters with local mock servers rather than live integration tests for every provider.

Only the key for the provider selected for the current request is resolved. Request-scoped credentials can also be supplied with StreamOptions.APIKey or StreamOptions.Env.

Discover models

Query the catalog instead of hard-coding model IDs supplied dynamically:

for _, provider := range llm.GetProviders() {
    fmt.Println(provider)
    for _, model := range llm.GetModels(provider) {
        fmt.Printf("  %s: %s\n", model.ID, model.Name)
    }
}

model, ok := llm.LookupModel("xiaomi", "mimo-v2.5")
if !ok {
    log.Fatal("model not found")
}

LookupModel returns a model and a found flag. GetModel is convenient for a known catalog entry and panics when the provider or model ID does not exist.

The catalog also contains models for protocols that do not have a built-in adapter yet. Use SupportsProtocol to test one protocol, or GetRunnableModels to list only models that the adapters imported by the current application can serve:

if !llm.SupportsProtocol(model.Protocol) {
    log.Fatalf("protocol %q is not registered", model.Protocol)
}

for _, model := range llm.GetRunnableModels("deepseek") {
    fmt.Println(model.ID)
}

Both functions inspect the package default adapter registry. Import the matching protocol package for side effects, or import llm/all, before using them. GetModels remains the unfiltered catalog query.

Model metadata

A Model is also a read-only metadata record. Inspect it to drive UI, enforce limits, or estimate cost before a request:

Field Type Meaning
ID string Identifier sent to the provider
Name string Human-readable display name
Provider string Vendor key, e.g. anthropic
Protocol Protocol Which adapter handles the model
BaseURL string Endpoint base URL
Headers map[string]string Default headers merged into each request
Reasoning bool Whether the model can produce thinking
Input []ModelInput Accepted modalities: Text, Image
ContextWindow int64 Maximum total tokens (input + output)
MaxTokens int64 Maximum tokens the model may generate
Cost ModelCost Per-million-token pricing
Compatibility ModelCompatibility Protocol-specific overrides (see below)

Reasoning reports only whether thinking is possible; use SupportedThinkingLevels to read the exact levels a model accepts rather than the raw ThinkingLevelMap.

Cost holds prices per million tokens, matching how CalculateCost computes a charge:

Field Meaning
Input Price per million input tokens
Output Price per million output tokens
CacheRead Price per million cache-read tokens
CacheWrite Price per million cache-write tokens
model, _ := llm.LookupModel("deepseek", "deepseek-v4-flash")
fmt.Printf("%s: %d-token window, $%.2f/M in, $%.2f/M out\n",
    model.Name, model.ContextWindow, model.Cost.Input, model.Cost.Output)

Catalog prices are static metadata bundled with this repository. They may lag provider price changes and do not include account-specific discounts, taxes, or provider-side rounding. Use the provider invoice as the billing authority.

See Responses and usage for the matching Usage and UsageCost records on a completed request.

Custom and compatible endpoints

Any endpoint implementing one of the built-in protocols can be used by constructing a Model directly and setting BaseURL. This covers local servers such as Ollama, vLLM, and LM Studio, as well as private model gateways:

model := llm.Model{
    ID:            "qwen2.5-coder:7b",
    Name:          "Qwen2.5 Coder 7B",
    Provider:      "ollama",
    Protocol:      llm.ProtocolOpenAICompletions,
    BaseURL:       "http://localhost:11434/v1",
    Input:         []llm.ModelInput{llm.ModelInputText},
    ContextWindow: 32768,
    MaxTokens:     4096,
}

events, err := llm.Stream(ctx, model, input, llm.StreamOptions{APIKey: "ollama"})

Endpoint-specific behavior—reasoning field names, cache-control support, and similar differences—is configured through Model.Compatibility with OpenAICompletionsCompatibility or AnthropicMessagesCompatibility. Set only the fields that differ from the default; each is a pointer so an unset field leaves the adapter's behavior unchanged.

supports := func(b bool) *bool { return &b }

// OpenAI-compatible endpoint that names its cap "max_completion_tokens"
// and accepts a reasoning effort field.
model.Compatibility = &llm.OpenAICompletionsCompatibility{
    MaxTokensField:          "max_completion_tokens",
    SupportsReasoningEffort: supports(true),
}

// Anthropic-compatible endpoint that does not support cache control.
model.Compatibility = &llm.AnthropicMessagesCompatibility{
    SupportsCacheControl: supports(false),
}

The compatibility records expose these fields:

Type Field Effect
OpenAICompletionsCompatibility MaxTokensField Changes the JSON field used for the output-token cap
OpenAICompletionsCompatibility SupportsReasoningEffort Enables or disables the OpenAI reasoning-effort field
OpenAICompletionsCompatibility SupportsDeveloperRole Controls whether developer-role messages may be sent
OpenAICompletionsCompatibility SupportsStore Controls whether the adapter sends store=false
OpenAICompletionsCompatibility SupportsStrictMode Controls strict mode on tool definitions
OpenAICompletionsCompatibility RequiresReasoningContentOnAssistantMessages Requires reasoning content when assistant turns are replayed
OpenAICompletionsCompatibility RequiresThinkingAsText Replays thinking as ordinary text instead of a reasoning field
OpenAICompletionsCompatibility ThinkingFormat Selects the endpoint-specific reasoning representation
OpenAICompletionsCompatibility ZAIToolStream Adds Z.AI's tool_stream field
AnthropicMessagesCompatibility SupportsTemperature Enables or disables temperature
AnthropicMessagesCompatibility SupportsCacheControl Enables or disables message cache control
AnthropicMessagesCompatibility SupportsCacheControlTools Enables or disables cache control on tools
AnthropicMessagesCompatibility ForceAdaptiveThinking Forces adaptive thinking
AnthropicMessagesCompatibility AllowEmptySignature Permits an empty replayed thinking signature

All Boolean compatibility fields are pointers. nil means adapter default; false is an explicit override. The accepted ThinkingFormat strings are defined by the current adapters; the package does not publish an open enum for arbitrary providers.

For a wire protocol that is neither OpenAI-compatible nor Anthropic-compatible, implement a custom protocol adapter.

Provider configuration and status

The package keeps a provider registry next to the model catalog. The catalog lists a provider's models; the registry holds its configuration: the environment variables that supply its key, and any override applied to its requests. The package-level Stream and Complete route through the default registry, so status queries and overrides take effect without building your own client.

Check whether a provider is configured

AuthStatus reports whether a key resolves and which source it came from, without sending a request.

registry := llm.DefaultProviderRegistry()

status, ok := registry.AuthStatus("deepseek", nil)
if ok && !status.Configured {
    fmt.Printf("%s not configured; set one of %v\n", status.Label, status.Missing)
}
// A configured provider reports its source, e.g. "env:DEEPSEEK_API_KEY".

Redirect a provider's requests

SetOverride sets a base URL, API key, or headers for every request to a provider, so a proxy or gateway needs no change to each Model.

proxy := "https://proxy.example.com/deepseek/v1"
registry.SetOverride("deepseek", llm.ProviderOverride{
    BaseURL: &proxy,
    Headers: map[string]string{"X-Team": "infra"},
})
// Every deepseek model now streams through the proxy.

SetOverride stores an independent snapshot, so the values, maps, and request-scoped environment passed to it may be reused or mutated after the call. Set overrides at startup when possible. Credential precedence is listed in request configuration.

Set DisableEnv: true when an application must accept credentials only from an explicit StreamOptions.APIKey or ProviderOverride.APIKey; in that mode, provider environment variables are not consulted by resolution or AuthStatus.

Register a custom provider

Register adds a provider the catalog does not ship. It resolves its key from its own environment variables and can be overridden like a built-in one, which is the alternative to passing a bare Model for a local server.

registry.Register(llm.NewSpecProvider(llm.ProviderSpec{
    ID:      "local",
    Name:    "Local LLM",
    EnvKeys: []string{"LOCAL_API_KEY"},
    Models: []llm.Model{{
        ID:       "qwen2.5-coder:7b",
        Provider: "local",
        Protocol: llm.ProtocolOpenAICompletions,
        BaseURL:  "http://localhost:11434/v1",
        Input:    []llm.ModelInput{llm.ModelInputText},
    }},
}))

NewSpecProvider builds a provider from an independent snapshot of the supplied data, including model configuration. Vendors that need per-request logic such as OAuth refresh are not covered by the spec type yet.

ProviderRegistry also exposes Get, Providers, Resolve, Models, Override, and ClearOverride. Applications that need isolated registries or explicit clients should use Clients and registries instead of mutating the package defaults.