import type { ProviderId } from '#src/providers/modelDiscovery.js'; /** The single models.dev dataset endpoint (whole catalog; sliced per-provider on our side). */ export declare const MODELS_DEV_URL = "https://models.dev/api.json"; /** * Cache freshness window. Pricing/limit metadata is near-static, so a day between refreshes is * plenty; `gth models --refresh` is the manual escape hatch when a newer number is needed sooner. */ export declare const CATALOG_TTL_MS: number; /** * Catalog fetch timeout. Deliberately generous (and separate from `/v1/models` discovery's 2 s * interactive-path probe): this fetch runs off the explicit `gth models` cache-fill path, not a * first-run keystroke, so latency headroom matters more than snappiness. The headroom is for the * slow and high-latency links rather than for the payload — compressed, it is a few hundred KB and * lands in a fraction of a second on a fast connection — and on those links this timeout is the * only thing bounding the wait. */ export declare const CATALOG_TIMEOUT_MS = 10000; /** Shown next to any enriched price/metadata. Costs from models.dev are US$ per 1M tokens. */ export declare const CATALOG_ATTRIBUTION = "* model prices provided by models.dev"; /** * Map a Gaunt Sloth {@link ProviderId} to its models.dev top-level provider key. Cloud providers * only; local/self-hosted providers (`ollama`) are intentionally absent so they get NO catalog * lookup. Returns `undefined` for any provider without a models.dev slice. */ export declare const MODELS_DEV_PROVIDER_KEY: Partial>; /** Per-token cost metadata (US$ per 1M tokens), as published by models.dev. */ export interface ModelCost { input?: number; output?: number; cacheRead?: number; cacheWrite?: number; } /** Context / output token limits. */ export interface ModelLimit { context?: number; output?: number; } /** Input/output modalities (e.g. `text`, `image`, `pdf`). */ export interface ModelModalities { input?: string[]; output?: string[]; } /** * Normalised catalog metadata for one model id — the models.dev fields we surface, camelCased and * detached from models.dev's raw JSON shape so a schema change there is absorbed in the parser. */ export interface ModelCatalogEntry { /** models.dev display name (e.g. "Claude Opus 4.5"). */ name?: string; cost?: ModelCost; limit?: ModelLimit; modalities?: ModelModalities; /** True when the model exposes an extended-reasoning / thinking mode. */ reasoning?: boolean; /** True when the model supports tool/function calling. */ toolCall?: boolean; } /** A provider's catalog slice: model-id → metadata, plus provenance for TTL / display. */ export interface ProviderCatalog { /** Gaunt Sloth provider id this slice belongs to. */ providerId: ProviderId; /** models.dev provider key it was sliced from. */ providerKey: string; /** Epoch ms the slice was fetched (drives TTL freshness). */ fetchedAt: number; /** Metadata keyed by model id, exactly as models.dev keys them. */ models: Record; } /** Options shared by catalog reads; every field is injectable for hermetic tests. */ export interface CatalogOptions { /** Force a network refresh even when a fresh cache exists (backs `gth models --refresh`). */ refresh?: boolean; /** Override the cache directory (defaults to `~/.gsloth/model-catalog`). */ cacheDir?: string; /** Override "now" for TTL math (defaults to `Date.now()`). */ now?: number; /** Override the fetch implementation (defaults to the global `fetch`). */ fetchImpl?: typeof fetch; /** Override the TTL window in ms (defaults to {@link CATALOG_TTL_MS}). */ ttlMs?: number; /** * EXT-161 — **read the cache and never reach the network**, returning the cached slice whether * it is fresh or stale, and `null` when there is none. * * For callers on a latency-critical path that would rather have no answer than a slow one. The * context guard runs before the first model call of a session, and a cold fetch there is bounded * only by {@link CATALOG_TIMEOUT_MS}: a fraction of a second on a fast link, and on a slow or * high-latency one whatever the link takes, all of it in front of the user's first turn — to * decide a threshold that has a fallback (the model profile) and whose absence costs only that * one session's preventive compaction. It is the tail this guards, not the median. The cache is * filled by `gth init` and `gth models --refresh`, which are explicit and can afford to wait. * * What the fallback costs in exchange is written where it is paid — see the tier-3 ruling in * `core/contextWindow.ts`. * * Ignored when {@link refresh} is set — an explicit refresh is a request to go to the network. */ cacheOnly?: boolean; } /** * Get the models.dev catalog slice for one provider, cache-first. * * - **Local / self-hosted** (`ollama`, or any provider without a {@link MODELS_DEV_PROVIDER_KEY}): * returns `null` immediately, with **no network call**. * - **Cloud, fresh cache** (and not `refresh`): returns the cached slice, no network call. * - **Cloud, missing / stale cache, or `refresh`**: fetches `api.json` once, slices this provider, * writes the slice to the per-provider cache, and returns it. * - **Fetch fails**: returns the stale cached slice if one exists (degrade, don't blank), else `null`. * * NEVER throws — catalog availability must never block model use. */ export declare function getProviderCatalog(providerId: ProviderId, options?: CatalogOptions): Promise; /** A {@link @gaunt-sloth/core!providers/modelDiscovery.ModelInfo | ModelInfo}-shaped record decorated with optional catalog metadata. */ export interface EnrichedModel { id: string; preferred: boolean; /** models.dev metadata for this id, when the catalog has an entry; absent = unenriched. */ enrichment?: ModelCatalogEntry; } /** * Enrich a provider's discovered model list with catalog metadata, cache-first. * * `/v1/models` stays authoritative: every input model is returned, in order, whether or not * models.dev knows it. Matched ids gain an `enrichment`; unmatched ids (and every model when the * catalog is unavailable or the provider is local) are returned untouched. NEVER throws. */ export declare function enrichModels(providerId: ProviderId, models: ReadonlyArray<{ id: string; preferred: boolean; }>, options?: CatalogOptions): Promise;