/** * Typed `LlmRoute` system — the structural-correctness replacement for * string-typed model identifiers. * * Two concepts, one type: * - `provider` — the API endpoint you authenticate against, owns its * own model namespace. Includes marketplaces (Bedrock, OpenRouter) * and direct-author APIs (Claude/Anthropic, OpenAI, Google AI * Studio). NOT a separate "platform" dimension — each provider's * API surface IS the platform. * - `model` — the wire-canonical string for THIS provider. Registry * KEY == what goes on the HTTP wire to the provider. No * transformation at dispatch — what you write in `MODELS[provider]` * is exactly what the API sees. * * Why this exists: three bugs of the same class in one week (#22 CLI * sent slash-prefixed `google/gemini-3.5-flash` → Gemini 404; #42 * mcp-server negotiator sent `anthropic/claude-haiku-4-5` → Anthropic * 404; the next one would have been...). The pattern was always * "someone wrote a code path that bypassed `getUpstreamModelId`". The * typed-route system makes the bug class structurally impossible — * `LlmRoute` is a discriminated union, the dispatch is * exhaustiveness-checked, and there's no transformation step to * forget because the model string IS the wire form. * * Slice spec: `docs/plans/2026-05-25-llm-route-typed-system.md` */ /** * Wire-canonical model names per provider. The KEY for each entry is * EXACTLY what the provider's API expects on the wire — no * transformation, no prefix-strip, no map. Register a new model by * adding its wire-canonical string to the matching provider's array. * * Provider naming uses the COMPANY name for consistency across all * providers (`anthropic`, `openai`, `google`) — matches LiteLLM's * `anthropic/` prefix + the existing `ANTHROPIC_API_KEY` env var * convention. Marketplaces (`bedrock`, `openrouter`) keep their * platform name because that IS the company you authenticate against. * * - `anthropic` — Anthropic's direct API * (`api.anthropic.com`). Auth: `ANTHROPIC_API_KEY`. * - `openai` — OpenAI's direct API. * - `google` — Google AI Studio (`generativelanguage.googleapis.com`). * - `bedrock` — AWS Bedrock marketplace. Region prefix (`us.`, * `eu.`, `apac.`) is part of the wire name; each region is its * own registry entry, not a `{region}` field. * - `openrouter` — OpenRouter marketplace. Authors are sub-namespaced * in the model string (`/`). * - `vertex` — DEFERRED to its own slice. Vertex needs region + * projectId + GCP IAM setup; migration is purely additive when * ready. */ export declare const MODELS: { readonly anthropic: readonly ["claude-fable-5-1", "claude-fable-5", "claude-opus-5", "claude-sonnet-5", "claude-haiku-4-5-20251001", "claude-opus-4-8", "claude-sonnet-4-6", "claude-opus-4-7", "claude-opus-4-6"]; readonly openai: readonly ["gpt-6-astra", "gpt-5.6", "gpt-5.6-sol", "gpt-5.6-terra", "gpt-5.6-luna", "gpt-5.5", "gpt-5.5-2026-04-23", "gpt-5.5-pro", "gpt-5.5-pro-2026-04-23", "gpt-5.4", "gpt-5.4-2026-03-05", "gpt-5.4-mini", "gpt-5.4-mini-2026-03-17", "gpt-5.4-nano", "gpt-5.4-nano-2026-03-17", "gpt-5.3-codex"]; readonly google: readonly ["gemini-3.7-flash", "gemini-3.6-flash", "gemini-3.5-flash", "gemini-3.5-flash-lite", "gemini-flash-latest", "gemini-3.1-flash-lite", "gemini-3.1-pro-preview", "gemini-3.1-flash-lite-preview", "gemini-3-flash-preview"]; readonly bedrock: readonly ["anthropic.claude-fable-5-1", "anthropic.claude-fable-5", "anthropic.claude-opus-5", "anthropic.claude-sonnet-5", "anthropic.claude-opus-4-8", "us.anthropic.claude-haiku-4-5-20251001-v1:0", "eu.anthropic.claude-haiku-4-5-20251001-v1:0", "apac.anthropic.claude-haiku-4-5-20251001-v1:0", "global.anthropic.claude-haiku-4-5-20251001-v1:0", "us.anthropic.claude-sonnet-4-6", "eu.anthropic.claude-sonnet-4-6", "apac.anthropic.claude-sonnet-4-6", "us.anthropic.claude-opus-4-7", "eu.anthropic.claude-opus-4-7", "apac.anthropic.claude-opus-4-7", "global.anthropic.claude-opus-4-7", "us.anthropic.claude-opus-4-6-v1"]; readonly openrouter: readonly ["anthropic/claude-fable-5", "anthropic/claude-opus-5", "anthropic/claude-sonnet-5", "anthropic/claude-haiku-4.5", "anthropic/claude-sonnet-4.6", "anthropic/claude-opus-4.7", "openai/gpt-5.6-sol", "openai/gpt-5.6-terra", "openai/gpt-5.6-luna", "openai/gpt-5.5", "openai/gpt-5.5-pro", "openai/gpt-5.4-mini", "openai/gpt-5.4-nano", "google/gemini-3.6-flash", "google/gemini-3.5-flash", "google/gemini-3.5-flash-lite", "google/gemini-3.1-pro-preview", "google/gemini-3.1-flash-lite", "x-ai/grok-4.3", "meta-llama/llama-4-maverick", "meta-llama/llama-3.3-70b-instruct", "deepseek/deepseek-v4-pro", "deepseek/deepseek-r1-0528", "qwen/qwen3.7-max", "qwen/qwen3-coder", "mistralai/mistral-large-2512", "openai/gpt-oss-120b"]; }; /** * Every supported LLM provider. Adding a new provider = add a key to * `MODELS` + add a dispatch case wherever `LlmRoute` is consumed (TS * exhaustiveness check forces handling). */ export type LlmProvider = keyof typeof MODELS; /** * Model names known at compile time for a given provider. For * OpenRouter, this is the enumerated subset; for every other provider, * this is the full set (their model lists are small + stable). */ export type KnownModelOf

= (typeof MODELS)[P][number]; /** * Model names accepted on a route for a given provider. Two providers * use the `(string & {})` escape hatch: * * - **OpenRouter** — `/` permutation space is too * large to enumerate. Curated subset gives IDE autocomplete; * arbitrary strings pass validation via shape (matches the * `/` rule). * - **Bedrock** — operators pick from AWS-supplied foundation model * ids (e.g. `'anthropic.claude-sonnet-4-6'`), cross-region * inference profile ids (e.g. `'us.anthropic.claude-haiku-4-5- * 20251001-v1:0'`), or even custom inference profile ARNs * (`'arn:aws:bedrock:...'`). The MODELS.bedrock list curates the * common cross-region profiles for autocomplete; arbitrary * strings pass at runtime so operators with custom profiles + * non-curated foundation models stay supported. * * Every other provider is strict-enum: only names in * `MODELS[provider]` typecheck. * * The `(string & {})` trick preserves IDE autocomplete on the known * subset while still accepting arbitrary strings — without it, the * union collapses to `string` and the known entries lose autocomplete. */ export type ModelOf

= P extends 'openrouter' | 'bedrock' ? KnownModelOf

| (string & {}) : KnownModelOf

; /** * A typed LLM call target. The pair `(provider, model)` is sufficient * — `provider` selects the SDK / endpoint / auth scheme, `model` is * the wire string that SDK sends literally. * * Discriminated union via mapped type: `LlmRoute` is the union of * `{provider: P, model: ModelOf

}` for every `P`. TypeScript * enforces that the model belongs to the provider's namespace — e.g. * `{provider: 'bedrock', model: 'claude-haiku-4-5-20251001'}` is a * compile error (that model lives under `claude`, not `bedrock`). * * Vertex AI is deferred — it needs `region` and `projectId` fields * for endpoint construction + IAM. When added, the Vertex variant * will look like `{provider: 'vertex', model: ..., region: ..., * projectId: ...}` — purely additive, doesn't disturb existing routes. */ export type LlmRoute = { [P in LlmProvider]: { provider: P; model: ModelOf

; }; }[LlmProvider]; /** * Runtime type guard for the provider enum. Pairs with the parser at * wire boundaries (ggui.json, CLI flags, env vars) where the value * arrives as an unvalidated string. */ export declare function isLlmProvider(s: string): s is LlmProvider; /** * Runtime type guard for a known model string under a given provider. * Strict-enum check; does NOT accept OpenRouter's arbitrary-string * extension — for that, use {@link isValidOpenrouterModel}. */ export declare function isKnownModel

(provider: P, model: string): model is KnownModelOf

; /** * Validate an OpenRouter model string by shape: `/` * where both segments are non-empty and contain only the characters * OpenRouter's catalog uses (alphanumerics + `-` + `.` + `_` + `:`). * * Used for the OpenRouter escape hatch — strings that pass this check * are accepted into `LlmRoute` even if not in `MODELS.openrouter[]`. * Strings that fail it are rejected at the parser boundary. */ export declare function isValidOpenrouterModel(s: string): boolean; /** * Validate a Bedrock model string by shape. AWS accepts THREE forms: * * - Cross-region inference profile ids — `.` where * region is `us`/`eu`/`apac`/`global` and inner contains `.`, * `-`, `:`, alphanumerics (e.g. * `'us.anthropic.claude-haiku-4-5-20251001-v1:0'`). * - Bedrock foundation model ids — bare `.` form * (e.g. `'anthropic.claude-sonnet-4-6'`). * - Inference profile ARNs — `'arn:aws:bedrock:...'`. * * Used for the Bedrock escape hatch — strings passing this check are * accepted into `LlmRoute` even if not in `MODELS.bedrock[]`. */ export declare function isValidBedrockModel(s: string): boolean; /** * Validate that a (provider, model) pair would construct a valid * `LlmRoute`. Two providers have arbitrary-string escape hatches: * * - `openrouter` — accepts any string passing * {@link isValidOpenrouterModel} * - `bedrock` — accepts any string passing {@link isValidBedrockModel} * * Every other provider requires the model to be in * `MODELS[provider]`. */ export declare function isValidLlmRoute(provider: string, model: string): boolean; /** * Serialize an `LlmRoute` to the canonical `provider:model` string. * Round-trip with {@link parseLlmRoute}. Used for human-readable * logging, `ggui.json` config values, and CLI `--model` flags. * * Examples: * `{provider: 'anthropic', model: 'claude-haiku-4-5-20251001'}` * → `'anthropic:claude-haiku-4-5-20251001'` * `{provider: 'bedrock', model: 'us.anthropic.claude-haiku-4-5-20251001-v1:0'}` * → `'bedrock:us.anthropic.claude-haiku-4-5-20251001-v1:0'` * `{provider: 'openrouter', model: 'anthropic/claude-haiku-4.5'}` * → `'openrouter:anthropic/claude-haiku-4.5'` */ export declare function serializeLlmRoute(route: LlmRoute): string; /** * Parse a canonical `provider:model` string into an `LlmRoute`. * Returns `null` if the string isn't well-formed or if (provider, * model) wouldn't construct a valid route. Permissive on the model * side for OpenRouter — accepts any string passing * {@link isValidOpenrouterModel} even when not in `MODELS.openrouter[]`. * * Round-trip with {@link serializeLlmRoute}. */ export declare function parseLlmRoute(serialized: string): LlmRoute | null; /** * Map from LiteLLM transport prefix → our `LlmProvider` enum. Used by * {@link parseLiteLlmString} to accept legacy ggui.json files and * ecosystem inputs without forcing a migration on every operator. * * Note: LiteLLM separates `gemini/` (AI Studio) from `vertex_ai/` * (enterprise Vertex). We don't have a Vertex variant in this slice; * `vertex_ai/` parsing falls through to `null` until Vertex lands. */ /** * The prefixes a {@link ModelRef} may start with — the LiteLLM spellings, * and the model registry's. The single source: the prefix→provider map * below is keyed by it (exhaustive by type), and the JSON-Schema-facing * `pattern` in `llmBlueprintSourceSchema` is built from it. */ export declare const MODEL_REF_PREFIXES: readonly ["anthropic", "gemini", "openai", "bedrock", "openrouter"]; export type ModelRefPrefix = (typeof MODEL_REF_PREFIXES)[number]; /** Whether `s` is one of {@link MODEL_REF_PREFIXES}. */ export declare function isModelRefPrefix(s: string): s is ModelRefPrefix; /** * Parse a LiteLLM-format string (`/` or * `//` for OpenRouter) into an `LlmRoute`. * Returns `null` if the prefix is unknown or the resulting route * wouldn't construct. * * Supports the historical formats ggui has used: * - `anthropic/claude-haiku-4-5` → `{anthropic, claude-haiku-4-5-20251001}` * - `gemini/gemini-3.5-flash` → `{google, gemini-3.5-flash}` * - `openai/gpt-5.5-...` → `{openai, gpt-5.5-...}` * - `bedrock/us.anthropic...` → `{bedrock, us.anthropic...}` * - `openrouter/anthropic/claude-haiku-4.5` * → `{openrouter, anthropic/claude-haiku-4.5}` * * Used at wire boundaries (ggui.json parser, CLI flag, env var) so * existing operator configs keep working without a forced migration. */ export declare function parseLiteLlmString(s: string): LlmRoute | null; /** * Serialize an `LlmRoute` to LiteLLM format for outbound observability * (Datadog, PostHog, OpenTelemetry `gen_ai.*` semantic conventions * recognize LiteLLM IDs). Inverse of {@link parseLiteLlmString}. * * `{provider: 'anthropic', model: 'claude-haiku-4-5-20251001'}` * → `'anthropic/claude-haiku-4-5'` * * For models without an explicit LITELLM_TO_WIRE mapping (Gemini, * OpenAI, OpenRouter), serialization uses the wire model as-is. */ export declare function toLiteLlmString(route: LlmRoute): ModelRef; /** * Single-entry parser that accepts both canonical (`provider:model`) * and LiteLLM (`prefix/model`) formats. Used at wire boundaries that * receive unvalidated strings — `ggui.json#generation.model`, CLI * `--model` flag, env-var defaults. * * Tries the canonical form first (no internal `/` ambiguity); if it * doesn't parse, falls back to LiteLLM. Returns `null` if neither * format produces a valid route. */ export declare function parseAnyLlmRoute(s: string): LlmRoute | null; /** * A route rendered as ONE string in the model registry's spelling — * the LiteLLM form `/` (#818: one model-id vocabulary, * two separators). Every registry id (`ModelId`, e.g. * `anthropic/claude-haiku-4-5`, `gemini/gemini-3.5-flash`) is the ref * of the route it names, so `ModelId ⊂ ModelRef`; a bedrock inference * profile or an unlisted OpenRouter model is a `ModelRef` the registry * does not list. Where a wire model has a LiteLLM alias * (`claude-haiku-4-5-20251001` ↔ `claude-haiku-4-5`) the ref is the * ALIAS — the dated wire id is not a ref spelling. * * Split at the FIRST `/`: prefixes contain no slash, so * `openrouter/anthropic/claude-3.5-sonnet` is unambiguous. * * Used where a record must say which model a run used and readers must * recover the route: `LlmBlueprintSource.model`. */ export type ModelRef = `${ModelRefPrefix}/${string}`; /** * The one composer of a {@link ModelRef}: render the route the run used, * inverting the LiteLLM alias map so a resolved `{anthropic, * claude-haiku-4-5-20251001}` renders as `anthropic/claude-haiku-4-5` — * the registry id. Total: every `LlmRoute` has a ref. Feed it the ROUTE * the call used; the ref is the same whether the route was configured * as the alias or already resolved. */ export declare function modelRefOfRoute(route: LlmRoute): ModelRef; /** * Recover the route from a {@link ModelRef}. `null` unless `s` is a * LiteLLM-form string that parses ({@link parseLiteLlmString}) AND is * the spelling {@link modelRefOfRoute} produces for that route — a ref * has one spelling, so the dated wire id of an aliased model and a * wrong prefix are refused here even though the lenient wire parsers * accept them. */ export declare function parseModelRef(s: string): LlmRoute | null; /** Whether `s` is a {@link ModelRef} — see {@link parseModelRef}. */ export declare function isModelRef(s: string): s is ModelRef; //# sourceMappingURL=llm-route.d.ts.map