catalog

package
v0.3.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 10, 2026 License: MIT Imports: 19 Imported by: 0

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

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

func EffectiveWindow(model string, override, defaultWindow int) int

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

func GeneratedAt() time.Time

GeneratedAt returns the timestamp the embedded catalog was last refreshed. Zero when the catalog is empty or pre-dates the field.

func IsCurated

func IsCurated(p config.Provider) bool

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

func IsCuratedKind(kind string) bool

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 ModelsDevWindow added in v0.3.0

func ModelsDevWindow(baseURL, model string) int

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

func ModelsDevWindowByProvider(providerID, model string) int

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

func ReasoningInfo(modelID string) (maxOutput int, supportsThinking *bool)

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

func RefreshModelsDev() (int, error)

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

func ResolveWindow(model string, override, defaultWindow int) int

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:

  1. 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).
  2. 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.
  3. WindowFor's prefix table — a conservative compiled fallback for models neither overridden nor catalogued.
  4. 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

func ResolveWindowForProvider(provider, model string, override, defaultWindow int) int

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:

  1. 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;
  2. 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.

When neither 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

func StoreComment(path string) string

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

func UpsertWindow(model string, window int) (changed bool, err error)

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

func WindowFor(model string, defaultWindow int) int

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

func WriteModelsDevSnapshot(path string) (int, error)

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

func Curated(provider string) []Model

Curated returns the offline model catalog for a curated provider kind. Gemini is augmented from the local models.dev snapshot so the picker can offer newly published Gemini 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

func FindByID(id string) (Model, bool)

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

func FindByProviderID(provider, id string) (Model, bool)

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

func Get(provider string) []Model

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

func List(ctx context.Context, p config.Provider, apiKey string) ([]Model, error)

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.

Errors are returned only for live fetches — curated reads 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

func Live(ctx context.Context, kind, baseURL, apiKey string) ([]Model, error)

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

func MergeModels(base, extra []Model) []Model

MergeModels appends models from extra that are not already present in base. The first occurrence of an ID wins, preserving the embedded catalog's display names and ordering while allowing runtime/local catalogs to backfill newer provider models for picker use.

func ModelsDevModelsByProvider added in v0.3.0

func ModelsDevModelsByProvider(providerID, prefix string) []Model

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.

func (Model) Label

func (m Model) Label() string

Label returns DisplayName when set, otherwise ID. Use this for any user-facing string; the picker should never render the bare ID when a friendlier name is available.

type WindowStoreEntry added in v0.3.0

type WindowStoreEntry struct {
	Prefix string `json:"prefix"`
	Window int    `json:"window"`
}

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL