Documentation
¶
Overview ¶
Package modelcontract defines the canonical, provider-agnostic model schema and the adapter interface that normalizes each provider's native model API into it.
The goal is a single contract the rest of the application consumes with confidence: every model has the same shape with normalized capabilities, pricing, context window, and lifecycle, regardless of how the originating provider happens to report them. Provider quirks live ONLY inside adapters. Values that genuinely can't be determined are left explicitly unknown (nil pointers / empty slices), never silently approximated.
Index ¶
- Constants
- func Bool(b bool) *bool
- func CapabilityTags(c Capabilities) []string
- func ClassifyEligibleRoles(m CanonicalModel) []string
- func ContextWarning(contextWindow int) string
- func FillEligibleRoles(models []CanonicalModel)
- func IsKnownFalse(b *bool) bool
- func IsTrue(b *bool) bool
- type CanonicalModel
- type Capabilities
- type DeepInfraAdapter
- type ModelAdapter
- type OpenAIAdapter
- type OpenAICompatAdapter
- type OpenRouterAdapter
- type Pricing
- type ProbeResult
- type ProviderFile
- type ReferenceCatalog
Constants ¶
const ( RolePrimary = "primary" RoleSubagent = "subagent" )
Role names a model can be eligible/recommended for.
const ( StatusActive = "active" StatusDeprecated = "deprecated" StatusPreview = "preview" )
Lifecycle status values.
const ( // SubagentMinContext is the hard floor: below this, no eligible roles. SubagentMinContext = 64_000 // PrimaryMinContext is required for the primary role, and is also the // threshold at/above which no context warning is attached. PrimaryMinContext = 128_000 )
Agentic-coding context thresholds for sprout. A context window below SubagentMinContext is a hard block — sprout's agentic loops (tool results, file reads, repo context) need room to work, and below ~64K a model is not usable. Between SubagentMinContext and PrimaryMinContext a model is usable only in a pinch and carries a strong warning. Eligibility ≠ recommendation; the capability probe provides the authoritative agentic-capable signal.
const SchemaVersion = 2
SchemaVersion is the version of the canonical published file format. Bumped only for breaking changes; consumers should accept known versions.
Variables ¶
This section is empty.
Functions ¶
func CapabilityTags ¶
func CapabilityTags(c Capabilities) []string
CapabilityTags renders known-true capabilities as legacy tag strings, for consumers/wire formats that carry capabilities as a flat tag list.
func ClassifyEligibleRoles ¶
func ClassifyEligibleRoles(m CanonicalModel) []string
ClassifyEligibleRoles returns the agentic roles a model meets the minimum deterministic bar for. A model that is *known* to lack tool calling is never eligible (tool use is mandatory for agentic coding); unknown tool support gets the benefit of the doubt at this pre-filter stage — the probe decides. Below SubagentMinContext (or unknown context) returns nil — a hard block.
func ContextWarning ¶
ContextWarning returns a strong, non-blocking warning when a model's context window is usable but below the recommended size for sprout (the SubagentMinContext–PrimaryMinContext band). It returns empty when the context is adequate (>= PrimaryMinContext) or when the model is hard-blocked (< SubagentMinContext, which yields no eligible roles instead of a warning).
func FillEligibleRoles ¶
func FillEligibleRoles(models []CanonicalModel)
FillEligibleRoles populates EligibleRoles and any derived Warnings for every model from its canonical fields. Always recomputed (deterministic) so it stays consistent with the current thresholds/capabilities.
func IsKnownFalse ¶
IsKnownFalse reports whether a tri-state capability is known-false (as opposed to unknown).
Types ¶
type CanonicalModel ¶
type CanonicalModel struct {
// Identity
ID string `json:"id"` // inference ID (provider-native)
Provider string `json:"provider"` // "deepinfra", "openrouter", …
DisplayName string `json:"display_name,omitempty"`
Description string `json:"description,omitempty"`
// Limits — 0 means unknown (documented convention).
ContextWindow int `json:"context_window,omitempty"`
MaxOutputTokens int `json:"max_output_tokens,omitempty"`
// Pricing — nil means unknown (NOT free).
Pricing *Pricing `json:"pricing,omitempty"`
// Capabilities — each is tri-state (see Capabilities).
Capabilities Capabilities `json:"capabilities"`
// Modality, e.g. ["text","image"].
InputModalities []string `json:"input_modalities,omitempty"`
OutputModalities []string `json:"output_modalities,omitempty"`
// Lifecycle
Status string `json:"status,omitempty"` // StatusActive / StatusDeprecated / StatusPreview
ReplacedBy string `json:"replaced_by,omitempty"`
// Derived (not reported by the provider) — populated by sprout, not adapters.
EligibleRoles []string `json:"eligible_roles,omitempty"` // deterministic pre-filter
Probe *ProbeResult `json:"probe,omitempty"` // capability probe (later phase)
RecommendedRoles []string `json:"recommended_roles,omitempty"` // post-probe (later phase)
Warnings []string `json:"warnings,omitempty"` // non-blocking caveats to surface (e.g. small context)
// Provenance
Source string `json:"source,omitempty"` // e.g. "deepinfra:/models/list"
UpdatedAt string `json:"updated_at,omitempty"` // RFC3339
}
CanonicalModel is the normalized, provider-agnostic representation of a model.
func EnrichFromReference ¶
func EnrichFromReference(dst, ref CanonicalModel) CanonicalModel
EnrichFromReference fills unknown fields on dst from a reference model that represents the same underlying model on another provider. Capabilities, context, and modality are copied with confidence (same model). Pricing is copied only when dst has none, and is marked Estimated because the originating provider's price may be account-specific.
type Capabilities ¶
type Capabilities struct {
Tools *bool `json:"tools,omitempty"`
Vision *bool `json:"vision,omitempty"`
Reasoning *bool `json:"reasoning,omitempty"`
StructuredOutput *bool `json:"structured_output,omitempty"`
Streaming *bool `json:"streaming,omitempty"`
}
Capabilities is a tri-state capability set: a non-nil *bool is a known true/false; nil means the provider's metadata didn't let us determine it. Consumers must treat nil as "unknown", never as false.
func CapabilitiesFromTags ¶
func CapabilitiesFromTags(tags []string) Capabilities
CapabilitiesFromTags reconstructs capabilities from a flat tag list (the inverse of CapabilityTags, used when projecting a legacy flat record up to the canonical shape). A tag's presence is known-true; absence stays unknown (nil) since a flat list can't distinguish known-false from unknown.
type DeepInfraAdapter ¶
DeepInfraAdapter normalizes DeepInfra's native /models/list (public, keyless) into canonical models. That endpoint — unlike the OpenAI-compatible /v1/openai/models — reports context window, per-token pricing, capability tags, type, and a deprecated flag.
func (DeepInfraAdapter) ListModels ¶
func (a DeepInfraAdapter) ListModels(ctx context.Context) ([]CanonicalModel, error)
func (DeepInfraAdapter) Provider ¶
func (a DeepInfraAdapter) Provider() string
type ModelAdapter ¶
type ModelAdapter interface {
Provider() string
ListModels(ctx context.Context) ([]CanonicalModel, error)
}
ModelAdapter owns ALL provider-specific parsing and emits canonical models. It is the only place a provider's quirks are allowed to exist. Listing must not require an API key where the provider's model endpoint is public.
type OpenAIAdapter ¶
type OpenAIAdapter struct {
APIKey string // required — OpenAI's model list is authenticated
HTTPClient *http.Client
Reference *ReferenceCatalog // optional enrichment source
}
OpenAIAdapter normalizes OpenAI's /v1/models into canonical models. Unlike DeepInfra/OpenRouter, OpenAI's listing requires an API key and returns only model IDs (no capabilities/context/pricing). The adapter therefore enriches each model from a Reference catalog (typically built from OpenRouter, which lists the same models as `openai/<id>`): capabilities and context are copied with confidence, pricing is borrowed but flagged Estimated since OpenAI pricing is account-specific.
func (OpenAIAdapter) ListModels ¶
func (a OpenAIAdapter) ListModels(ctx context.Context) ([]CanonicalModel, error)
func (OpenAIAdapter) Provider ¶
func (a OpenAIAdapter) Provider() string
type OpenAICompatAdapter ¶ added in v0.16.2
type OpenAICompatAdapter struct {
ProviderID string // provider identifier (e.g. "cerebras")
BaseURL string // full URL to /v1/models (e.g. "https://api.cerebras.ai/v1/models")
EnvVar string // environment variable name for the API key (e.g. "CEREBRAS_API_KEY")
HTTPClient *http.Client
}
OpenAICompatAdapter is a generic adapter for any provider that exposes a standard OpenAI-compatible /v1/models endpoint. These providers return { "data": [{ "id": "..." }] } and require a Bearer API key for auth.
func NewOpenAICompatAdapter ¶ added in v0.16.2
func NewOpenAICompatAdapter(providerID, baseURL, envVar string) OpenAICompatAdapter
NewOpenAICompatAdapter creates a new adapter for an OpenAI-compatible provider. The ProviderID is used for credential resolution and the Provider() return value.
func (OpenAICompatAdapter) ListModels ¶ added in v0.16.2
func (a OpenAICompatAdapter) ListModels(ctx context.Context) ([]CanonicalModel, error)
func (OpenAICompatAdapter) Provider ¶ added in v0.16.2
func (a OpenAICompatAdapter) Provider() string
type OpenRouterAdapter ¶
OpenRouterAdapter normalizes OpenRouter's /api/v1/models (public, keyless) into canonical models. OpenRouter is also the richest cross-provider source, so its output typically seeds the ReferenceCatalog other adapters borrow from.
func (OpenRouterAdapter) ListModels ¶
func (a OpenRouterAdapter) ListModels(ctx context.Context) ([]CanonicalModel, error)
func (OpenRouterAdapter) Provider ¶
func (a OpenRouterAdapter) Provider() string
type Pricing ¶
type Pricing struct {
InputPerMTok float64 `json:"input_per_mtok"`
OutputPerMTok float64 `json:"output_per_mtok"`
CachedPerMTok float64 `json:"cached_input_per_mtok,omitempty"`
Currency string `json:"currency,omitempty"` // "USD"
Estimated bool `json:"estimated,omitempty"`
Source string `json:"source,omitempty"` // e.g. "openrouter-reference"
}
Pricing is USD per million tokens. Estimated marks values borrowed from a reference source (e.g. OpenRouter) rather than confirmed for the caller's account — relevant where provider pricing is account-specific.
type ProbeResult ¶
type ProbeResult struct {
Passed bool `json:"passed"`
Complex bool `json:"complex,omitempty"`
Score float64 `json:"score,omitempty"`
LastProbedAt string `json:"last_probed_at,omitempty"`
ProbeVersion string `json:"probe_version,omitempty"`
}
ProbeResult records the outcome of a capability probe. Passed is the minimum gate (model is usable for agentic edits at all); Complex additionally reports that the model cleared the discovery+scoping tier, the signal for driving primary-grade complex flows.
type ProviderFile ¶
type ProviderFile struct {
SchemaVersion int `json:"schema_version"`
Provider string `json:"provider"`
GeneratedAt string `json:"generated_at"`
Models []CanonicalModel `json:"models"`
}
ProviderFile is the published per-provider canonical model file.
type ReferenceCatalog ¶
type ReferenceCatalog struct {
// contains filtered or unexported fields
}
ReferenceCatalog indexes canonical models by a normalized `org/model` key so adapters whose native API exposes little metadata (e.g. OpenAI) can borrow capabilities/context/modality from a richer source that lists the same model (e.g. OpenRouter, whose IDs are already `org/model`).
func NewReferenceCatalog ¶
func NewReferenceCatalog(models []CanonicalModel) *ReferenceCatalog
NewReferenceCatalog builds a catalog from already-canonical models (typically the OpenRouter adapter's output).
func (*ReferenceCatalog) Lookup ¶
func (c *ReferenceCatalog) Lookup(org, id string) (CanonicalModel, bool)
Lookup finds a reference model for a provider org + model ID — e.g. org="openai", id="gpt-4o" resolves "openai/gpt-4o". Returns false if absent.