Documentation
¶
Overview ¶
Package provider implements the multi-provider system for sol. This matches opencode's provider architecture for supporting multiple LLM providers. Provider information comes from the validated Airlock model catalog.
Index ¶
- Constants
- Variables
- func AllProviders() (map[string]*ModelsDevProvider, error)
- func CreateEmbeddingModel(providerID, modelID string, opts Options) model.EmbeddingModel
- func CreateImageModel(providerID, modelID string, opts Options) model.ImageModel
- func CreateModel(providerID, modelID string, opts Options) stream.Model
- func CreateProxyModel(fullModelID string, opts ProxyOptions) stream.Model
- func CreateSpeechModel(providerID, modelID string, opts Options) model.SpeechModel
- func CreateTranscriptionModel(providerID, modelID string, opts Options) model.TranscriptionModel
- func GetContextLimit(providerID, modelID string) int
- func GetDisplayName(providerID string) string
- func GetEnvVarName(providerID string) string
- func GetOutputLimit(providerID, modelID string) int
- func ListProviders() []string
- func LoadProviders() (map[string]*ModelsDevProvider, error)
- func MaxOutputTokens(modelID string) int
- func ParseModel(model string) (providerID, modelID string)
- func ProviderOptions(providerID, modelID, sessionID string) map[string]any
- func SearchBackend(providerID string) string
- func StartPeriodicRefresh(ctx context.Context)
- func SupportsInputModality(providerID, modelID, modality string) bool
- func ValidateCatalogJSON(data []byte) error
- type AttachmentPolicy
- type CapabilitySet
- type ModelCapabilities
- type ModelCost
- type ModelInfo
- type ModelKind
- type ModelLimit
- type ModelModalities
- type ModelsDevProvider
- type Options
- type ProxyOptions
Constants ¶
const ( CapText = "text" CapVision = "vision" CapImageGen = "image_gen" CapSearch = "search" CapEmbedding = "embedding" CapSpeech = "speech" CapTranscription = "transcription" CapReranking = "reranking" )
Capability constants. These are the strings exposed over the API and used as keys in the UI capability matrix. Keep them lowercase snake_case.
Two axes:
- Kind-derived (CapEmbedding/CapSpeech/CapTranscription/CapReranking, plus CapText/CapImageGen which double as kinds). True when the provider has at least one model of that kind.
- Modality-derived (CapVision). True when the provider has at least one language model that accepts the corresponding extra input modality.
const ( CatalogURL = "https://models.airlock.run/models.json" RefreshInterval = 12 * time.Hour )
const OpenRouterBaseURL = "https://openrouter.ai/api/v1"
OpenRouterBaseURL is OpenRouter's OpenAI-compatible API root. Used as the default when a configured provider leaves the base URL blank.
Variables ¶
var AttachmentOverlay = map[string]AttachmentPolicy{ "openai": { SupportsURL: true, SupportsFileURL: false, MaxURLImages: 50, MaxURLBytesPerImage: 18 * 1024 * 1024, MaxInlineBytesTotal: 40 * 1024 * 1024, }, "anthropic": { SupportsURL: true, SupportsFileURL: true, MaxURLImages: 50, MaxURLBytesPerImage: 4*1024*1024 + 512*1024, MaxInlineBytesTotal: 24 * 1024 * 1024, }, "google": { SupportsURL: true, SupportsFileURL: true, MaxURLImages: 100, MaxURLBytesPerImage: 20 * 1024 * 1024, MaxInlineBytesTotal: 80 * 1024 * 1024, }, "vertex": { SupportsURL: true, SupportsFileURL: true, MaxURLImages: 100, MaxURLBytesPerImage: 20 * 1024 * 1024, MaxInlineBytesTotal: 80 * 1024 * 1024, }, "bedrock": { SupportsURL: true, SupportsFileURL: true, MaxURLImages: 50, MaxURLBytesPerImage: 4*1024*1024 + 512*1024, MaxInlineBytesTotal: 24 * 1024 * 1024, }, "xai": { SupportsURL: true, SupportsFileURL: false, MaxURLImages: 50, MaxURLBytesPerImage: 18 * 1024 * 1024, MaxInlineBytesTotal: 40 * 1024 * 1024, }, "openaicompat": { SupportsURL: true, SupportsFileURL: false, MaxURLImages: 5, MaxURLBytesPerImage: 3*1024*1024 + 512*1024, MaxInlineBytesTotal: 5 * 1024 * 1024, }, }
AttachmentOverlay is the per-provider override map, keyed by provider_id. Values are applied as-is — not merged with DefaultAttachmentPolicy — so each entry must be complete.
Covered: every provider in goai whose message conversion accepts a message.FilePart carrying a URL data variant.
Sizing references (as of 2026-Q2 docs):
- OpenAI GPT-4o: 500 images/req, 50 MB total payload. Per-image soft cap ~20 MB (data-URI path). https://platform.openai.com/docs/guides/images-vision
- Anthropic Claude: 600 images/req, 32 MB request, HARD 5 MB per image (URL or inline — provider enforces both paths). https://platform.claude.com/docs/en/build-with-claude/vision
- Google Gemini: 3600 images/req, 100 MB URL-fetch per payload. https://ai.google.dev/gemini-api/docs/image-understanding
- xAI Grok: no documented image count cap, 20 MiB per image. https://docs.x.ai/docs/guides/image-understanding
Our caps sit *below* each provider's hard limit to leave headroom for JSON framing, tool schemas, prompt bytes, etc. `MaxInlineBytesTotal` is the base64-encoded byte budget (roughly 1.33× the raw byte count).
var DefaultAttachmentPolicy = AttachmentPolicy{ MaxInlineBytesTotal: 5 * 1024 * 1024, SupportsURL: false, }
DefaultAttachmentPolicy is the fallback — inline-only, 5 MiB request cap.
Functions ¶
func AllProviders ¶
func AllProviders() (map[string]*ModelsDevProvider, error)
AllProviders returns the Airlock model catalog enriched with reserved provider stubs. Callers that want the source catalog unmodified should call LoadProviders directly.
The catalog is remotely refreshed and has an embedded fallback. The only hand-maintained entries in Sol are reserved providers (overlay.go).
Deprecated models (Status == "deprecated") are dropped so they don't surface in pickers / catalogs. Runtime lookups for source-catalog providers use LoadProviders and still resolve, so configured deprecated models keep running while remaining hidden from the dropdown.
Merge semantics, in order:
- Start from LoadProviders().
- Overlay reserved providers with model-free stubs.
- Drop deprecated entries from every provider in the merged set.
The returned map's inner *ModelsDevProvider pointers are clones whenever we touched the provider, so callers can mutate top-level safely. Caches are unchanged.
func CreateEmbeddingModel ¶
func CreateEmbeddingModel(providerID, modelID string, opts Options) model.EmbeddingModel
CreateEmbeddingModel creates an embedding model from provider and model IDs.
func CreateImageModel ¶
func CreateImageModel(providerID, modelID string, opts Options) model.ImageModel
CreateImageModel creates an image generation model from provider and model IDs.
func CreateModel ¶
CreateModel creates a language model from provider and model IDs. This is the main entry point for getting a model instance.
func CreateProxyModel ¶
func CreateProxyModel(fullModelID string, opts ProxyOptions) stream.Model
CreateProxyModel creates a model that proxies LLM calls through an Airlock-compatible endpoint. The full model string (e.g., "anthropic/claude-sonnet-4-20250514") is passed to the proxy which resolves credentials and forwards to the real provider.
func CreateSpeechModel ¶
func CreateSpeechModel(providerID, modelID string, opts Options) model.SpeechModel
CreateSpeechModel creates a text-to-speech model from provider and model IDs.
func CreateTranscriptionModel ¶
func CreateTranscriptionModel(providerID, modelID string, opts Options) model.TranscriptionModel
CreateTranscriptionModel creates a speech-to-text model from provider and model IDs.
func GetContextLimit ¶
func GetDisplayName ¶
GetDisplayName returns the display name for a provider. Uses the active model catalog when available.
func GetEnvVarName ¶
GetEnvVarName returns the primary environment variable name for a provider's API key. Uses the active model catalog when available.
func GetOutputLimit ¶
func ListProviders ¶
func ListProviders() []string
func LoadProviders ¶
func LoadProviders() (map[string]*ModelsDevProvider, error)
LoadProviders returns the active validated catalog immediately. The embedded snapshot is always available; the first call starts a non-blocking refresh.
func MaxOutputTokens ¶
MaxOutputTokens returns the appropriate max output tokens for a model. This matches opencode's defaults for reasoning vs non-reasoning models.
func ParseModel ¶
ParseModel parses a "provider/model" string into provider and model IDs. This matches opencode's parseModel function. Examples:
- "openai/gpt-4o-mini" → ("openai", "gpt-4o-mini")
- "anthropic/claude-3-5-sonnet" → ("anthropic", "claude-3-5-sonnet")
- "gpt-4o-mini" → ("openai", "gpt-4o-mini") // defaults to openai
func ProviderOptions ¶
ProviderOptions returns the provider-specific options for a model. This matches opencode's ProviderTransform.options() function.
func SearchBackend ¶ added in v0.1.8
SearchBackend returns the sol/websearch backend client name for a provider, or "" if the provider doesn't offer web search.
func StartPeriodicRefresh ¶ added in v0.1.2
StartPeriodicRefresh starts one immediate refresh and one periodic ticker.
func SupportsInputModality ¶
func ValidateCatalogJSON ¶ added in v0.1.8
ValidateCatalogJSON validates a candidate for the embedded catalog snapshot.
Types ¶
type AttachmentPolicy ¶
type AttachmentPolicy struct {
// MaxInlineBytesTotal caps the total base64-encoded attachment bytes in
// a single request body. Parts that don't fit are evicted (oldest first)
// and replaced with a text placeholder that tells the LLM how to
// re-attach.
MaxInlineBytesTotal int
// SupportsURL is true when the provider accepts http(s):// URLs for
// image file parts (a FilePart with an image/* type and a URL data
// variant). Anthropic + Google also accept non-image files by URL (see
// SupportsFileURL).
SupportsURL bool
// SupportsFileURL is true when the provider accepts http(s):// URLs for
// non-image file parts (a FilePart with a URL data variant). Anthropic +
// Google support PDFs and other file types by URL; OpenAI currently does
// not on the chat path.
SupportsFileURL bool
// MaxURLImages caps how many image/file parts per request can be sent
// as URLs before we fall back to inline. Shared across both kinds to
// keep the provider from rejecting oversized fan-out fetches.
MaxURLImages int
// MaxURLBytesPerImage is the per-object size ceiling when sending URLs.
// Objects above this are fetched and inlined (or evicted) rather than
// referenced.
MaxURLBytesPerImage int
}
AttachmentPolicy describes how a given provider/model accepts image and file attachments. Used by airlock's attachref resolver to decide between URL mode (presigned S3 link) and inline mode (base64-encoded bytes), and to enforce per-request size caps.
Defaults are conservative: inline-only, 5 MiB per-request budget. Provider overrides (see AttachmentOverlay) relax these where the upstream API is known to support URL references.
func PolicyFor ¶
func PolicyFor(providerID, modelID string) AttachmentPolicy
PolicyFor returns the attachment policy for a given provider/model pair. modelID is accepted for future model-level overrides but currently unused.
type CapabilitySet ¶
type CapabilitySet struct {
Text bool
Vision bool
ImageGen bool
Search bool
Embedding bool
Speech bool
Transcription bool
Reranking bool
}
CapabilitySet is the set of high-level capabilities a model or provider offers. Derived from catalog modalities, model kind, and the overlay's extras.
func CapabilitiesFromModel ¶
func CapabilitiesFromModel(m ModelInfo) CapabilitySet
CapabilitiesFromModel derives the capability set for a single model from its kind plus its catalog modality list. Search is never set at the model level — it's a provider capability (see ProviderCapabilities).
Kind-derived capabilities trust the catalog. Modalities add orthogonal capabilities to language models, such as vision and image output.
func ProviderCapabilities ¶
func ProviderCapabilities(p *ModelsDevProvider) CapabilitySet
ProviderCapabilities returns declared capabilities for reserved providers. For catalog providers it unions capabilities across every model, then ORs in "search" when the provider has a web-search backend (SearchBackend). Search is a provider feature, not derivable from any single model's modalities.
A provider with no models is valid — e.g. brave, a search-only stub — in which case the result is just its search capability.
func (CapabilitySet) List ¶
func (c CapabilitySet) List() []string
List returns the set as a sorted slice of capability strings in the canonical UI order.
type ModelCapabilities ¶
type ModelCapabilities struct {
// IsReasoningModel indicates if the model is a reasoning model
IsReasoningModel bool
// SystemMessageMode determines how system messages should be handled.
// "system" - use standard system role
// "developer" - convert to developer role (for reasoning models)
// "remove" - remove system messages entirely
SystemMessageMode string
// DefaultReasoningEffort is the default reasoning effort level for this model.
// Empty string means no default (non-reasoning model or special case like gpt-5-pro).
DefaultReasoningEffort string
// DefaultMaxOutputTokens is the recommended max output tokens for this model.
// 0 means use the standard default.
DefaultMaxOutputTokens int
}
ModelCapabilities describes capabilities of a model. This matches opencode's getResponsesModelConfig() in openai-responses-language-model.ts
func GetModelCapabilities ¶
func GetModelCapabilities(modelID string) ModelCapabilities
GetModelCapabilities returns capabilities for a given model ID. This matches opencode's getResponsesModelConfig() function.
type ModelInfo ¶
type ModelInfo struct {
ID string `json:"id"`
Name string `json:"name"`
Kind ModelKind `json:"kind,omitempty"`
Family string `json:"family,omitempty"`
ReleaseDate string `json:"release_date,omitempty"`
Attachment bool `json:"attachment,omitempty"`
Reasoning bool `json:"reasoning,omitempty"`
Temperature bool `json:"temperature,omitempty"`
ToolCall bool `json:"tool_call,omitempty"`
Modalities *ModelModalities `json:"modalities,omitempty"`
Cost *ModelCost `json:"cost,omitempty"`
Limit *ModelLimit `json:"limit,omitempty"`
Status string `json:"status,omitempty"`
Experimental json.RawMessage `json:"experimental,omitempty"`
}
ModelInfo represents a model in the Airlock catalog.
func GetModelInfo ¶
type ModelKind ¶
type ModelKind string
ModelKind classifies a model's primary purpose as published by the catalog.
const ( KindLanguage ModelKind = "language" KindEmbedding ModelKind = "embedding" KindImage ModelKind = "image" KindAudio ModelKind = "audio" KindVideo ModelKind = "video" KindSpeech ModelKind = "speech" // text-to-speech KindTranscription ModelKind = "transcription" // speech-to-text KindReranking ModelKind = "reranking" )
type ModelLimit ¶
type ModelModalities ¶
func GetModalities ¶
func GetModalities(providerID, modelID string) *ModelModalities
func (*ModelModalities) SupportsInput ¶
func (m *ModelModalities) SupportsInput(modality string) bool
type ModelsDevProvider ¶
type ModelsDevProvider struct {
ID string `json:"id"`
Name string `json:"name"`
API string `json:"api,omitempty"`
NPM string `json:"npm,omitempty"`
Env []string `json:"env"`
Aliases []string `json:"aliases,omitempty"`
Models map[string]ModelInfo `json:"models"`
}
ModelsDevProvider represents one provider in the models.dev-compatible Airlock model catalog.
func GetProviderInfo ¶
func GetProviderInfo(providerID string) (*ModelsDevProvider, bool)
type Options ¶
type Options struct {
APIKey string
BaseURL string
// HTTPClient sends requests only for the explicit openai-compatible provider.
HTTPClient *http.Client
// SupportsStructuredOutputs controls native json_schema response formats for
// the explicit openai-compatible provider. Nil defaults to false for that
// provider.
SupportsStructuredOutputs *bool
// IncludeUsage controls stream_options.include_usage for the explicit
// openai-compatible provider. Nil defaults to false for that provider.
IncludeUsage *bool
}
Options holds configuration for creating a provider.
type ProxyOptions ¶
type ProxyOptions struct {
BaseURL string // Proxy server URL (e.g., "http://localhost:8080")
Token string // Bearer token for authentication
}
ProxyOptions holds configuration for creating a proxy-backed model.