Documentation
¶
Overview ¶
Package catalog is yottacode's source of truth for the set of cloud LLMs the wizard and TUI offer. The data lives in catalog.gen.json, regenerated by `go run ./cmd/yotta-models refresh` against each provider's list-models endpoint. Embedding the JSON keeps the binary self-contained and the user-facing flows offline-friendly.
The schema is the union of fields any provider exposes — Anthropic and Gemini populate most of it, OpenAI's list endpoint is sparse so most fields are zero-valued for OpenAI rows. Capabilities use a tristate (*bool) so renderers can distinguish "not supported" from "not reported." Picker UI shows only known-true capabilities; a verbose details view shows the full tristate.
Index ¶
- func DiscoverContextWindow(ctx context.Context, p config.Provider, apiKey, model string) (int, string)
- func EffectiveWindow(model string, override, defaultWindow int) int
- func GeneratedAt() time.Time
- func IsCurated(p config.Provider) bool
- func IsCuratedKind(kind string) bool
- func LoadError() error
- func ModelsDevLimitsByProvider(providerID, model string) (contextWindow, maxOutput int)
- func ModelsDevWindow(baseURL, model string) int
- func ModelsDevWindowByProvider(providerID, model string) int
- func ReasoningInfo(modelID string) (maxOutput int, supportsThinking *bool)
- func RefreshModelsDev() (int, error)
- func ResolveWindow(model string, override, defaultWindow int) int
- func ResolveWindowForProvider(provider, model string, override, defaultWindow int) int
- func StoreComment(path string) string
- func UpsertWindow(model string, window int) (changed bool, err error)
- func WarmModelsDev()
- func WindowFor(model string, defaultWindow int) int
- func WriteModelsDevSnapshot(path string) (int, error)
- func WriteWindowStore(path string, entries []WindowStoreEntry, comment string) error
- type Capabilities
- type File
- type Model
- func All() []Model
- func Curated(provider string) []Model
- func FindByID(id string) (Model, bool)
- func FindByProviderID(provider, id string) (Model, bool)
- func Get(provider string) []Model
- func List(ctx context.Context, p config.Provider, apiKey string) ([]Model, error)
- func Live(ctx context.Context, kind, baseURL, apiKey string) ([]Model, error)
- func MergeModels(base, extra []Model) []Model
- func ModelsDevModelsByProvider(providerID, prefix string) []Model
- type WindowStoreEntry
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func DiscoverContextWindow ¶ added in v0.3.0
func DiscoverContextWindow(ctx context.Context, p config.Provider, apiKey, model string) (int, string)
DiscoverContextWindow resolves a model's context window from the provider's live API — no hardcoded per-model table. It reads the list-models endpoint first (which surfaces max_model_len / context_length / context_window on the backends that report them — vLLM, many NVIDIA NIM deployments, OpenRouter, Together) and falls back to the local models.dev copy for endpoints that list no window. Returns 0 when neither source yields a positive window, leaving the caller on default_window. Both sources report ADVERTISED limits; when a backend enforces something smaller, the TUI's passive drift correction (internal/tui/window_drift.go) observes it from live traffic and pins the measured value in the runtime overlay.
The window is a per-DEPLOYMENT fact, not a per-model constant: a NIM can serve a model with a --max-model-len far below the model's architectural maximum to fit GPU memory, and new models ship daily, so it must be read from the live endpoint rather than guessed from the model name. Curated providers (Anthropic, OpenAI-auth, …) carry the window in the embedded catalog and never reach the probe.
The second return is a short human-readable diagnostic (which source answered, or why none did) so callers can tell the user what happened instead of silently falling back to default_window.
func EffectiveWindow ¶ added in v0.3.0
EffectiveWindow resolves the context window from an explicit override and the built-in model-tag table only (no catalog lookup) — the lower two layers of ResolveWindow. An explicit per-model override (override > 0 — sourced from the user's config, typically captured from the provider's list-models endpoint or a live probe at registration time) wins over the model-tag table and the configured default. A non-positive override means "not set"; resolution then falls through to WindowFor.
Prefer ResolveWindow, which also consults the catalog; EffectiveWindow is kept as the override+prefix primitive it builds on (and for callers that have no catalog model id to look up).
func GeneratedAt ¶
GeneratedAt returns the timestamp the embedded catalog was last refreshed. Zero when the catalog is empty or pre-dates the field.
func IsCurated ¶
IsCurated reports whether p's kind is sourced from the embedded catalog. Useful when callers want to render different empty-state hints ("run `yotta-models refresh`" vs "couldn't reach API").
func IsCuratedKind ¶
IsCuratedKind is the kind-only flavor of IsCurated, for callers that have a kind string but no full config.Provider in scope (e.g. the wizard, where models live in CatalogEntry, not config.Provider).
func LoadError ¶
func LoadError() error
LoadError returns any parse error that occurred when loading the embedded catalog. nil on success or when the catalog is empty by design.
func ModelsDevLimitsByProvider ¶ added in v0.4.0
ModelsDevLimitsByProvider returns both limits models.dev records for a model under a specific provider id: the context window and the max-output cap, either of which is 0 when unknown.
Output matters as much as context and is just as often missing from a vendor's own API — OpenAI's /v1/models reports neither, and MaxOutput is what sizes an extended-thinking budget. Returning the pair keeps both backfills on one snapshot lookup.
func ModelsDevWindow ¶ added in v0.3.0
ModelsDevWindow returns the context window for a model on the provider whose base URL host matches a models.dev provider's `api` host, or 0 when the catalog is unavailable or has no matching entry. Matching by host keeps the per-deployment value (NVIDIA's 1M for deepseek-v4-pro) instead of some other host's listing of the same id.
func ModelsDevWindowByProvider ¶ added in v0.3.0
ModelsDevWindowByProvider returns the context window for a model under a specific models.dev provider id (e.g. "openai", "anthropic", "google"), or 0. Unlike ModelsDevWindow (host-matched), this looks the provider up by NAME — needed for the curated labs whose models.dev entry carries no `api` URL to host-match against. Used by the catalog refresh to backfill windows a provider's own API omits (notably OpenAI, whose /v1/models returns no context length).
func ReasoningInfo ¶ added in v0.3.0
ReasoningInfo returns the two catalog facts the adapter needs to size an extended-thinking budget for budget-based providers (Anthropic, Gemini): the model's max-output tokens and its thinking-capability tristate. Both are zero/nil when the model isn't in the catalog — the adapter then leaves reasoning at the provider default. Cheap enough to call on every adapter (re)build.
func RefreshModelsDev ¶ added in v0.3.0
RefreshModelsDev forces a network re-fetch of the models.dev catalog, updating the in-memory and on-disk caches regardless of TTL. Returns the number of providers loaded. Used by an explicit refresh flag/command.
func ResolveWindow ¶ added in v0.3.0
ResolveWindow is the single static (no-network) resolver every window consumer should call to size a model's context window. It layers the sources by authority, highest first:
- override (>0) — the user's per-model context_window from config, which is also where a successful live probe persists its result (so this doubles as the observed, per-DEPLOYMENT cache).
- the embedded catalog's ContextWindow for this exact model id — the canonical per-MODEL value for curated providers (Anthropic, Gemini, OpenAI). Consulting it here makes the catalog we already maintain the authority for curated windows, instead of the coarse model-tag prefix table.
- WindowFor's prefix table — a conservative compiled fallback for models neither overridden nor catalogued.
- defaultWindow — the final floor.
It deliberately does NOT touch the network; the live, per-deployment discovery (DiscoverContextWindow) runs separately and persists into the override layer above. Keeping the catalog (per-model, build-time) and the probe cache (per-deployment, runtime) as distinct layers behind one resolver is intentional: they are different kinds of fact with different keys and mutability (see DiscoverContextWindow).
func ResolveWindowForProvider ¶ added in v0.3.0
ResolveWindowForProvider is ResolveWindow for callers that know which provider kind serves the model. The same model id can have different real limits per backend — gpt-5.5 is 1.05M-context on api.openai.com but ~272k-input through the ChatGPT Codex backend (measured 2026-06-10: 264,995 input tokens accepted, ~281k rejected) — so two provider-scoped layers run ahead of ResolveWindow's per-model-id layers, after the override:
- the window store under "<kind>/<model id>" — provider-qualified entries (embedded baseline or the ~/.yottacode overlay, where users can pin their own) carrying per-backend measurements the per-model catalog cannot express;
- the catalog entry for this exact (provider, id) pair — a curated provider's number applies on its own backend only and never leaks to a namesake model behind a different kind;
- the provider's curated list, matched against the host-qualified forms of the id (see curatedWindowForProvider).
When no provider-scoped layer answers — openai-compatible proxies, an empty kind, models with no qualified facts — resolution degrades to ResolveWindow unchanged.
func StoreComment ¶ added in v0.3.0
StoreComment returns the _comment of the window-store file at path, or "" for a missing or malformed file. Regeneration tooling uses it to carry the existing comment forward — the committed baseline's comment documents the entry conventions (provider-qualified prefixes, measurement provenance) and must survive a rewrite.
func UpsertWindow ¶ added in v0.3.0
UpsertWindow records window for an exact model id in the runtime overlay (~/.yottacode/context-windows.json), writing it atomically and refreshing the cache. An existing entry whose prefix equals model is updated; otherwise a new exact-id entry is added. window <= 0 or an empty model is a no-op. This is the write side used by the TUI's background probe (so a discovered window persists for later sessions) and by `model probe-windows` without an explicit output path.
changed reports whether the store actually changed: false when the model was already recorded with this exact window (a re-probe of an already- cached value), so callers can stay quiet about a discovery that told them nothing new. When unchanged, the file is left untouched (no rewrite).
func WarmModelsDev ¶ added in v0.3.0
func WarmModelsDev()
WarmModelsDev triggers a TTL-gated load of the models.dev catalog — a no-op when the in-memory or on-disk cache is still fresh. Call it in the background at startup so the first real window lookup never waits on the ~2MB fetch, and so a stale local copy is refreshed once a day.
func WindowFor ¶ added in v0.3.0
WindowFor returns the context-window capacity (in tokens) for the given model tag from the file-backed window store, falling back to defaultWindow when no entry matches. Matching is by lowercase prefix — generous on purpose so unknown variants of known families still resolve, with the longest (most specific) prefix winning.
The store is the LAST resort, consulted only when neither the user's override nor the curated catalog answered, so its values lean toward safe under-estimates (an over-estimate here would let a session overrun a smaller real window and hard-fail). Its data lives OUTSIDE the Go source — an embedded, committed baseline plus a runtime overlay (~/.yottacode/context-windows.json) — see windowstore.go. Users with an exotic model can pin a per-model context_window or context.default_window.
func WriteModelsDevSnapshot ¶ added in v0.3.0
WriteModelsDevSnapshot fetches the live models.dev catalog and writes it (trimmed to the fields we use) to path as a diskCacheEnvelope. Used by `yotta-models refresh-modelsdev` to regenerate the committed embedded snapshot. Returns the number of providers written.
func WriteWindowStore ¶ added in v0.3.0
func WriteWindowStore(path string, entries []WindowStoreEntry, comment string) error
WriteWindowStore replaces a window-store file at path with the given entries (full rewrite, not an upsert) — used by `model probe-windows` to (re)generate either the runtime overlay or, with --output, the committed embedded baseline. Pass an empty path to target the runtime overlay.
Types ¶
type Capabilities ¶
type Capabilities struct {
Thinking *bool `json:"thinking,omitempty"`
Vision *bool `json:"vision,omitempty"`
PDF *bool `json:"pdf,omitempty"`
StructuredOutputs *bool `json:"structured_outputs,omitempty"`
Tools *bool `json:"tools,omitempty"`
}
Capabilities is the small flag set we surface in the picker. *bool is intentional: nil means the provider didn't tell us, true means supported, false means explicitly not supported. Renderers should distinguish — chip rows show only true; details view shows all three states.
type File ¶
type File struct {
GeneratedAt time.Time `json:"generated_at,omitempty"`
Models []Model `json:"models"`
}
File is the on-disk shape of catalog.gen.json. Keeping the wrapper (rather than a top-level array) gives us room to add metadata (refresh timestamp, schema version) without breaking older binaries.
type Model ¶
type Model struct {
ID string `json:"id"`
DisplayName string `json:"display_name,omitempty"`
Provider string `json:"provider"`
ContextWindow int `json:"context_window,omitempty"`
MaxOutput int `json:"max_output,omitempty"`
ReleasedAt time.Time `json:"released_at,omitempty"`
Description string `json:"description,omitempty"`
Capabilities Capabilities `json:"capabilities,omitempty"`
Disabled bool `json:"disabled,omitempty"`
}
Model is one entry in the catalog. Provider is always populated; every other field may be empty/zero when the source API doesn't surface that information. ID is the canonical model identifier the API will accept in subsequent calls.
func All ¶
func All() []Model
All returns every model across every provider. Useful for the debug `/doctor` view; not used by the picker (which is always scoped to one provider). The returned slice is shared — callers must not mutate it.
func Curated ¶ added in v0.3.0
Curated returns the offline model catalog for a curated provider kind. Kinds in modelsDevAugment are backfilled from the local models.dev snapshot so the picker can offer newly published IDs even when catalog.gen.json lags. This is the entry point every picker surface (wizard, /provider add, /model) and model-ownership lookup should use for curated kinds; reach for Get only when the raw embedded catalog is specifically wanted.
func FindByID ¶ added in v0.3.0
FindByID returns the embedded-catalog entry whose ID matches, searching across every provider. Model IDs are effectively globally unique (claude-*, gemini-*, gpt-*, …), so the first match is the right one. ok is false when nothing matches — including for the runtime-sourced openai-auth/copilot sets, which carry no token limits or capability flags worth reasoning over. Callers then leave catalog-derived fields zero/nil.
func FindByProviderID ¶ added in v0.3.0
FindByProviderID returns the catalog entry for id owned by the given provider. Unlike FindByID it never crosses provider namespaces: the same model id served through a different backend (gpt-5.5 via the ChatGPT Codex backend vs api.openai.com) is a different deployment with different limits, so a namesake's facts must not leak.
For the runtime-sourced kinds the per-user scan set stands in for the embedded catalog — copilot's scan captures real per-backend token limits that exist nowhere else (openai-auth's scan carries bare ids, so its entries simply never satisfy window>0 checks).
func Get ¶
Get returns the catalog entries for one provider, sorted newest- first by ReleasedAt with ID as tiebreak. Returns an empty slice when the catalog is empty or the provider isn't known. Never returns nil. The returned slice is shared — callers must not mutate it.
openai-auth is special-cased here (not just in List) because callers across the wizard / TUI / picker historically reach for Get directly. The result for openai-auth comes from the runtime per-user models file written by post-login scans, not from catalog.gen.json — see openAIAuthModels in list.go.
func List ¶
List returns the model list a picker should display for one provider profile. Curated providers are read out of the embedded catalog (or, for openai-auth, the runtime allow-list) and never touch the network; non-curated providers are fetched live each call. The signature is the same in both cases so callers don't branch.
Models the user declared under [[providers.models]] are merged in either way, and stand in for the list when a live fetch fails: plenty of real OpenAI-compatible endpoints implement chat/completions and no /models route at all (Vertex's Gemini shim 404s on it), and a user who hand-listed their models has already told us what the endpoint serves. Leaving the picker empty in that case makes the config a lie.
A live-fetch error is returned even when declared models cover for it, so callers can say so — the picker notes the failed fetch under the list, and `model fetch` prints a warning. Swallowing it would make an unreachable endpoint look healthy, which for Ollama usually means the daemon is down and the user needs to know. Callers that only want a list can ignore the error when the slice is non-empty.
Errors never come from curated reads, which are in-memory and infallible. An empty catalog (initial state, before the maintainer runs the refresh command) returns an empty slice with no error.
func Live ¶
Live queries a provider's list-models endpoint at runtime. Used for non-curated providers — Ollama (lists locally-installed models, genuinely runtime state) and openai-compatible endpoints (NVIDIA NIM, custom proxies — too varied to script-curate).
The response is mapped onto our common Model schema, but most fields stay zero/nil because these list endpoints return only the model id. The picker shows "—" for missing fields.
Errors surface so callers can render "couldn't reach API"; they should not silently fall back, since for Ollama a failed probe usually means the daemon isn't running and the user needs to know.
func MergeModels ¶ added in v0.3.0
MergeModels appends models from extra that are not already present in base. The first occurrence of an ID wins, preserving the caller's preferred ordering and metadata while allowing runtime/local catalogs to backfill newer provider models for picker use.
func ModelsDevModelsByProvider ¶ added in v0.3.0
ModelsDevModelsByProvider returns model IDs from the local models.dev snapshot for a provider, filtered by prefix. This backs picker lists with a fresh offline catalog when the generated provider catalog lags a vendor release; it never touches the network beyond the normal models.dev cache.
type WindowStoreEntry ¶ added in v0.3.0
WindowStoreEntry is one row of the context-window store.
func LoadEntriesFromFile ¶ added in v0.3.0
func LoadEntriesFromFile(path string) []WindowStoreEntry
LoadEntriesFromFile reads and parses the window entries from a specific file (e.g. the committed baseline or the runtime overlay), for tooling that regenerates the store and wants to merge with what's already there. Returns nil for a missing or malformed file.