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 search-only 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 search providers (overlay.go).
Deprecated models (Status == "deprecated") are dropped so they don't surface in pickers / catalogs. Runtime lookups via GetModelInfo go through LoadProviders directly and so still resolve — agents configured on a now-deprecated model keep running, they're just hidden from the dropdown.
Merge semantics, in order:
- Start from LoadProviders().
- Synthesize stubs for search-only providers absent from the catalog (e.g. brave). Their search capability is derived from SearchBackend.
- 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 ¶
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 ¶
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 unions per-model capabilities across every model in the provider, 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, so it's the one capability sourced outside the models.
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 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.