llm-strings 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/README.md +1 -3
  2. package/dist/ai-sdk.cjs +355 -9
  3. package/dist/ai-sdk.cjs.map +1 -1
  4. package/dist/ai-sdk.d.cts +5 -4
  5. package/dist/ai-sdk.d.ts +5 -4
  6. package/dist/ai-sdk.js +87 -8
  7. package/dist/ai-sdk.js.map +1 -1
  8. package/dist/{chunk-RPXK2A7O.js → chunk-7HE4RH6X.js} +4 -4
  9. package/dist/{chunk-2ARD4TFU.js → chunk-NZR5DUX5.js} +2 -2
  10. package/dist/{chunk-W4NIQY7M.js → chunk-OCJX4QFJ.js} +318 -4
  11. package/dist/chunk-OCJX4QFJ.js.map +1 -0
  12. package/dist/{chunk-BCOUH7LH.js → chunk-TQJ2ABCT.js} +2 -2
  13. package/dist/index.cjs +317 -3
  14. package/dist/index.cjs.map +1 -1
  15. package/dist/index.d.cts +1 -1
  16. package/dist/index.d.ts +1 -1
  17. package/dist/index.js +4 -4
  18. package/dist/normalize.cjs +157 -3
  19. package/dist/normalize.cjs.map +1 -1
  20. package/dist/normalize.d.cts +1 -1
  21. package/dist/normalize.d.ts +1 -1
  22. package/dist/normalize.js +2 -2
  23. package/dist/parse.cjs +27 -0
  24. package/dist/parse.cjs.map +1 -1
  25. package/dist/parse.d.cts +1 -1
  26. package/dist/parse.d.ts +1 -1
  27. package/dist/parse.js +2 -2
  28. package/dist/{provider-core-BiAl8MCV.d.cts → provider-core-B934MuhJ.d.cts} +2 -2
  29. package/dist/{provider-core-BiAl8MCV.d.ts → provider-core-B934MuhJ.d.ts} +2 -2
  30. package/dist/providers.cjs +610 -4
  31. package/dist/providers.cjs.map +1 -1
  32. package/dist/providers.d.cts +2 -2
  33. package/dist/providers.d.ts +2 -2
  34. package/dist/providers.js +294 -2
  35. package/dist/providers.js.map +1 -1
  36. package/dist/validate.cjs +317 -3
  37. package/dist/validate.cjs.map +1 -1
  38. package/dist/validate.js +4 -4
  39. package/package.json +1 -1
  40. package/dist/chunk-W4NIQY7M.js.map +0 -1
  41. /package/dist/{chunk-RPXK2A7O.js.map → chunk-7HE4RH6X.js.map} +0 -0
  42. /package/dist/{chunk-2ARD4TFU.js.map → chunk-NZR5DUX5.js.map} +0 -0
  43. /package/dist/{chunk-BCOUH7LH.js.map → chunk-TQJ2ABCT.js.map} +0 -0
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/normalize.ts","../src/provider-core.ts"],"sourcesContent":["import type { LlmConnectionConfig } from \"./parse.js\";\nimport {\n ALIASES,\n CACHE_TTLS,\n CACHE_VALUES,\n DURATION_RE,\n PROVIDER_PARAMS,\n bedrockSupportsCaching,\n canHostOpenAIModels,\n detectGatewaySubProvider,\n detectProvider,\n isGatewayProvider,\n isReasoningModel,\n providerFromHostAlias,\n type Provider,\n} from \"./provider-core.js\";\n\nexport interface NormalizeChange {\n from: string;\n to: string;\n value: string;\n reason: string;\n}\n\nexport interface NormalizeResult {\n config: LlmConnectionConfig;\n provider: Provider | undefined;\n /** Underlying provider extracted from gateway model prefix (e.g. \"anthropic\" from \"anthropic/claude-sonnet-4-5\"). */\n subProvider: Provider | undefined;\n changes: NormalizeChange[];\n}\n\nexport interface NormalizeOptions {\n /** Include detailed change log in the result. */\n verbose?: boolean;\n}\n\n/**\n * Normalize an LLM connection config's params for its target provider.\n *\n * 1. Expands shorthand aliases (e.g. `temp` → `temperature`)\n * 2. Maps canonical param names to provider-specific names\n * (e.g. `max_tokens` → `maxOutputTokens` for Google)\n * 3. Normalizes special values (e.g. `cache=true` → `cache_control=ephemeral` for Anthropic)\n * 4. For OpenAI reasoning models, remaps `max_tokens` → `max_completion_tokens`\n * and warns about unsupported sampling params\n */\nexport function normalize(\n config: LlmConnectionConfig,\n options: NormalizeOptions = {},\n): NormalizeResult {\n const provider =\n (config.hostAlias ? providerFromHostAlias(config.hostAlias) : undefined) ??\n detectProvider(config.host);\n const subProvider =\n provider && isGatewayProvider(provider)\n ? detectGatewaySubProvider(config.model)\n : undefined;\n const changes: NormalizeChange[] = [];\n const params: Record<string, string> = {};\n\n for (const [rawKey, value] of Object.entries(config.params)) {\n let key = rawKey;\n\n // Step 1: Expand aliases to canonical name\n if (ALIASES[key]) {\n const canonical = ALIASES[key];\n if (options.verbose) {\n changes.push({\n from: key,\n to: canonical,\n value,\n reason: `alias: \"${key}\" → \"${canonical}\"`,\n });\n }\n key = canonical;\n }\n\n // Step 2: Handle special \"cache\" param\n if (key === \"cache\" && provider) {\n let cacheValue = CACHE_VALUES[provider];\n\n // Bedrock supports cache for Anthropic Claude and Amazon Nova models\n if (provider === \"bedrock\" && !bedrockSupportsCaching(config.model)) {\n cacheValue = undefined;\n }\n\n // Provider/model doesn't support cache — drop it\n if (!cacheValue) {\n if (options.verbose) {\n changes.push({\n from: \"cache\",\n to: \"(dropped)\",\n value,\n reason: `${provider} does not use a cache param for this model (caching is automatic or unsupported)`,\n });\n }\n continue;\n }\n\n const isBool = value === \"true\" || value === \"1\" || value === \"yes\";\n const isDuration = DURATION_RE.test(value);\n\n if (isBool || isDuration) {\n const providerKey = PROVIDER_PARAMS[provider]?.[\"cache\"] ?? \"cache\";\n if (options.verbose) {\n changes.push({\n from: \"cache\",\n to: providerKey,\n value: cacheValue,\n reason: `cache=${value} → ${providerKey}=${cacheValue} for ${provider}`,\n });\n }\n params[providerKey] = cacheValue;\n\n // Emit cache_ttl when a duration is specified\n if (isDuration && CACHE_TTLS[provider]) {\n if (options.verbose) {\n changes.push({\n from: \"cache\",\n to: \"cache_ttl\",\n value,\n reason: `cache=${value} → cache_ttl=${value} for ${provider}`,\n });\n }\n params[\"cache_ttl\"] = value;\n }\n continue;\n }\n }\n\n // Step 3: Map canonical → provider-specific param name\n if (provider && PROVIDER_PARAMS[provider]) {\n const providerKey = PROVIDER_PARAMS[provider][key];\n if (providerKey && providerKey !== key) {\n if (options.verbose) {\n changes.push({\n from: key,\n to: providerKey,\n value,\n reason: `${provider} uses \"${providerKey}\" instead of \"${key}\"`,\n });\n }\n key = providerKey;\n }\n }\n\n // Step 4: OpenAI reasoning model adjustments (direct or via gateway)\n if (\n provider &&\n canHostOpenAIModels(provider) &&\n isReasoningModel(config.model) &&\n key === \"max_tokens\"\n ) {\n if (options.verbose) {\n changes.push({\n from: \"max_tokens\",\n to: \"max_completion_tokens\",\n value,\n reason:\n \"OpenAI reasoning models use max_completion_tokens instead of max_tokens\",\n });\n }\n key = \"max_completion_tokens\";\n }\n\n params[key] = value;\n }\n\n return {\n config: { ...config, params },\n provider,\n subProvider,\n changes,\n };\n}\n","export type Provider =\n | \"openai\"\n | \"anthropic\"\n | \"google\"\n | \"mistral\"\n | \"cohere\"\n | \"bedrock\"\n | \"openrouter\"\n | \"vercel\";\n\nexport type HostAlias =\n | Provider\n | \"aistudio\"\n | \"alibaba\"\n | \"alibabacloud\"\n | \"atlascloud\"\n | \"baidu\"\n | \"dashscope\"\n | \"deepinfra\"\n | \"fireworks\"\n | \"fireworksai\"\n | \"grok\"\n | \"minimax\"\n | \"novita\"\n | \"novitaai\"\n | \"parasail\"\n | \"qianfan\"\n | \"vertex\"\n | \"venice\"\n | \"wandb\"\n | \"weightsandbiases\"\n | \"xai\"\n | \"xiaomi\";\n\ntype Env = Record<string, string | undefined>;\n\ndeclare const process:\n | {\n env?: Env;\n }\n | undefined;\n\nfunction hasOwn<T extends object>(object: T, key: PropertyKey): key is keyof T {\n return Object.prototype.hasOwnProperty.call(object, key);\n}\n\nexport const HOST_ALIASES: Record<HostAlias, string> = {\n openai: \"api.openai.com\",\n anthropic: \"api.anthropic.com\",\n google: \"generativelanguage.googleapis.com\",\n aistudio: \"generativelanguage.googleapis.com\",\n mistral: \"api.mistral.ai\",\n cohere: \"api.cohere.com\",\n bedrock: \"bedrock-runtime.us-east-1.amazonaws.com\",\n openrouter: \"openrouter.ai\",\n vercel: \"gateway.ai.vercel.app\",\n alibaba: \"dashscope-intl.aliyuncs.com\",\n alibabacloud: \"dashscope-intl.aliyuncs.com\",\n dashscope: \"dashscope-intl.aliyuncs.com\",\n fireworks: \"api.fireworks.ai\",\n fireworksai: \"api.fireworks.ai\",\n venice: \"api.venice.ai\",\n parasail: \"api.parasail.io\",\n deepinfra: \"api.deepinfra.com\",\n atlascloud: \"api.atlascloud.ai\",\n novita: \"api.novita.ai\",\n novitaai: \"api.novita.ai\",\n grok: \"api.x.ai\",\n xai: \"api.x.ai\",\n wandb: \"api.inference.wandb.ai\",\n weightsandbiases: \"api.inference.wandb.ai\",\n baidu: \"qianfan.baidubce.com\",\n qianfan: \"qianfan.baidubce.com\",\n vertex: \"aiplatform.googleapis.com\",\n xiaomi: \"api.xiaomimimo.com\",\n minimax: \"api.minimax.io\",\n};\n\nexport interface HostResolution {\n /** The hostname/host to use for requests. */\n host: string;\n /** The provider alias that was expanded, if any. */\n alias?: HostAlias;\n}\n\nfunction readProcessEnv(): Env {\n return typeof process !== \"undefined\" && process.env ? process.env : {};\n}\n\nfunction normalizeHostValue(value: string): string {\n const trimmed = value.trim();\n if (!trimmed) return trimmed;\n\n try {\n if (trimmed.includes(\"://\")) {\n return new URL(trimmed).host;\n }\n } catch {\n // Fall through and treat it as a host-ish string.\n }\n\n return trimmed.replace(/^\\/\\//, \"\").split(\"/\")[0] ?? trimmed;\n}\n\nfunction envHostOverride(alias: HostAlias, env: Env): string | undefined {\n const upper = alias.toUpperCase();\n const override =\n env[`LLM_STRINGS_${upper}_HOST`] ?? env[`LLM_STRINGS_HOST_${upper}`];\n return override?.trim() ? override : undefined;\n}\n\n/**\n * Resolve short provider host aliases (`openai`, `anthropic`, etc.) to their\n * canonical hostnames. Per-provider environment overrides can redirect aliases\n * to regional or private endpoints:\n *\n * - `LLM_STRINGS_OPENAI_HOST`\n * - `LLM_STRINGS_HOST_OPENAI`\n */\nexport function resolveHostAlias(\n host: string,\n env: Env = readProcessEnv(),\n): HostResolution {\n const normalizedHost = host.toLowerCase();\n if (!hasOwn(HOST_ALIASES, normalizedHost)) {\n return { host };\n }\n\n const alias = normalizedHost as HostAlias;\n const override = envHostOverride(alias, env);\n return {\n host: normalizeHostValue(override ?? HOST_ALIASES[alias]),\n alias,\n };\n}\n\nexport function providerFromHostAlias(alias: string): Provider | undefined {\n const normalizedAlias = alias.toLowerCase();\n if (hasOwn(PROVIDER_PARAMS, normalizedAlias)) {\n return normalizedAlias as Provider;\n }\n return undefined;\n}\n\nexport function detectProvider(host: string): Provider | undefined {\n host = host.toLowerCase();\n\n // Gateways and aggregators first — they proxy to other providers\n if (host.includes(\"openrouter\")) return \"openrouter\";\n if (host.includes(\"gateway.ai.vercel\")) return \"vercel\";\n // Bedrock before native providers since it hosts models from multiple vendors\n if (host.includes(\"amazonaws\") || host.includes(\"bedrock\")) return \"bedrock\";\n if (host.includes(\"openai\")) return \"openai\";\n if (host.includes(\"anthropic\") || host.includes(\"claude\")) return \"anthropic\";\n if (host.includes(\"googleapis\") || host.includes(\"google\")) return \"google\";\n if (host.includes(\"mistral\")) return \"mistral\";\n if (host.includes(\"cohere\")) return \"cohere\";\n return undefined;\n}\n\n/**\n * Shorthand aliases → canonical param name.\n * Canonical names use snake_case and follow OpenAI conventions where possible.\n */\nexport const ALIASES: Record<string, string> = {\n // temperature\n temp: \"temperature\",\n\n // max_tokens\n max: \"max_tokens\",\n max_out: \"max_tokens\",\n max_output: \"max_tokens\",\n max_output_tokens: \"max_tokens\",\n max_completion_tokens: \"max_tokens\",\n maxOutputTokens: \"max_tokens\",\n maxTokens: \"max_tokens\",\n\n // top_p\n topp: \"top_p\",\n topP: \"top_p\",\n nucleus: \"top_p\",\n\n // top_k\n topk: \"top_k\",\n topK: \"top_k\",\n\n // frequency_penalty\n freq: \"frequency_penalty\",\n freq_penalty: \"frequency_penalty\",\n frequencyPenalty: \"frequency_penalty\",\n repetition_penalty: \"frequency_penalty\",\n\n // presence_penalty\n pres: \"presence_penalty\",\n pres_penalty: \"presence_penalty\",\n presencePenalty: \"presence_penalty\",\n\n // stop\n stop_sequences: \"stop\",\n stopSequences: \"stop\",\n stop_sequence: \"stop\",\n\n // seed\n random_seed: \"seed\",\n randomSeed: \"seed\",\n\n // n (completions count)\n candidateCount: \"n\",\n candidate_count: \"n\",\n num_completions: \"n\",\n\n // effort / reasoning\n reasoning_effort: \"effort\",\n reasoning: \"effort\",\n\n // cache\n cache_control: \"cache\",\n cacheControl: \"cache\",\n cachePoint: \"cache\",\n cache_point: \"cache\",\n};\n\n/**\n * Canonical param name → provider-specific API param name.\n * Only includes params the provider actually supports.\n */\nexport const PROVIDER_PARAMS: Record<Provider, Record<string, string>> = {\n openai: {\n temperature: \"temperature\",\n max_tokens: \"max_tokens\",\n top_p: \"top_p\",\n frequency_penalty: \"frequency_penalty\",\n presence_penalty: \"presence_penalty\",\n stop: \"stop\",\n n: \"n\",\n seed: \"seed\",\n stream: \"stream\",\n effort: \"reasoning_effort\",\n },\n anthropic: {\n temperature: \"temperature\",\n max_tokens: \"max_tokens\",\n top_p: \"top_p\",\n top_k: \"top_k\",\n stop: \"stop_sequences\",\n stream: \"stream\",\n effort: \"effort\",\n cache: \"cache_control\",\n cache_ttl: \"cache_ttl\",\n },\n google: {\n temperature: \"temperature\",\n max_tokens: \"maxOutputTokens\",\n top_p: \"topP\",\n top_k: \"topK\",\n frequency_penalty: \"frequencyPenalty\",\n presence_penalty: \"presencePenalty\",\n stop: \"stopSequences\",\n n: \"candidateCount\",\n stream: \"stream\",\n seed: \"seed\",\n responseMimeType: \"responseMimeType\",\n responseSchema: \"responseSchema\",\n },\n mistral: {\n temperature: \"temperature\",\n max_tokens: \"max_tokens\",\n top_p: \"top_p\",\n frequency_penalty: \"frequency_penalty\",\n presence_penalty: \"presence_penalty\",\n stop: \"stop\",\n n: \"n\",\n seed: \"random_seed\",\n stream: \"stream\",\n safe_prompt: \"safe_prompt\",\n min_tokens: \"min_tokens\",\n },\n cohere: {\n temperature: \"temperature\",\n max_tokens: \"max_tokens\",\n top_p: \"p\",\n top_k: \"k\",\n frequency_penalty: \"frequency_penalty\",\n presence_penalty: \"presence_penalty\",\n stop: \"stop_sequences\",\n stream: \"stream\",\n seed: \"seed\",\n },\n bedrock: {\n // Bedrock Converse API uses camelCase\n temperature: \"temperature\",\n max_tokens: \"maxTokens\",\n top_p: \"topP\",\n top_k: \"topK\", // Claude models via additionalModelRequestFields\n stop: \"stopSequences\",\n stream: \"stream\",\n cache: \"cache_control\",\n cache_ttl: \"cache_ttl\",\n },\n openrouter: {\n // OpenAI-compatible API with extra routing params\n temperature: \"temperature\",\n max_tokens: \"max_tokens\",\n top_p: \"top_p\",\n top_k: \"top_k\",\n frequency_penalty: \"frequency_penalty\",\n presence_penalty: \"presence_penalty\",\n stop: \"stop\",\n n: \"n\",\n seed: \"seed\",\n stream: \"stream\",\n effort: \"reasoning_effort\",\n },\n vercel: {\n // OpenAI-compatible gateway\n temperature: \"temperature\",\n max_tokens: \"max_tokens\",\n top_p: \"top_p\",\n top_k: \"top_k\",\n frequency_penalty: \"frequency_penalty\",\n presence_penalty: \"presence_penalty\",\n stop: \"stop\",\n n: \"n\",\n seed: \"seed\",\n stream: \"stream\",\n effort: \"reasoning_effort\",\n },\n};\n\n/**\n * Validation specs per provider, keyed by provider-specific param name.\n */\nexport interface ParamSpec {\n type: \"number\" | \"string\" | \"boolean\";\n min?: number;\n max?: number;\n values?: string[];\n default?: string | number | boolean;\n description?: string;\n}\n\nexport const PARAM_SPECS: Record<Provider, Record<string, ParamSpec>> = {\n openai: {\n temperature: {\n type: \"number\",\n min: 0,\n max: 2,\n default: 0.7,\n description: \"Controls randomness\",\n },\n max_tokens: {\n type: \"number\",\n min: 1,\n default: 4096,\n description: \"Maximum output tokens\",\n },\n top_p: {\n type: \"number\",\n min: 0,\n max: 1,\n default: 1,\n description: \"Nucleus sampling\",\n },\n frequency_penalty: {\n type: \"number\",\n min: -2,\n max: 2,\n default: 0,\n description: \"Penalize frequent tokens\",\n },\n presence_penalty: {\n type: \"number\",\n min: -2,\n max: 2,\n default: 0,\n description: \"Penalize repeated topics\",\n },\n stop: { type: \"string\", description: \"Stop sequences\" },\n n: { type: \"number\", min: 1, default: 1, description: \"Completions count\" },\n seed: { type: \"number\", description: \"Random seed\" },\n stream: { type: \"boolean\", default: false, description: \"Stream response\" },\n reasoning_effort: {\n type: \"string\",\n values: [\"none\", \"minimal\", \"low\", \"medium\", \"high\", \"xhigh\"],\n default: \"medium\",\n description: \"Reasoning effort\",\n },\n },\n anthropic: {\n temperature: {\n type: \"number\",\n min: 0,\n max: 1,\n default: 0.7,\n description: \"Controls randomness\",\n },\n max_tokens: {\n type: \"number\",\n min: 1,\n default: 4096,\n description: \"Maximum output tokens\",\n },\n top_p: {\n type: \"number\",\n min: 0,\n max: 1,\n default: 1,\n description: \"Nucleus sampling\",\n },\n top_k: {\n type: \"number\",\n min: 0,\n default: 40,\n description: \"Top-K sampling\",\n },\n stop_sequences: { type: \"string\", description: \"Stop sequences\" },\n stream: { type: \"boolean\", default: false, description: \"Stream response\" },\n effort: {\n type: \"string\",\n values: [\"low\", \"medium\", \"high\", \"max\"],\n default: \"medium\",\n description: \"Thinking effort\",\n },\n cache_control: {\n type: \"string\",\n values: [\"ephemeral\"],\n default: \"ephemeral\",\n description: \"Cache control\",\n },\n cache_ttl: {\n type: \"string\",\n values: [\"5m\", \"1h\"],\n default: \"5m\",\n description: \"Cache TTL\",\n },\n },\n google: {\n temperature: {\n type: \"number\",\n min: 0,\n max: 2,\n default: 0.7,\n description: \"Controls randomness\",\n },\n maxOutputTokens: {\n type: \"number\",\n min: 1,\n default: 4096,\n description: \"Maximum output tokens\",\n },\n topP: {\n type: \"number\",\n min: 0,\n max: 1,\n default: 1,\n description: \"Nucleus sampling\",\n },\n topK: {\n type: \"number\",\n min: 0,\n default: 40,\n description: \"Top-K sampling\",\n },\n frequencyPenalty: {\n type: \"number\",\n min: -2,\n max: 2,\n default: 0,\n description: \"Penalize frequent tokens\",\n },\n presencePenalty: {\n type: \"number\",\n min: -2,\n max: 2,\n default: 0,\n description: \"Penalize repeated topics\",\n },\n stopSequences: { type: \"string\", description: \"Stop sequences\" },\n candidateCount: {\n type: \"number\",\n min: 1,\n default: 1,\n description: \"Candidate count\",\n },\n stream: { type: \"boolean\", default: false, description: \"Stream response\" },\n seed: { type: \"number\", description: \"Random seed\" },\n responseMimeType: { type: \"string\", description: \"Response MIME type\" },\n responseSchema: { type: \"string\", description: \"Response schema\" },\n },\n mistral: {\n temperature: {\n type: \"number\",\n min: 0,\n max: 1,\n default: 0.7,\n description: \"Controls randomness\",\n },\n max_tokens: {\n type: \"number\",\n min: 1,\n default: 4096,\n description: \"Maximum output tokens\",\n },\n top_p: {\n type: \"number\",\n min: 0,\n max: 1,\n default: 1,\n description: \"Nucleus sampling\",\n },\n frequency_penalty: {\n type: \"number\",\n min: -2,\n max: 2,\n default: 0,\n description: \"Penalize frequent tokens\",\n },\n presence_penalty: {\n type: \"number\",\n min: -2,\n max: 2,\n default: 0,\n description: \"Penalize repeated topics\",\n },\n stop: { type: \"string\", description: \"Stop sequences\" },\n n: { type: \"number\", min: 1, default: 1, description: \"Completions count\" },\n random_seed: { type: \"number\", description: \"Random seed\" },\n stream: { type: \"boolean\", default: false, description: \"Stream response\" },\n safe_prompt: {\n type: \"boolean\",\n default: false,\n description: \"Enable safe prompt\",\n },\n min_tokens: {\n type: \"number\",\n min: 0,\n default: 0,\n description: \"Minimum tokens\",\n },\n },\n cohere: {\n temperature: {\n type: \"number\",\n min: 0,\n max: 1,\n default: 0.7,\n description: \"Controls randomness\",\n },\n max_tokens: {\n type: \"number\",\n min: 1,\n default: 4096,\n description: \"Maximum output tokens\",\n },\n p: {\n type: \"number\",\n min: 0,\n max: 1,\n default: 1,\n description: \"Nucleus sampling (p)\",\n },\n k: {\n type: \"number\",\n min: 0,\n max: 500,\n default: 40,\n description: \"Top-K sampling (k)\",\n },\n frequency_penalty: {\n type: \"number\",\n min: 0,\n max: 1,\n default: 0,\n description: \"Penalize frequent tokens\",\n },\n presence_penalty: {\n type: \"number\",\n min: 0,\n max: 1,\n default: 0,\n description: \"Penalize repeated topics\",\n },\n stop_sequences: { type: \"string\", description: \"Stop sequences\" },\n stream: { type: \"boolean\", default: false, description: \"Stream response\" },\n seed: { type: \"number\", description: \"Random seed\" },\n },\n bedrock: {\n // Converse API inferenceConfig params\n temperature: {\n type: \"number\",\n min: 0,\n max: 1,\n default: 0.7,\n description: \"Controls randomness\",\n },\n maxTokens: {\n type: \"number\",\n min: 1,\n default: 4096,\n description: \"Maximum output tokens\",\n },\n topP: {\n type: \"number\",\n min: 0,\n max: 1,\n default: 1,\n description: \"Nucleus sampling\",\n },\n topK: {\n type: \"number\",\n min: 0,\n default: 40,\n description: \"Top-K sampling\",\n },\n stopSequences: { type: \"string\", description: \"Stop sequences\" },\n stream: { type: \"boolean\", default: false, description: \"Stream response\" },\n cache_control: {\n type: \"string\",\n values: [\"ephemeral\"],\n default: \"ephemeral\",\n description: \"Cache control\",\n },\n cache_ttl: {\n type: \"string\",\n values: [\"5m\", \"1h\"],\n default: \"5m\",\n description: \"Cache TTL\",\n },\n },\n openrouter: {\n // Loose validation — proxies to many providers with varying ranges\n temperature: {\n type: \"number\",\n min: 0,\n max: 2,\n default: 0.7,\n description: \"Controls randomness\",\n },\n max_tokens: {\n type: \"number\",\n min: 1,\n default: 4096,\n description: \"Maximum output tokens\",\n },\n top_p: {\n type: \"number\",\n min: 0,\n max: 1,\n default: 1,\n description: \"Nucleus sampling\",\n },\n top_k: {\n type: \"number\",\n min: 0,\n default: 40,\n description: \"Top-K sampling\",\n },\n frequency_penalty: {\n type: \"number\",\n min: -2,\n max: 2,\n default: 0,\n description: \"Penalize frequent tokens\",\n },\n presence_penalty: {\n type: \"number\",\n min: -2,\n max: 2,\n default: 0,\n description: \"Penalize repeated topics\",\n },\n stop: { type: \"string\", description: \"Stop sequences\" },\n n: { type: \"number\", min: 1, default: 1, description: \"Completions count\" },\n seed: { type: \"number\", description: \"Random seed\" },\n stream: { type: \"boolean\", default: false, description: \"Stream response\" },\n reasoning_effort: {\n type: \"string\",\n values: [\"none\", \"minimal\", \"low\", \"medium\", \"high\", \"xhigh\"],\n default: \"medium\",\n description: \"Reasoning effort\",\n },\n },\n vercel: {\n // Loose validation — proxies to many providers with varying ranges\n temperature: {\n type: \"number\",\n min: 0,\n max: 2,\n default: 0.7,\n description: \"Controls randomness\",\n },\n max_tokens: {\n type: \"number\",\n min: 1,\n default: 4096,\n description: \"Maximum output tokens\",\n },\n top_p: {\n type: \"number\",\n min: 0,\n max: 1,\n default: 1,\n description: \"Nucleus sampling\",\n },\n top_k: {\n type: \"number\",\n min: 0,\n default: 40,\n description: \"Top-K sampling\",\n },\n frequency_penalty: {\n type: \"number\",\n min: -2,\n max: 2,\n default: 0,\n description: \"Penalize frequent tokens\",\n },\n presence_penalty: {\n type: \"number\",\n min: -2,\n max: 2,\n default: 0,\n description: \"Penalize repeated topics\",\n },\n stop: { type: \"string\", description: \"Stop sequences\" },\n n: { type: \"number\", min: 1, default: 1, description: \"Completions count\" },\n seed: { type: \"number\", description: \"Random seed\" },\n stream: { type: \"boolean\", default: false, description: \"Stream response\" },\n reasoning_effort: {\n type: \"string\",\n values: [\"none\", \"minimal\", \"low\", \"medium\", \"high\", \"xhigh\"],\n default: \"medium\",\n description: \"Reasoning effort\",\n },\n },\n};\n\n/** OpenAI reasoning models don't support standard sampling params. */\nexport function isReasoningModel(model: string): boolean {\n // Strip gateway prefix: \"openai/o3\" → \"o3\"\n const name = model.includes(\"/\") ? model.split(\"/\").pop()! : model;\n return /^o[134]/.test(name);\n}\n\n/** Providers that can route to OpenAI models (and need reasoning-model checks). */\nexport function canHostOpenAIModels(provider: Provider): boolean {\n return (\n provider === \"openai\" || provider === \"openrouter\" || provider === \"vercel\"\n );\n}\n\n/** Whether this provider is a gateway/router that proxies to other providers. */\nexport function isGatewayProvider(provider: Provider): boolean {\n return provider === \"openrouter\" || provider === \"vercel\";\n}\n\n/**\n * Extract the underlying provider from a gateway model string.\n * e.g. \"anthropic/claude-sonnet-4-5\" → \"anthropic\"\n * Returns undefined for unknown prefixes (qwen, deepseek, etc.) or models without \"/\".\n */\nexport function detectGatewaySubProvider(model: string): Provider | undefined {\n const slash = model.indexOf(\"/\");\n if (slash < 1) return undefined;\n const prefix = model.slice(0, slash);\n const direct: Provider[] = [\n \"openai\",\n \"anthropic\",\n \"google\",\n \"mistral\",\n \"cohere\",\n ];\n return direct.find((p) => p === prefix);\n}\n\nexport const REASONING_MODEL_UNSUPPORTED = new Set([\n \"temperature\",\n \"top_p\",\n \"frequency_penalty\",\n \"presence_penalty\",\n \"n\",\n]);\n\n/**\n * Bedrock model IDs are prefixed with the vendor name.\n * e.g. \"anthropic.claude-sonnet-4-5-20250929-v1:0\"\n */\nexport type BedrockModelFamily =\n | \"anthropic\"\n | \"meta\"\n | \"amazon\"\n | \"mistral\"\n | \"cohere\"\n | \"ai21\";\n\nexport function detectBedrockModelFamily(\n model: string,\n): BedrockModelFamily | undefined {\n // Handle cross-region inference profiles (e.g. \"us.anthropic.claude-sonnet-4-5...\")\n // and global inference profiles (e.g. \"global.anthropic.claude-sonnet-4-5...\")\n const parts = model.split(\".\");\n\n // If first part is a region prefix (us, eu, apac) or global, skip it\n let prefix = parts[0];\n if ([\"us\", \"eu\", \"apac\", \"global\"].includes(prefix) && parts.length > 1) {\n prefix = parts[1];\n }\n\n const families: BedrockModelFamily[] = [\n \"anthropic\",\n \"meta\",\n \"amazon\",\n \"mistral\",\n \"cohere\",\n \"ai21\",\n ];\n return families.find((f) => prefix === f);\n}\n\n/** Whether a Bedrock model supports prompt caching (Claude and Nova only). */\nexport function bedrockSupportsCaching(model: string): boolean {\n const family = detectBedrockModelFamily(model);\n if (family === \"anthropic\") return true;\n if (family === \"amazon\" && model.includes(\"nova\")) return true;\n return false;\n}\n\n/** Cache value normalization per provider. */\nexport const CACHE_VALUES: Record<Provider, string | undefined> = {\n openai: undefined, // OpenAI auto-caches; no explicit param\n anthropic: \"ephemeral\",\n google: undefined, // Google uses explicit caching API, not a param\n mistral: undefined,\n cohere: undefined,\n bedrock: \"ephemeral\", // Supported for Claude models on Bedrock\n openrouter: undefined, // Depends on underlying provider\n vercel: undefined, // Depends on underlying provider\n};\n\n/** Valid cache TTL values per provider. */\nexport const CACHE_TTLS: Record<Provider, string[] | undefined> = {\n openai: undefined,\n anthropic: [\"5m\", \"1h\"],\n google: undefined,\n mistral: undefined,\n cohere: undefined,\n bedrock: [\"5m\", \"1h\"], // Claude on Bedrock uses same TTLs as direct Anthropic\n openrouter: undefined,\n vercel: undefined,\n};\n\n/** Match a duration expression like \"5m\", \"1h\", \"30m\". */\nexport const DURATION_RE = /^\\d+[mh]$/;\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/normalize.ts","../src/provider-core.ts"],"sourcesContent":["import type { LlmConnectionConfig } from \"./parse.js\";\nimport {\n ALIASES,\n CACHE_TTLS,\n CACHE_VALUES,\n DURATION_RE,\n PROVIDER_PARAMS,\n bedrockSupportsCaching,\n canHostOpenAIModels,\n detectGatewaySubProvider,\n detectProvider,\n isGatewayProvider,\n isReasoningModel,\n providerFromHostAlias,\n type Provider,\n} from \"./provider-core.js\";\n\nexport interface NormalizeChange {\n from: string;\n to: string;\n value: string;\n reason: string;\n}\n\nexport interface NormalizeResult {\n config: LlmConnectionConfig;\n provider: Provider | undefined;\n /** Underlying provider extracted from gateway model prefix (e.g. \"anthropic\" from \"anthropic/claude-sonnet-4-5\"). */\n subProvider: Provider | undefined;\n changes: NormalizeChange[];\n}\n\nexport interface NormalizeOptions {\n /** Include detailed change log in the result. */\n verbose?: boolean;\n}\n\n/**\n * Normalize an LLM connection config's params for its target provider.\n *\n * 1. Expands shorthand aliases (e.g. `temp` → `temperature`)\n * 2. Maps canonical param names to provider-specific names\n * (e.g. `max_tokens` → `maxOutputTokens` for Google)\n * 3. Normalizes special values (e.g. `cache=true` → `cache_control=ephemeral` for Anthropic)\n * 4. For OpenAI reasoning models, remaps `max_tokens` → `max_completion_tokens`\n * and warns about unsupported sampling params\n */\nexport function normalize(\n config: LlmConnectionConfig,\n options: NormalizeOptions = {},\n): NormalizeResult {\n const provider =\n (config.hostAlias ? providerFromHostAlias(config.hostAlias) : undefined) ??\n detectProvider(config.host);\n const subProvider =\n provider && isGatewayProvider(provider)\n ? detectGatewaySubProvider(config.model)\n : undefined;\n const changes: NormalizeChange[] = [];\n const params: Record<string, string> = {};\n\n for (const [rawKey, value] of Object.entries(config.params)) {\n let key = rawKey;\n\n // Step 1: Expand aliases to canonical name\n if (ALIASES[key]) {\n const canonical = ALIASES[key];\n if (options.verbose) {\n changes.push({\n from: key,\n to: canonical,\n value,\n reason: `alias: \"${key}\" → \"${canonical}\"`,\n });\n }\n key = canonical;\n }\n\n // Step 2: Handle special \"cache\" param\n if (key === \"cache\" && provider) {\n let cacheValue = CACHE_VALUES[provider];\n\n // Bedrock supports cache for Anthropic Claude and Amazon Nova models\n if (provider === \"bedrock\" && !bedrockSupportsCaching(config.model)) {\n cacheValue = undefined;\n }\n\n // Provider/model doesn't support cache — drop it\n if (!cacheValue) {\n if (options.verbose) {\n changes.push({\n from: \"cache\",\n to: \"(dropped)\",\n value,\n reason: `${provider} does not use a cache param for this model (caching is automatic or unsupported)`,\n });\n }\n continue;\n }\n\n const isBool = value === \"true\" || value === \"1\" || value === \"yes\";\n const isDuration = DURATION_RE.test(value);\n\n if (isBool || isDuration) {\n const providerKey = PROVIDER_PARAMS[provider]?.[\"cache\"] ?? \"cache\";\n if (options.verbose) {\n changes.push({\n from: \"cache\",\n to: providerKey,\n value: cacheValue,\n reason: `cache=${value} → ${providerKey}=${cacheValue} for ${provider}`,\n });\n }\n params[providerKey] = cacheValue;\n\n // Emit cache_ttl when a duration is specified\n if (isDuration && CACHE_TTLS[provider]) {\n if (options.verbose) {\n changes.push({\n from: \"cache\",\n to: \"cache_ttl\",\n value,\n reason: `cache=${value} → cache_ttl=${value} for ${provider}`,\n });\n }\n params[\"cache_ttl\"] = value;\n }\n continue;\n }\n }\n\n // Step 3: Map canonical → provider-specific param name\n if (provider && PROVIDER_PARAMS[provider]) {\n const providerKey = PROVIDER_PARAMS[provider][key];\n if (providerKey && providerKey !== key) {\n if (options.verbose) {\n changes.push({\n from: key,\n to: providerKey,\n value,\n reason: `${provider} uses \"${providerKey}\" instead of \"${key}\"`,\n });\n }\n key = providerKey;\n }\n }\n\n // Step 4: OpenAI reasoning model adjustments (direct or via gateway)\n if (\n provider &&\n canHostOpenAIModels(provider) &&\n isReasoningModel(config.model) &&\n key === \"max_tokens\"\n ) {\n if (options.verbose) {\n changes.push({\n from: \"max_tokens\",\n to: \"max_completion_tokens\",\n value,\n reason:\n \"OpenAI reasoning models use max_completion_tokens instead of max_tokens\",\n });\n }\n key = \"max_completion_tokens\";\n }\n\n params[key] = value;\n }\n\n return {\n config: { ...config, params },\n provider,\n subProvider,\n changes,\n };\n}\n","export type Provider =\n | \"openai\"\n | \"azure\"\n | \"anthropic\"\n | \"google\"\n | \"google-vertex\"\n | \"mistral\"\n | \"cohere\"\n | \"bedrock\"\n | \"openrouter\"\n | \"vercel\"\n | \"xai\"\n | \"groq\"\n | \"fal\"\n | \"deepinfra\"\n | \"black-forest-labs\"\n | \"together\"\n | \"fireworks\"\n | \"deepseek\"\n | \"moonshotai\"\n | \"perplexity\"\n | \"alibaba\"\n | \"cerebras\"\n | \"replicate\"\n | \"prodia\"\n | \"luma\"\n | \"bytedance\"\n | \"kling\"\n | \"elevenlabs\"\n | \"assemblyai\"\n | \"deepgram\"\n | \"gladia\"\n | \"lmnt\"\n | \"hume\"\n | \"revai\"\n | \"baseten\"\n | \"huggingface\";\n\nexport type HostAlias =\n | Provider\n | \"aistudio\"\n | \"alibaba\"\n | \"alibabacloud\"\n | \"atlascloud\"\n | \"baidu\"\n | \"cerebras\"\n | \"dashscope\"\n | \"deepinfra\"\n | \"deepseek\"\n | \"fireworks\"\n | \"fireworksai\"\n | \"groq\"\n | \"grok\"\n | \"bfl\"\n | \"minimax\"\n | \"moonshot\"\n | \"novita\"\n | \"novitaai\"\n | \"parasail\"\n | \"perplexity\"\n | \"qianfan\"\n | \"together\"\n | \"togetherai\"\n | \"vertex\"\n | \"venice\"\n | \"wandb\"\n | \"weightsandbiases\"\n | \"xai\"\n | \"xiaomi\";\n\ntype Env = Record<string, string | undefined>;\n\ndeclare const process:\n | {\n env?: Env;\n }\n | undefined;\n\nfunction hasOwn<T extends object>(object: T, key: PropertyKey): key is keyof T {\n return Object.prototype.hasOwnProperty.call(object, key);\n}\n\nexport const HOST_ALIASES: Record<HostAlias, string> = {\n openai: \"api.openai.com\",\n azure: \"models.inference.ai.azure.com\",\n anthropic: \"api.anthropic.com\",\n google: \"generativelanguage.googleapis.com\",\n \"google-vertex\": \"aiplatform.googleapis.com\",\n aistudio: \"generativelanguage.googleapis.com\",\n mistral: \"api.mistral.ai\",\n cohere: \"api.cohere.com\",\n bedrock: \"bedrock-runtime.us-east-1.amazonaws.com\",\n openrouter: \"openrouter.ai\",\n vercel: \"gateway.ai.vercel.app\",\n alibaba: \"dashscope-intl.aliyuncs.com\",\n alibabacloud: \"dashscope-intl.aliyuncs.com\",\n dashscope: \"dashscope-intl.aliyuncs.com\",\n groq: \"api.groq.com\",\n fal: \"fal.run\",\n fireworks: \"api.fireworks.ai\",\n fireworksai: \"api.fireworks.ai\",\n \"black-forest-labs\": \"api.bfl.ai\",\n bfl: \"api.bfl.ai\",\n deepseek: \"api.deepseek.com\",\n moonshotai: \"api.moonshot.ai\",\n moonshot: \"api.moonshot.ai\",\n perplexity: \"api.perplexity.ai\",\n venice: \"api.venice.ai\",\n parasail: \"api.parasail.io\",\n deepinfra: \"api.deepinfra.com\",\n atlascloud: \"api.atlascloud.ai\",\n novita: \"api.novita.ai\",\n novitaai: \"api.novita.ai\",\n grok: \"api.x.ai\",\n xai: \"api.x.ai\",\n together: \"api.together.xyz\",\n togetherai: \"api.together.xyz\",\n cerebras: \"api.cerebras.ai\",\n replicate: \"api.replicate.com\",\n prodia: \"api.prodia.com\",\n luma: \"api.lumalabs.ai\",\n bytedance: \"ark.cn-beijing.volces.com\",\n kling: \"api.klingai.com\",\n elevenlabs: \"api.elevenlabs.io\",\n assemblyai: \"api.assemblyai.com\",\n deepgram: \"api.deepgram.com\",\n gladia: \"api.gladia.io\",\n lmnt: \"api.lmnt.com\",\n hume: \"api.hume.ai\",\n revai: \"api.rev.ai\",\n baseten: \"api.baseten.co\",\n huggingface: \"api-inference.huggingface.co\",\n wandb: \"api.inference.wandb.ai\",\n weightsandbiases: \"api.inference.wandb.ai\",\n baidu: \"qianfan.baidubce.com\",\n qianfan: \"qianfan.baidubce.com\",\n vertex: \"aiplatform.googleapis.com\",\n xiaomi: \"api.xiaomimimo.com\",\n minimax: \"api.minimax.io\",\n};\n\nconst HOST_ALIAS_PROVIDERS: Partial<Record<HostAlias, Provider>> = {\n aistudio: \"google\",\n vertex: \"google-vertex\",\n grok: \"xai\",\n bfl: \"black-forest-labs\",\n moonshot: \"moonshotai\",\n alibaba: \"alibaba\",\n alibabacloud: \"alibaba\",\n dashscope: \"alibaba\",\n togetherai: \"together\",\n fireworksai: \"fireworks\",\n};\n\nexport interface HostResolution {\n /** The hostname/host to use for requests. */\n host: string;\n /** The provider alias that was expanded, if any. */\n alias?: HostAlias;\n}\n\nfunction readProcessEnv(): Env {\n return typeof process !== \"undefined\" && process.env ? process.env : {};\n}\n\nfunction normalizeHostValue(value: string): string {\n const trimmed = value.trim();\n if (!trimmed) return trimmed;\n\n try {\n if (trimmed.includes(\"://\")) {\n return new URL(trimmed).host;\n }\n } catch {\n // Fall through and treat it as a host-ish string.\n }\n\n return trimmed.replace(/^\\/\\//, \"\").split(\"/\")[0] ?? trimmed;\n}\n\nfunction envHostOverride(alias: HostAlias, env: Env): string | undefined {\n const upper = alias.toUpperCase();\n const override =\n env[`LLM_STRINGS_${upper}_HOST`] ?? env[`LLM_STRINGS_HOST_${upper}`];\n return override?.trim() ? override : undefined;\n}\n\n/**\n * Resolve short provider host aliases (`openai`, `anthropic`, etc.) to their\n * canonical hostnames. Per-provider environment overrides can redirect aliases\n * to regional or private endpoints:\n *\n * - `LLM_STRINGS_OPENAI_HOST`\n * - `LLM_STRINGS_HOST_OPENAI`\n */\nexport function resolveHostAlias(\n host: string,\n env: Env = readProcessEnv(),\n): HostResolution {\n const normalizedHost = host.toLowerCase();\n if (!hasOwn(HOST_ALIASES, normalizedHost)) {\n return { host };\n }\n\n const alias = normalizedHost as HostAlias;\n const override = envHostOverride(alias, env);\n return {\n host: normalizeHostValue(override ?? HOST_ALIASES[alias]),\n alias,\n };\n}\n\nexport function providerFromHostAlias(alias: string): Provider | undefined {\n const normalizedAlias = alias.toLowerCase();\n if (hasOwn(PROVIDER_PARAMS, normalizedAlias)) {\n return normalizedAlias as Provider;\n }\n if (hasOwn(HOST_ALIAS_PROVIDERS, normalizedAlias)) {\n return HOST_ALIAS_PROVIDERS[normalizedAlias as HostAlias];\n }\n return undefined;\n}\n\nexport function detectProvider(host: string): Provider | undefined {\n host = host.toLowerCase();\n\n // Gateways and aggregators first — they proxy to other providers\n if (host.includes(\"openrouter\")) return \"openrouter\";\n if (host.includes(\"gateway.ai.vercel\")) return \"vercel\";\n // Bedrock before native providers since it hosts models from multiple vendors\n if (host.includes(\"amazonaws\") || host.includes(\"bedrock\")) return \"bedrock\";\n if (host.includes(\"aiplatform.googleapis\")) return \"google-vertex\";\n if (host.includes(\"api.x.ai\")) return \"xai\";\n if (host.includes(\"groq\")) return \"groq\";\n if (host.includes(\"fal.run\") || host.includes(\"fal.ai\")) return \"fal\";\n if (host.includes(\"deepinfra\")) return \"deepinfra\";\n if (host.includes(\"bfl.ai\")) return \"black-forest-labs\";\n if (host.includes(\"together\")) return \"together\";\n if (host.includes(\"fireworks\")) return \"fireworks\";\n if (host.includes(\"deepseek\")) return \"deepseek\";\n if (host.includes(\"moonshot\")) return \"moonshotai\";\n if (host.includes(\"perplexity\")) return \"perplexity\";\n if (host.includes(\"dashscope\") || host.includes(\"aliyuncs\")) return \"alibaba\";\n if (host.includes(\"cerebras\")) return \"cerebras\";\n if (host.includes(\"replicate\")) return \"replicate\";\n if (host.includes(\"prodia\")) return \"prodia\";\n if (host.includes(\"lumalabs\") || host.includes(\"luma\")) return \"luma\";\n if (host.includes(\"volces\") || host.includes(\"bytedance\")) return \"bytedance\";\n if (host.includes(\"kling\")) return \"kling\";\n if (host.includes(\"elevenlabs\")) return \"elevenlabs\";\n if (host.includes(\"assemblyai\")) return \"assemblyai\";\n if (host.includes(\"deepgram\")) return \"deepgram\";\n if (host.includes(\"gladia\")) return \"gladia\";\n if (host.includes(\"lmnt\")) return \"lmnt\";\n if (host.includes(\"hume\")) return \"hume\";\n if (host.includes(\"rev.ai\")) return \"revai\";\n if (host.includes(\"baseten\")) return \"baseten\";\n if (host.includes(\"huggingface\")) return \"huggingface\";\n if (host.includes(\"azure\")) return \"azure\";\n if (host.includes(\"openai\")) return \"openai\";\n if (host.includes(\"anthropic\") || host.includes(\"claude\")) return \"anthropic\";\n if (host.includes(\"googleapis\") || host.includes(\"google\")) return \"google\";\n if (host.includes(\"mistral\")) return \"mistral\";\n if (host.includes(\"cohere\")) return \"cohere\";\n return undefined;\n}\n\n/**\n * Shorthand aliases → canonical param name.\n * Canonical names use snake_case and follow OpenAI conventions where possible.\n */\nexport const ALIASES: Record<string, string> = {\n // temperature\n temp: \"temperature\",\n\n // max_tokens\n max: \"max_tokens\",\n max_out: \"max_tokens\",\n max_output: \"max_tokens\",\n max_output_tokens: \"max_tokens\",\n max_completion_tokens: \"max_tokens\",\n maxOutputTokens: \"max_tokens\",\n maxTokens: \"max_tokens\",\n\n // top_p\n topp: \"top_p\",\n topP: \"top_p\",\n nucleus: \"top_p\",\n\n // top_k\n topk: \"top_k\",\n topK: \"top_k\",\n\n // frequency_penalty\n freq: \"frequency_penalty\",\n freq_penalty: \"frequency_penalty\",\n frequencyPenalty: \"frequency_penalty\",\n repetition_penalty: \"frequency_penalty\",\n\n // presence_penalty\n pres: \"presence_penalty\",\n pres_penalty: \"presence_penalty\",\n presencePenalty: \"presence_penalty\",\n\n // stop\n stop_sequences: \"stop\",\n stopSequences: \"stop\",\n stop_sequence: \"stop\",\n\n // seed\n random_seed: \"seed\",\n randomSeed: \"seed\",\n\n // n (completions count)\n candidateCount: \"n\",\n candidate_count: \"n\",\n num_completions: \"n\",\n\n // effort / reasoning\n reasoning_effort: \"effort\",\n reasoning: \"effort\",\n\n // cache\n cache_control: \"cache\",\n cacheControl: \"cache\",\n cachePoint: \"cache\",\n cache_point: \"cache\",\n};\n\nconst OPENAI_COMPATIBLE_PARAMS: Record<string, string> = {\n temperature: \"temperature\",\n max_tokens: \"max_tokens\",\n top_p: \"top_p\",\n top_k: \"top_k\",\n frequency_penalty: \"frequency_penalty\",\n presence_penalty: \"presence_penalty\",\n stop: \"stop\",\n n: \"n\",\n seed: \"seed\",\n stream: \"stream\",\n effort: \"reasoning_effort\",\n};\n\nconst GOOGLE_COMPATIBLE_PARAMS: Record<string, string> = {\n temperature: \"temperature\",\n max_tokens: \"maxOutputTokens\",\n top_p: \"topP\",\n top_k: \"topK\",\n frequency_penalty: \"frequencyPenalty\",\n presence_penalty: \"presencePenalty\",\n stop: \"stopSequences\",\n n: \"candidateCount\",\n stream: \"stream\",\n seed: \"seed\",\n responseMimeType: \"responseMimeType\",\n responseSchema: \"responseSchema\",\n};\n\n/**\n * Canonical param name → provider-specific API param name.\n * Only includes params the provider actually supports.\n */\nexport const PROVIDER_PARAMS: Record<Provider, Record<string, string>> = {\n openai: {\n temperature: \"temperature\",\n max_tokens: \"max_tokens\",\n top_p: \"top_p\",\n frequency_penalty: \"frequency_penalty\",\n presence_penalty: \"presence_penalty\",\n stop: \"stop\",\n n: \"n\",\n seed: \"seed\",\n stream: \"stream\",\n effort: \"reasoning_effort\",\n },\n azure: OPENAI_COMPATIBLE_PARAMS,\n anthropic: {\n temperature: \"temperature\",\n max_tokens: \"max_tokens\",\n top_p: \"top_p\",\n top_k: \"top_k\",\n stop: \"stop_sequences\",\n stream: \"stream\",\n effort: \"effort\",\n cache: \"cache_control\",\n cache_ttl: \"cache_ttl\",\n },\n google: {\n temperature: \"temperature\",\n max_tokens: \"maxOutputTokens\",\n top_p: \"topP\",\n top_k: \"topK\",\n frequency_penalty: \"frequencyPenalty\",\n presence_penalty: \"presencePenalty\",\n stop: \"stopSequences\",\n n: \"candidateCount\",\n stream: \"stream\",\n seed: \"seed\",\n responseMimeType: \"responseMimeType\",\n responseSchema: \"responseSchema\",\n },\n \"google-vertex\": GOOGLE_COMPATIBLE_PARAMS,\n mistral: {\n temperature: \"temperature\",\n max_tokens: \"max_tokens\",\n top_p: \"top_p\",\n frequency_penalty: \"frequency_penalty\",\n presence_penalty: \"presence_penalty\",\n stop: \"stop\",\n n: \"n\",\n seed: \"random_seed\",\n stream: \"stream\",\n safe_prompt: \"safe_prompt\",\n min_tokens: \"min_tokens\",\n },\n cohere: {\n temperature: \"temperature\",\n max_tokens: \"max_tokens\",\n top_p: \"p\",\n top_k: \"k\",\n frequency_penalty: \"frequency_penalty\",\n presence_penalty: \"presence_penalty\",\n stop: \"stop_sequences\",\n stream: \"stream\",\n seed: \"seed\",\n },\n bedrock: {\n // Bedrock Converse API uses camelCase\n temperature: \"temperature\",\n max_tokens: \"maxTokens\",\n top_p: \"topP\",\n top_k: \"topK\", // Claude models via additionalModelRequestFields\n stop: \"stopSequences\",\n stream: \"stream\",\n cache: \"cache_control\",\n cache_ttl: \"cache_ttl\",\n },\n openrouter: {\n // OpenAI-compatible API with extra routing params\n temperature: \"temperature\",\n max_tokens: \"max_tokens\",\n top_p: \"top_p\",\n top_k: \"top_k\",\n frequency_penalty: \"frequency_penalty\",\n presence_penalty: \"presence_penalty\",\n stop: \"stop\",\n n: \"n\",\n seed: \"seed\",\n stream: \"stream\",\n effort: \"reasoning_effort\",\n },\n vercel: {\n // OpenAI-compatible gateway\n temperature: \"temperature\",\n max_tokens: \"max_tokens\",\n top_p: \"top_p\",\n top_k: \"top_k\",\n frequency_penalty: \"frequency_penalty\",\n presence_penalty: \"presence_penalty\",\n stop: \"stop\",\n n: \"n\",\n seed: \"seed\",\n stream: \"stream\",\n effort: \"reasoning_effort\",\n },\n xai: OPENAI_COMPATIBLE_PARAMS,\n groq: OPENAI_COMPATIBLE_PARAMS,\n fal: {},\n deepinfra: OPENAI_COMPATIBLE_PARAMS,\n \"black-forest-labs\": {},\n together: OPENAI_COMPATIBLE_PARAMS,\n fireworks: OPENAI_COMPATIBLE_PARAMS,\n deepseek: OPENAI_COMPATIBLE_PARAMS,\n moonshotai: OPENAI_COMPATIBLE_PARAMS,\n perplexity: OPENAI_COMPATIBLE_PARAMS,\n alibaba: OPENAI_COMPATIBLE_PARAMS,\n cerebras: OPENAI_COMPATIBLE_PARAMS,\n replicate: {},\n prodia: {},\n luma: {},\n bytedance: {},\n kling: {},\n elevenlabs: {},\n assemblyai: {},\n deepgram: {},\n gladia: {},\n lmnt: {},\n hume: {},\n revai: {},\n baseten: OPENAI_COMPATIBLE_PARAMS,\n huggingface: OPENAI_COMPATIBLE_PARAMS,\n};\n\n/**\n * Validation specs per provider, keyed by provider-specific param name.\n */\nexport interface ParamSpec {\n type: \"number\" | \"string\" | \"boolean\";\n min?: number;\n max?: number;\n values?: string[];\n default?: string | number | boolean;\n description?: string;\n}\n\nconst OPENAI_COMPATIBLE_PARAM_SPECS: Record<string, ParamSpec> = {\n temperature: {\n type: \"number\",\n min: 0,\n max: 2,\n default: 0.7,\n description: \"Controls randomness\",\n },\n max_tokens: {\n type: \"number\",\n min: 1,\n default: 4096,\n description: \"Maximum output tokens\",\n },\n top_p: {\n type: \"number\",\n min: 0,\n max: 1,\n default: 1,\n description: \"Nucleus sampling\",\n },\n top_k: {\n type: \"number\",\n min: 0,\n default: 40,\n description: \"Top-K sampling\",\n },\n frequency_penalty: {\n type: \"number\",\n min: -2,\n max: 2,\n default: 0,\n description: \"Penalize frequent tokens\",\n },\n presence_penalty: {\n type: \"number\",\n min: -2,\n max: 2,\n default: 0,\n description: \"Penalize repeated topics\",\n },\n stop: { type: \"string\", description: \"Stop sequences\" },\n n: { type: \"number\", min: 1, default: 1, description: \"Completions count\" },\n seed: { type: \"number\", description: \"Random seed\" },\n stream: { type: \"boolean\", default: false, description: \"Stream response\" },\n reasoning_effort: {\n type: \"string\",\n values: [\"none\", \"minimal\", \"low\", \"medium\", \"high\", \"xhigh\"],\n default: \"medium\",\n description: \"Reasoning effort\",\n },\n};\n\nconst GOOGLE_COMPATIBLE_PARAM_SPECS: Record<string, ParamSpec> = {\n temperature: {\n type: \"number\",\n min: 0,\n max: 2,\n default: 0.7,\n description: \"Controls randomness\",\n },\n maxOutputTokens: {\n type: \"number\",\n min: 1,\n default: 4096,\n description: \"Maximum output tokens\",\n },\n topP: {\n type: \"number\",\n min: 0,\n max: 1,\n default: 1,\n description: \"Nucleus sampling\",\n },\n topK: {\n type: \"number\",\n min: 0,\n default: 40,\n description: \"Top-K sampling\",\n },\n frequencyPenalty: {\n type: \"number\",\n min: -2,\n max: 2,\n default: 0,\n description: \"Penalize frequent tokens\",\n },\n presencePenalty: {\n type: \"number\",\n min: -2,\n max: 2,\n default: 0,\n description: \"Penalize repeated topics\",\n },\n stopSequences: { type: \"string\", description: \"Stop sequences\" },\n candidateCount: {\n type: \"number\",\n min: 1,\n default: 1,\n description: \"Candidate count\",\n },\n stream: { type: \"boolean\", default: false, description: \"Stream response\" },\n seed: { type: \"number\", description: \"Random seed\" },\n responseMimeType: { type: \"string\", description: \"Response MIME type\" },\n responseSchema: { type: \"string\", description: \"Response schema\" },\n};\n\nexport const PARAM_SPECS: Record<Provider, Record<string, ParamSpec>> = {\n openai: {\n temperature: {\n type: \"number\",\n min: 0,\n max: 2,\n default: 0.7,\n description: \"Controls randomness\",\n },\n max_tokens: {\n type: \"number\",\n min: 1,\n default: 4096,\n description: \"Maximum output tokens\",\n },\n top_p: {\n type: \"number\",\n min: 0,\n max: 1,\n default: 1,\n description: \"Nucleus sampling\",\n },\n frequency_penalty: {\n type: \"number\",\n min: -2,\n max: 2,\n default: 0,\n description: \"Penalize frequent tokens\",\n },\n presence_penalty: {\n type: \"number\",\n min: -2,\n max: 2,\n default: 0,\n description: \"Penalize repeated topics\",\n },\n stop: { type: \"string\", description: \"Stop sequences\" },\n n: { type: \"number\", min: 1, default: 1, description: \"Completions count\" },\n seed: { type: \"number\", description: \"Random seed\" },\n stream: { type: \"boolean\", default: false, description: \"Stream response\" },\n reasoning_effort: {\n type: \"string\",\n values: [\"none\", \"minimal\", \"low\", \"medium\", \"high\", \"xhigh\"],\n default: \"medium\",\n description: \"Reasoning effort\",\n },\n },\n azure: OPENAI_COMPATIBLE_PARAM_SPECS,\n anthropic: {\n temperature: {\n type: \"number\",\n min: 0,\n max: 1,\n default: 0.7,\n description: \"Controls randomness\",\n },\n max_tokens: {\n type: \"number\",\n min: 1,\n default: 4096,\n description: \"Maximum output tokens\",\n },\n top_p: {\n type: \"number\",\n min: 0,\n max: 1,\n default: 1,\n description: \"Nucleus sampling\",\n },\n top_k: {\n type: \"number\",\n min: 0,\n default: 40,\n description: \"Top-K sampling\",\n },\n stop_sequences: { type: \"string\", description: \"Stop sequences\" },\n stream: { type: \"boolean\", default: false, description: \"Stream response\" },\n effort: {\n type: \"string\",\n values: [\"low\", \"medium\", \"high\", \"max\"],\n default: \"medium\",\n description: \"Thinking effort\",\n },\n cache_control: {\n type: \"string\",\n values: [\"ephemeral\"],\n default: \"ephemeral\",\n description: \"Cache control\",\n },\n cache_ttl: {\n type: \"string\",\n values: [\"5m\", \"1h\"],\n default: \"5m\",\n description: \"Cache TTL\",\n },\n },\n google: {\n temperature: {\n type: \"number\",\n min: 0,\n max: 2,\n default: 0.7,\n description: \"Controls randomness\",\n },\n maxOutputTokens: {\n type: \"number\",\n min: 1,\n default: 4096,\n description: \"Maximum output tokens\",\n },\n topP: {\n type: \"number\",\n min: 0,\n max: 1,\n default: 1,\n description: \"Nucleus sampling\",\n },\n topK: {\n type: \"number\",\n min: 0,\n default: 40,\n description: \"Top-K sampling\",\n },\n frequencyPenalty: {\n type: \"number\",\n min: -2,\n max: 2,\n default: 0,\n description: \"Penalize frequent tokens\",\n },\n presencePenalty: {\n type: \"number\",\n min: -2,\n max: 2,\n default: 0,\n description: \"Penalize repeated topics\",\n },\n stopSequences: { type: \"string\", description: \"Stop sequences\" },\n candidateCount: {\n type: \"number\",\n min: 1,\n default: 1,\n description: \"Candidate count\",\n },\n stream: { type: \"boolean\", default: false, description: \"Stream response\" },\n seed: { type: \"number\", description: \"Random seed\" },\n responseMimeType: { type: \"string\", description: \"Response MIME type\" },\n responseSchema: { type: \"string\", description: \"Response schema\" },\n },\n \"google-vertex\": GOOGLE_COMPATIBLE_PARAM_SPECS,\n mistral: {\n temperature: {\n type: \"number\",\n min: 0,\n max: 1,\n default: 0.7,\n description: \"Controls randomness\",\n },\n max_tokens: {\n type: \"number\",\n min: 1,\n default: 4096,\n description: \"Maximum output tokens\",\n },\n top_p: {\n type: \"number\",\n min: 0,\n max: 1,\n default: 1,\n description: \"Nucleus sampling\",\n },\n frequency_penalty: {\n type: \"number\",\n min: -2,\n max: 2,\n default: 0,\n description: \"Penalize frequent tokens\",\n },\n presence_penalty: {\n type: \"number\",\n min: -2,\n max: 2,\n default: 0,\n description: \"Penalize repeated topics\",\n },\n stop: { type: \"string\", description: \"Stop sequences\" },\n n: { type: \"number\", min: 1, default: 1, description: \"Completions count\" },\n random_seed: { type: \"number\", description: \"Random seed\" },\n stream: { type: \"boolean\", default: false, description: \"Stream response\" },\n safe_prompt: {\n type: \"boolean\",\n default: false,\n description: \"Enable safe prompt\",\n },\n min_tokens: {\n type: \"number\",\n min: 0,\n default: 0,\n description: \"Minimum tokens\",\n },\n },\n cohere: {\n temperature: {\n type: \"number\",\n min: 0,\n max: 1,\n default: 0.7,\n description: \"Controls randomness\",\n },\n max_tokens: {\n type: \"number\",\n min: 1,\n default: 4096,\n description: \"Maximum output tokens\",\n },\n p: {\n type: \"number\",\n min: 0,\n max: 1,\n default: 1,\n description: \"Nucleus sampling (p)\",\n },\n k: {\n type: \"number\",\n min: 0,\n max: 500,\n default: 40,\n description: \"Top-K sampling (k)\",\n },\n frequency_penalty: {\n type: \"number\",\n min: 0,\n max: 1,\n default: 0,\n description: \"Penalize frequent tokens\",\n },\n presence_penalty: {\n type: \"number\",\n min: 0,\n max: 1,\n default: 0,\n description: \"Penalize repeated topics\",\n },\n stop_sequences: { type: \"string\", description: \"Stop sequences\" },\n stream: { type: \"boolean\", default: false, description: \"Stream response\" },\n seed: { type: \"number\", description: \"Random seed\" },\n },\n bedrock: {\n // Converse API inferenceConfig params\n temperature: {\n type: \"number\",\n min: 0,\n max: 1,\n default: 0.7,\n description: \"Controls randomness\",\n },\n maxTokens: {\n type: \"number\",\n min: 1,\n default: 4096,\n description: \"Maximum output tokens\",\n },\n topP: {\n type: \"number\",\n min: 0,\n max: 1,\n default: 1,\n description: \"Nucleus sampling\",\n },\n topK: {\n type: \"number\",\n min: 0,\n default: 40,\n description: \"Top-K sampling\",\n },\n stopSequences: { type: \"string\", description: \"Stop sequences\" },\n stream: { type: \"boolean\", default: false, description: \"Stream response\" },\n cache_control: {\n type: \"string\",\n values: [\"ephemeral\"],\n default: \"ephemeral\",\n description: \"Cache control\",\n },\n cache_ttl: {\n type: \"string\",\n values: [\"5m\", \"1h\"],\n default: \"5m\",\n description: \"Cache TTL\",\n },\n },\n openrouter: {\n // Loose validation — proxies to many providers with varying ranges\n temperature: {\n type: \"number\",\n min: 0,\n max: 2,\n default: 0.7,\n description: \"Controls randomness\",\n },\n max_tokens: {\n type: \"number\",\n min: 1,\n default: 4096,\n description: \"Maximum output tokens\",\n },\n top_p: {\n type: \"number\",\n min: 0,\n max: 1,\n default: 1,\n description: \"Nucleus sampling\",\n },\n top_k: {\n type: \"number\",\n min: 0,\n default: 40,\n description: \"Top-K sampling\",\n },\n frequency_penalty: {\n type: \"number\",\n min: -2,\n max: 2,\n default: 0,\n description: \"Penalize frequent tokens\",\n },\n presence_penalty: {\n type: \"number\",\n min: -2,\n max: 2,\n default: 0,\n description: \"Penalize repeated topics\",\n },\n stop: { type: \"string\", description: \"Stop sequences\" },\n n: { type: \"number\", min: 1, default: 1, description: \"Completions count\" },\n seed: { type: \"number\", description: \"Random seed\" },\n stream: { type: \"boolean\", default: false, description: \"Stream response\" },\n reasoning_effort: {\n type: \"string\",\n values: [\"none\", \"minimal\", \"low\", \"medium\", \"high\", \"xhigh\"],\n default: \"medium\",\n description: \"Reasoning effort\",\n },\n },\n vercel: {\n // Loose validation — proxies to many providers with varying ranges\n temperature: {\n type: \"number\",\n min: 0,\n max: 2,\n default: 0.7,\n description: \"Controls randomness\",\n },\n max_tokens: {\n type: \"number\",\n min: 1,\n default: 4096,\n description: \"Maximum output tokens\",\n },\n top_p: {\n type: \"number\",\n min: 0,\n max: 1,\n default: 1,\n description: \"Nucleus sampling\",\n },\n top_k: {\n type: \"number\",\n min: 0,\n default: 40,\n description: \"Top-K sampling\",\n },\n frequency_penalty: {\n type: \"number\",\n min: -2,\n max: 2,\n default: 0,\n description: \"Penalize frequent tokens\",\n },\n presence_penalty: {\n type: \"number\",\n min: -2,\n max: 2,\n default: 0,\n description: \"Penalize repeated topics\",\n },\n stop: { type: \"string\", description: \"Stop sequences\" },\n n: { type: \"number\", min: 1, default: 1, description: \"Completions count\" },\n seed: { type: \"number\", description: \"Random seed\" },\n stream: { type: \"boolean\", default: false, description: \"Stream response\" },\n reasoning_effort: {\n type: \"string\",\n values: [\"none\", \"minimal\", \"low\", \"medium\", \"high\", \"xhigh\"],\n default: \"medium\",\n description: \"Reasoning effort\",\n },\n },\n xai: OPENAI_COMPATIBLE_PARAM_SPECS,\n groq: OPENAI_COMPATIBLE_PARAM_SPECS,\n fal: {},\n deepinfra: OPENAI_COMPATIBLE_PARAM_SPECS,\n \"black-forest-labs\": {},\n together: OPENAI_COMPATIBLE_PARAM_SPECS,\n fireworks: OPENAI_COMPATIBLE_PARAM_SPECS,\n deepseek: OPENAI_COMPATIBLE_PARAM_SPECS,\n moonshotai: OPENAI_COMPATIBLE_PARAM_SPECS,\n perplexity: OPENAI_COMPATIBLE_PARAM_SPECS,\n alibaba: OPENAI_COMPATIBLE_PARAM_SPECS,\n cerebras: OPENAI_COMPATIBLE_PARAM_SPECS,\n replicate: {},\n prodia: {},\n luma: {},\n bytedance: {},\n kling: {},\n elevenlabs: {},\n assemblyai: {},\n deepgram: {},\n gladia: {},\n lmnt: {},\n hume: {},\n revai: {},\n baseten: OPENAI_COMPATIBLE_PARAM_SPECS,\n huggingface: OPENAI_COMPATIBLE_PARAM_SPECS,\n};\n\n/** OpenAI reasoning models don't support standard sampling params. */\nexport function isReasoningModel(model: string): boolean {\n // Strip gateway prefix: \"openai/o3\" → \"o3\"\n const name = model.includes(\"/\") ? model.split(\"/\").pop()! : model;\n return /^o[134]/.test(name);\n}\n\n/** Providers that can route to OpenAI models (and need reasoning-model checks). */\nexport function canHostOpenAIModels(provider: Provider): boolean {\n return (\n provider === \"openai\" || provider === \"openrouter\" || provider === \"vercel\"\n );\n}\n\n/** Whether this provider is a gateway/router that proxies to other providers. */\nexport function isGatewayProvider(provider: Provider): boolean {\n return provider === \"openrouter\" || provider === \"vercel\";\n}\n\n/**\n * Extract the underlying provider from a gateway model string.\n * e.g. \"anthropic/claude-sonnet-4-5\" → \"anthropic\"\n * Returns undefined for unknown prefixes (qwen, deepseek, etc.) or models without \"/\".\n */\nexport function detectGatewaySubProvider(model: string): Provider | undefined {\n const slash = model.indexOf(\"/\");\n if (slash < 1) return undefined;\n const prefix = model.slice(0, slash);\n const direct: Provider[] = [\n \"openai\",\n \"anthropic\",\n \"google\",\n \"mistral\",\n \"cohere\",\n ];\n return direct.find((p) => p === prefix);\n}\n\nexport const REASONING_MODEL_UNSUPPORTED = new Set([\n \"temperature\",\n \"top_p\",\n \"frequency_penalty\",\n \"presence_penalty\",\n \"n\",\n]);\n\n/**\n * Bedrock model IDs are prefixed with the vendor name.\n * e.g. \"anthropic.claude-sonnet-4-5-20250929-v1:0\"\n */\nexport type BedrockModelFamily =\n | \"anthropic\"\n | \"meta\"\n | \"amazon\"\n | \"mistral\"\n | \"cohere\"\n | \"ai21\";\n\nexport function detectBedrockModelFamily(\n model: string,\n): BedrockModelFamily | undefined {\n // Handle cross-region inference profiles (e.g. \"us.anthropic.claude-sonnet-4-5...\")\n // and global inference profiles (e.g. \"global.anthropic.claude-sonnet-4-5...\")\n const parts = model.split(\".\");\n\n // If first part is a region prefix (us, eu, apac) or global, skip it\n let prefix = parts[0];\n if ([\"us\", \"eu\", \"apac\", \"global\"].includes(prefix) && parts.length > 1) {\n prefix = parts[1];\n }\n\n const families: BedrockModelFamily[] = [\n \"anthropic\",\n \"meta\",\n \"amazon\",\n \"mistral\",\n \"cohere\",\n \"ai21\",\n ];\n return families.find((f) => prefix === f);\n}\n\n/** Whether a Bedrock model supports prompt caching (Claude and Nova only). */\nexport function bedrockSupportsCaching(model: string): boolean {\n const family = detectBedrockModelFamily(model);\n if (family === \"anthropic\") return true;\n if (family === \"amazon\" && model.includes(\"nova\")) return true;\n return false;\n}\n\n/** Cache value normalization per provider. */\nexport const CACHE_VALUES: Record<Provider, string | undefined> = {\n openai: undefined, // OpenAI auto-caches; no explicit param\n azure: undefined,\n anthropic: \"ephemeral\",\n google: undefined, // Google uses explicit caching API, not a param\n \"google-vertex\": undefined,\n mistral: undefined,\n cohere: undefined,\n bedrock: \"ephemeral\", // Supported for Claude models on Bedrock\n openrouter: undefined, // Depends on underlying provider\n vercel: undefined, // Depends on underlying provider\n xai: undefined,\n groq: undefined,\n fal: undefined,\n deepinfra: undefined,\n \"black-forest-labs\": undefined,\n together: undefined,\n fireworks: undefined,\n deepseek: undefined,\n moonshotai: undefined,\n perplexity: undefined,\n alibaba: undefined,\n cerebras: undefined,\n replicate: undefined,\n prodia: undefined,\n luma: undefined,\n bytedance: undefined,\n kling: undefined,\n elevenlabs: undefined,\n assemblyai: undefined,\n deepgram: undefined,\n gladia: undefined,\n lmnt: undefined,\n hume: undefined,\n revai: undefined,\n baseten: undefined,\n huggingface: undefined,\n};\n\n/** Valid cache TTL values per provider. */\nexport const CACHE_TTLS: Record<Provider, string[] | undefined> = {\n openai: undefined,\n azure: undefined,\n anthropic: [\"5m\", \"1h\"],\n google: undefined,\n \"google-vertex\": undefined,\n mistral: undefined,\n cohere: undefined,\n bedrock: [\"5m\", \"1h\"], // Claude on Bedrock uses same TTLs as direct Anthropic\n openrouter: undefined,\n vercel: undefined,\n xai: undefined,\n groq: undefined,\n fal: undefined,\n deepinfra: undefined,\n \"black-forest-labs\": undefined,\n together: undefined,\n fireworks: undefined,\n deepseek: undefined,\n moonshotai: undefined,\n perplexity: undefined,\n alibaba: undefined,\n cerebras: undefined,\n replicate: undefined,\n prodia: undefined,\n luma: undefined,\n bytedance: undefined,\n kling: undefined,\n elevenlabs: undefined,\n assemblyai: undefined,\n deepgram: undefined,\n gladia: undefined,\n lmnt: undefined,\n hume: undefined,\n revai: undefined,\n baseten: undefined,\n huggingface: undefined,\n};\n\n/** Match a duration expression like \"5m\", \"1h\", \"30m\". */\nexport const DURATION_RE = /^\\d+[mh]$/;\n"],"mappings":"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","names":[]}
@@ -1,5 +1,5 @@
1
1
  import { LlmConnectionConfig } from './parse.cjs';
2
- import { P as Provider } from './provider-core-BiAl8MCV.cjs';
2
+ import { P as Provider } from './provider-core-B934MuhJ.cjs';
3
3
 
4
4
  interface NormalizeChange {
5
5
  from: string;
@@ -1,5 +1,5 @@
1
1
  import { LlmConnectionConfig } from './parse.js';
2
- import { P as Provider } from './provider-core-BiAl8MCV.js';
2
+ import { P as Provider } from './provider-core-B934MuhJ.js';
3
3
 
4
4
  interface NormalizeChange {
5
5
  from: string;
package/dist/normalize.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  normalize
3
- } from "./chunk-2ARD4TFU.js";
4
- import "./chunk-W4NIQY7M.js";
3
+ } from "./chunk-NZR5DUX5.js";
4
+ import "./chunk-OCJX4QFJ.js";
5
5
  export {
6
6
  normalize
7
7
  };
package/dist/parse.cjs CHANGED
@@ -31,8 +31,10 @@ function hasOwn(object, key) {
31
31
  }
32
32
  var HOST_ALIASES = {
33
33
  openai: "api.openai.com",
34
+ azure: "models.inference.ai.azure.com",
34
35
  anthropic: "api.anthropic.com",
35
36
  google: "generativelanguage.googleapis.com",
37
+ "google-vertex": "aiplatform.googleapis.com",
36
38
  aistudio: "generativelanguage.googleapis.com",
37
39
  mistral: "api.mistral.ai",
38
40
  cohere: "api.cohere.com",
@@ -42,8 +44,16 @@ var HOST_ALIASES = {
42
44
  alibaba: "dashscope-intl.aliyuncs.com",
43
45
  alibabacloud: "dashscope-intl.aliyuncs.com",
44
46
  dashscope: "dashscope-intl.aliyuncs.com",
47
+ groq: "api.groq.com",
48
+ fal: "fal.run",
45
49
  fireworks: "api.fireworks.ai",
46
50
  fireworksai: "api.fireworks.ai",
51
+ "black-forest-labs": "api.bfl.ai",
52
+ bfl: "api.bfl.ai",
53
+ deepseek: "api.deepseek.com",
54
+ moonshotai: "api.moonshot.ai",
55
+ moonshot: "api.moonshot.ai",
56
+ perplexity: "api.perplexity.ai",
47
57
  venice: "api.venice.ai",
48
58
  parasail: "api.parasail.io",
49
59
  deepinfra: "api.deepinfra.com",
@@ -52,6 +62,23 @@ var HOST_ALIASES = {
52
62
  novitaai: "api.novita.ai",
53
63
  grok: "api.x.ai",
54
64
  xai: "api.x.ai",
65
+ together: "api.together.xyz",
66
+ togetherai: "api.together.xyz",
67
+ cerebras: "api.cerebras.ai",
68
+ replicate: "api.replicate.com",
69
+ prodia: "api.prodia.com",
70
+ luma: "api.lumalabs.ai",
71
+ bytedance: "ark.cn-beijing.volces.com",
72
+ kling: "api.klingai.com",
73
+ elevenlabs: "api.elevenlabs.io",
74
+ assemblyai: "api.assemblyai.com",
75
+ deepgram: "api.deepgram.com",
76
+ gladia: "api.gladia.io",
77
+ lmnt: "api.lmnt.com",
78
+ hume: "api.hume.ai",
79
+ revai: "api.rev.ai",
80
+ baseten: "api.baseten.co",
81
+ huggingface: "api-inference.huggingface.co",
55
82
  wandb: "api.inference.wandb.ai",
56
83
  weightsandbiases: "api.inference.wandb.ai",
57
84
  baidu: "qianfan.baidubce.com",
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/parse.ts","../src/provider-core.ts"],"sourcesContent":["import { resolveHostAlias } from \"./provider-core.js\";\nimport type { HostAlias } from \"./provider-core.js\";\n\nexport interface LlmConnectionConfig {\n /** The original connection string */\n raw: string;\n /** Provider's API host (e.g. \"api.openai.com\") */\n host: string;\n /** Short provider alias that was expanded to host, if any. */\n hostAlias?: HostAlias;\n /** Model name (e.g. \"gpt-5.2\") */\n model: string;\n /** Optional label or app name */\n label?: string;\n /** Optional API key or password */\n apiKey?: string;\n /** Additional config parameters (temp, max_tokens, etc.) */\n params: Record<string, string>;\n}\n\n/**\n * Parse an LLM connection string into its component parts.\n *\n * Format: `llm://[label[:apiKey]@]host/model[?key=value&...]`\n *\n * @example\n * ```ts\n * parse(\"llm://api.openai.com/gpt-5.2?temp=0.7&max_tokens=1500\")\n * parse(\"llm://app-name:sk-proj-123456@api.openai.com/gpt-5.2?temp=0.7\")\n * ```\n */\nexport function parse(connectionString: string): LlmConnectionConfig {\n const url = new URL(connectionString);\n\n if (url.protocol !== \"llm:\") {\n throw new Error(\n `Invalid scheme: expected \"llm://\", got \"${url.protocol}//\"`,\n );\n }\n\n const { host, alias: hostAlias } = resolveHostAlias(url.host);\n const model = url.pathname.replace(/^\\//, \"\");\n const label = url.username || undefined;\n const apiKey = url.password || undefined;\n\n const params: Record<string, string> = {};\n for (const [key, value] of url.searchParams) {\n params[key] = value;\n }\n\n return {\n raw: connectionString,\n host,\n hostAlias,\n model,\n label,\n apiKey,\n params,\n };\n}\n\n/**\n * Build an LLM connection string from a config object.\n */\nexport function build(config: Omit<LlmConnectionConfig, \"raw\">): string {\n const { host } = resolveHostAlias(config.host);\n const auth =\n config.label || config.apiKey\n ? `${config.label ?? \"\"}${config.apiKey ? `:${config.apiKey}` : \"\"}@`\n : \"\";\n\n const query = new URLSearchParams(config.params).toString();\n const qs = query ? `?${query}` : \"\";\n\n return `llm://${auth}${host}/${config.model}${qs}`;\n}\n","export type Provider =\n | \"openai\"\n | \"anthropic\"\n | \"google\"\n | \"mistral\"\n | \"cohere\"\n | \"bedrock\"\n | \"openrouter\"\n | \"vercel\";\n\nexport type HostAlias =\n | Provider\n | \"aistudio\"\n | \"alibaba\"\n | \"alibabacloud\"\n | \"atlascloud\"\n | \"baidu\"\n | \"dashscope\"\n | \"deepinfra\"\n | \"fireworks\"\n | \"fireworksai\"\n | \"grok\"\n | \"minimax\"\n | \"novita\"\n | \"novitaai\"\n | \"parasail\"\n | \"qianfan\"\n | \"vertex\"\n | \"venice\"\n | \"wandb\"\n | \"weightsandbiases\"\n | \"xai\"\n | \"xiaomi\";\n\ntype Env = Record<string, string | undefined>;\n\ndeclare const process:\n | {\n env?: Env;\n }\n | undefined;\n\nfunction hasOwn<T extends object>(object: T, key: PropertyKey): key is keyof T {\n return Object.prototype.hasOwnProperty.call(object, key);\n}\n\nexport const HOST_ALIASES: Record<HostAlias, string> = {\n openai: \"api.openai.com\",\n anthropic: \"api.anthropic.com\",\n google: \"generativelanguage.googleapis.com\",\n aistudio: \"generativelanguage.googleapis.com\",\n mistral: \"api.mistral.ai\",\n cohere: \"api.cohere.com\",\n bedrock: \"bedrock-runtime.us-east-1.amazonaws.com\",\n openrouter: \"openrouter.ai\",\n vercel: \"gateway.ai.vercel.app\",\n alibaba: \"dashscope-intl.aliyuncs.com\",\n alibabacloud: \"dashscope-intl.aliyuncs.com\",\n dashscope: \"dashscope-intl.aliyuncs.com\",\n fireworks: \"api.fireworks.ai\",\n fireworksai: \"api.fireworks.ai\",\n venice: \"api.venice.ai\",\n parasail: \"api.parasail.io\",\n deepinfra: \"api.deepinfra.com\",\n atlascloud: \"api.atlascloud.ai\",\n novita: \"api.novita.ai\",\n novitaai: \"api.novita.ai\",\n grok: \"api.x.ai\",\n xai: \"api.x.ai\",\n wandb: \"api.inference.wandb.ai\",\n weightsandbiases: \"api.inference.wandb.ai\",\n baidu: \"qianfan.baidubce.com\",\n qianfan: \"qianfan.baidubce.com\",\n vertex: \"aiplatform.googleapis.com\",\n xiaomi: \"api.xiaomimimo.com\",\n minimax: \"api.minimax.io\",\n};\n\nexport interface HostResolution {\n /** The hostname/host to use for requests. */\n host: string;\n /** The provider alias that was expanded, if any. */\n alias?: HostAlias;\n}\n\nfunction readProcessEnv(): Env {\n return typeof process !== \"undefined\" && process.env ? process.env : {};\n}\n\nfunction normalizeHostValue(value: string): string {\n const trimmed = value.trim();\n if (!trimmed) return trimmed;\n\n try {\n if (trimmed.includes(\"://\")) {\n return new URL(trimmed).host;\n }\n } catch {\n // Fall through and treat it as a host-ish string.\n }\n\n return trimmed.replace(/^\\/\\//, \"\").split(\"/\")[0] ?? trimmed;\n}\n\nfunction envHostOverride(alias: HostAlias, env: Env): string | undefined {\n const upper = alias.toUpperCase();\n const override =\n env[`LLM_STRINGS_${upper}_HOST`] ?? env[`LLM_STRINGS_HOST_${upper}`];\n return override?.trim() ? override : undefined;\n}\n\n/**\n * Resolve short provider host aliases (`openai`, `anthropic`, etc.) to their\n * canonical hostnames. Per-provider environment overrides can redirect aliases\n * to regional or private endpoints:\n *\n * - `LLM_STRINGS_OPENAI_HOST`\n * - `LLM_STRINGS_HOST_OPENAI`\n */\nexport function resolveHostAlias(\n host: string,\n env: Env = readProcessEnv(),\n): HostResolution {\n const normalizedHost = host.toLowerCase();\n if (!hasOwn(HOST_ALIASES, normalizedHost)) {\n return { host };\n }\n\n const alias = normalizedHost as HostAlias;\n const override = envHostOverride(alias, env);\n return {\n host: normalizeHostValue(override ?? HOST_ALIASES[alias]),\n alias,\n };\n}\n\nexport function providerFromHostAlias(alias: string): Provider | undefined {\n const normalizedAlias = alias.toLowerCase();\n if (hasOwn(PROVIDER_PARAMS, normalizedAlias)) {\n return normalizedAlias as Provider;\n }\n return undefined;\n}\n\nexport function detectProvider(host: string): Provider | undefined {\n host = host.toLowerCase();\n\n // Gateways and aggregators first — they proxy to other providers\n if (host.includes(\"openrouter\")) return \"openrouter\";\n if (host.includes(\"gateway.ai.vercel\")) return \"vercel\";\n // Bedrock before native providers since it hosts models from multiple vendors\n if (host.includes(\"amazonaws\") || host.includes(\"bedrock\")) return \"bedrock\";\n if (host.includes(\"openai\")) return \"openai\";\n if (host.includes(\"anthropic\") || host.includes(\"claude\")) return \"anthropic\";\n if (host.includes(\"googleapis\") || host.includes(\"google\")) return \"google\";\n if (host.includes(\"mistral\")) return \"mistral\";\n if (host.includes(\"cohere\")) return \"cohere\";\n return undefined;\n}\n\n/**\n * Shorthand aliases → canonical param name.\n * Canonical names use snake_case and follow OpenAI conventions where possible.\n */\nexport const ALIASES: Record<string, string> = {\n // temperature\n temp: \"temperature\",\n\n // max_tokens\n max: \"max_tokens\",\n max_out: \"max_tokens\",\n max_output: \"max_tokens\",\n max_output_tokens: \"max_tokens\",\n max_completion_tokens: \"max_tokens\",\n maxOutputTokens: \"max_tokens\",\n maxTokens: \"max_tokens\",\n\n // top_p\n topp: \"top_p\",\n topP: \"top_p\",\n nucleus: \"top_p\",\n\n // top_k\n topk: \"top_k\",\n topK: \"top_k\",\n\n // frequency_penalty\n freq: \"frequency_penalty\",\n freq_penalty: \"frequency_penalty\",\n frequencyPenalty: \"frequency_penalty\",\n repetition_penalty: \"frequency_penalty\",\n\n // presence_penalty\n pres: \"presence_penalty\",\n pres_penalty: \"presence_penalty\",\n presencePenalty: \"presence_penalty\",\n\n // stop\n stop_sequences: \"stop\",\n stopSequences: \"stop\",\n stop_sequence: \"stop\",\n\n // seed\n random_seed: \"seed\",\n randomSeed: \"seed\",\n\n // n (completions count)\n candidateCount: \"n\",\n candidate_count: \"n\",\n num_completions: \"n\",\n\n // effort / reasoning\n reasoning_effort: \"effort\",\n reasoning: \"effort\",\n\n // cache\n cache_control: \"cache\",\n cacheControl: \"cache\",\n cachePoint: \"cache\",\n cache_point: \"cache\",\n};\n\n/**\n * Canonical param name → provider-specific API param name.\n * Only includes params the provider actually supports.\n */\nexport const PROVIDER_PARAMS: Record<Provider, Record<string, string>> = {\n openai: {\n temperature: \"temperature\",\n max_tokens: \"max_tokens\",\n top_p: \"top_p\",\n frequency_penalty: \"frequency_penalty\",\n presence_penalty: \"presence_penalty\",\n stop: \"stop\",\n n: \"n\",\n seed: \"seed\",\n stream: \"stream\",\n effort: \"reasoning_effort\",\n },\n anthropic: {\n temperature: \"temperature\",\n max_tokens: \"max_tokens\",\n top_p: \"top_p\",\n top_k: \"top_k\",\n stop: \"stop_sequences\",\n stream: \"stream\",\n effort: \"effort\",\n cache: \"cache_control\",\n cache_ttl: \"cache_ttl\",\n },\n google: {\n temperature: \"temperature\",\n max_tokens: \"maxOutputTokens\",\n top_p: \"topP\",\n top_k: \"topK\",\n frequency_penalty: \"frequencyPenalty\",\n presence_penalty: \"presencePenalty\",\n stop: \"stopSequences\",\n n: \"candidateCount\",\n stream: \"stream\",\n seed: \"seed\",\n responseMimeType: \"responseMimeType\",\n responseSchema: \"responseSchema\",\n },\n mistral: {\n temperature: \"temperature\",\n max_tokens: \"max_tokens\",\n top_p: \"top_p\",\n frequency_penalty: \"frequency_penalty\",\n presence_penalty: \"presence_penalty\",\n stop: \"stop\",\n n: \"n\",\n seed: \"random_seed\",\n stream: \"stream\",\n safe_prompt: \"safe_prompt\",\n min_tokens: \"min_tokens\",\n },\n cohere: {\n temperature: \"temperature\",\n max_tokens: \"max_tokens\",\n top_p: \"p\",\n top_k: \"k\",\n frequency_penalty: \"frequency_penalty\",\n presence_penalty: \"presence_penalty\",\n stop: \"stop_sequences\",\n stream: \"stream\",\n seed: \"seed\",\n },\n bedrock: {\n // Bedrock Converse API uses camelCase\n temperature: \"temperature\",\n max_tokens: \"maxTokens\",\n top_p: \"topP\",\n top_k: \"topK\", // Claude models via additionalModelRequestFields\n stop: \"stopSequences\",\n stream: \"stream\",\n cache: \"cache_control\",\n cache_ttl: \"cache_ttl\",\n },\n openrouter: {\n // OpenAI-compatible API with extra routing params\n temperature: \"temperature\",\n max_tokens: \"max_tokens\",\n top_p: \"top_p\",\n top_k: \"top_k\",\n frequency_penalty: \"frequency_penalty\",\n presence_penalty: \"presence_penalty\",\n stop: \"stop\",\n n: \"n\",\n seed: \"seed\",\n stream: \"stream\",\n effort: \"reasoning_effort\",\n },\n vercel: {\n // OpenAI-compatible gateway\n temperature: \"temperature\",\n max_tokens: \"max_tokens\",\n top_p: \"top_p\",\n top_k: \"top_k\",\n frequency_penalty: \"frequency_penalty\",\n presence_penalty: \"presence_penalty\",\n stop: \"stop\",\n n: \"n\",\n seed: \"seed\",\n stream: \"stream\",\n effort: \"reasoning_effort\",\n },\n};\n\n/**\n * Validation specs per provider, keyed by provider-specific param name.\n */\nexport interface ParamSpec {\n type: \"number\" | \"string\" | \"boolean\";\n min?: number;\n max?: number;\n values?: string[];\n default?: string | number | boolean;\n description?: string;\n}\n\nexport const PARAM_SPECS: Record<Provider, Record<string, ParamSpec>> = {\n openai: {\n temperature: {\n type: \"number\",\n min: 0,\n max: 2,\n default: 0.7,\n description: \"Controls randomness\",\n },\n max_tokens: {\n type: \"number\",\n min: 1,\n default: 4096,\n description: \"Maximum output tokens\",\n },\n top_p: {\n type: \"number\",\n min: 0,\n max: 1,\n default: 1,\n description: \"Nucleus sampling\",\n },\n frequency_penalty: {\n type: \"number\",\n min: -2,\n max: 2,\n default: 0,\n description: \"Penalize frequent tokens\",\n },\n presence_penalty: {\n type: \"number\",\n min: -2,\n max: 2,\n default: 0,\n description: \"Penalize repeated topics\",\n },\n stop: { type: \"string\", description: \"Stop sequences\" },\n n: { type: \"number\", min: 1, default: 1, description: \"Completions count\" },\n seed: { type: \"number\", description: \"Random seed\" },\n stream: { type: \"boolean\", default: false, description: \"Stream response\" },\n reasoning_effort: {\n type: \"string\",\n values: [\"none\", \"minimal\", \"low\", \"medium\", \"high\", \"xhigh\"],\n default: \"medium\",\n description: \"Reasoning effort\",\n },\n },\n anthropic: {\n temperature: {\n type: \"number\",\n min: 0,\n max: 1,\n default: 0.7,\n description: \"Controls randomness\",\n },\n max_tokens: {\n type: \"number\",\n min: 1,\n default: 4096,\n description: \"Maximum output tokens\",\n },\n top_p: {\n type: \"number\",\n min: 0,\n max: 1,\n default: 1,\n description: \"Nucleus sampling\",\n },\n top_k: {\n type: \"number\",\n min: 0,\n default: 40,\n description: \"Top-K sampling\",\n },\n stop_sequences: { type: \"string\", description: \"Stop sequences\" },\n stream: { type: \"boolean\", default: false, description: \"Stream response\" },\n effort: {\n type: \"string\",\n values: [\"low\", \"medium\", \"high\", \"max\"],\n default: \"medium\",\n description: \"Thinking effort\",\n },\n cache_control: {\n type: \"string\",\n values: [\"ephemeral\"],\n default: \"ephemeral\",\n description: \"Cache control\",\n },\n cache_ttl: {\n type: \"string\",\n values: [\"5m\", \"1h\"],\n default: \"5m\",\n description: \"Cache TTL\",\n },\n },\n google: {\n temperature: {\n type: \"number\",\n min: 0,\n max: 2,\n default: 0.7,\n description: \"Controls randomness\",\n },\n maxOutputTokens: {\n type: \"number\",\n min: 1,\n default: 4096,\n description: \"Maximum output tokens\",\n },\n topP: {\n type: \"number\",\n min: 0,\n max: 1,\n default: 1,\n description: \"Nucleus sampling\",\n },\n topK: {\n type: \"number\",\n min: 0,\n default: 40,\n description: \"Top-K sampling\",\n },\n frequencyPenalty: {\n type: \"number\",\n min: -2,\n max: 2,\n default: 0,\n description: \"Penalize frequent tokens\",\n },\n presencePenalty: {\n type: \"number\",\n min: -2,\n max: 2,\n default: 0,\n description: \"Penalize repeated topics\",\n },\n stopSequences: { type: \"string\", description: \"Stop sequences\" },\n candidateCount: {\n type: \"number\",\n min: 1,\n default: 1,\n description: \"Candidate count\",\n },\n stream: { type: \"boolean\", default: false, description: \"Stream response\" },\n seed: { type: \"number\", description: \"Random seed\" },\n responseMimeType: { type: \"string\", description: \"Response MIME type\" },\n responseSchema: { type: \"string\", description: \"Response schema\" },\n },\n mistral: {\n temperature: {\n type: \"number\",\n min: 0,\n max: 1,\n default: 0.7,\n description: \"Controls randomness\",\n },\n max_tokens: {\n type: \"number\",\n min: 1,\n default: 4096,\n description: \"Maximum output tokens\",\n },\n top_p: {\n type: \"number\",\n min: 0,\n max: 1,\n default: 1,\n description: \"Nucleus sampling\",\n },\n frequency_penalty: {\n type: \"number\",\n min: -2,\n max: 2,\n default: 0,\n description: \"Penalize frequent tokens\",\n },\n presence_penalty: {\n type: \"number\",\n min: -2,\n max: 2,\n default: 0,\n description: \"Penalize repeated topics\",\n },\n stop: { type: \"string\", description: \"Stop sequences\" },\n n: { type: \"number\", min: 1, default: 1, description: \"Completions count\" },\n random_seed: { type: \"number\", description: \"Random seed\" },\n stream: { type: \"boolean\", default: false, description: \"Stream response\" },\n safe_prompt: {\n type: \"boolean\",\n default: false,\n description: \"Enable safe prompt\",\n },\n min_tokens: {\n type: \"number\",\n min: 0,\n default: 0,\n description: \"Minimum tokens\",\n },\n },\n cohere: {\n temperature: {\n type: \"number\",\n min: 0,\n max: 1,\n default: 0.7,\n description: \"Controls randomness\",\n },\n max_tokens: {\n type: \"number\",\n min: 1,\n default: 4096,\n description: \"Maximum output tokens\",\n },\n p: {\n type: \"number\",\n min: 0,\n max: 1,\n default: 1,\n description: \"Nucleus sampling (p)\",\n },\n k: {\n type: \"number\",\n min: 0,\n max: 500,\n default: 40,\n description: \"Top-K sampling (k)\",\n },\n frequency_penalty: {\n type: \"number\",\n min: 0,\n max: 1,\n default: 0,\n description: \"Penalize frequent tokens\",\n },\n presence_penalty: {\n type: \"number\",\n min: 0,\n max: 1,\n default: 0,\n description: \"Penalize repeated topics\",\n },\n stop_sequences: { type: \"string\", description: \"Stop sequences\" },\n stream: { type: \"boolean\", default: false, description: \"Stream response\" },\n seed: { type: \"number\", description: \"Random seed\" },\n },\n bedrock: {\n // Converse API inferenceConfig params\n temperature: {\n type: \"number\",\n min: 0,\n max: 1,\n default: 0.7,\n description: \"Controls randomness\",\n },\n maxTokens: {\n type: \"number\",\n min: 1,\n default: 4096,\n description: \"Maximum output tokens\",\n },\n topP: {\n type: \"number\",\n min: 0,\n max: 1,\n default: 1,\n description: \"Nucleus sampling\",\n },\n topK: {\n type: \"number\",\n min: 0,\n default: 40,\n description: \"Top-K sampling\",\n },\n stopSequences: { type: \"string\", description: \"Stop sequences\" },\n stream: { type: \"boolean\", default: false, description: \"Stream response\" },\n cache_control: {\n type: \"string\",\n values: [\"ephemeral\"],\n default: \"ephemeral\",\n description: \"Cache control\",\n },\n cache_ttl: {\n type: \"string\",\n values: [\"5m\", \"1h\"],\n default: \"5m\",\n description: \"Cache TTL\",\n },\n },\n openrouter: {\n // Loose validation — proxies to many providers with varying ranges\n temperature: {\n type: \"number\",\n min: 0,\n max: 2,\n default: 0.7,\n description: \"Controls randomness\",\n },\n max_tokens: {\n type: \"number\",\n min: 1,\n default: 4096,\n description: \"Maximum output tokens\",\n },\n top_p: {\n type: \"number\",\n min: 0,\n max: 1,\n default: 1,\n description: \"Nucleus sampling\",\n },\n top_k: {\n type: \"number\",\n min: 0,\n default: 40,\n description: \"Top-K sampling\",\n },\n frequency_penalty: {\n type: \"number\",\n min: -2,\n max: 2,\n default: 0,\n description: \"Penalize frequent tokens\",\n },\n presence_penalty: {\n type: \"number\",\n min: -2,\n max: 2,\n default: 0,\n description: \"Penalize repeated topics\",\n },\n stop: { type: \"string\", description: \"Stop sequences\" },\n n: { type: \"number\", min: 1, default: 1, description: \"Completions count\" },\n seed: { type: \"number\", description: \"Random seed\" },\n stream: { type: \"boolean\", default: false, description: \"Stream response\" },\n reasoning_effort: {\n type: \"string\",\n values: [\"none\", \"minimal\", \"low\", \"medium\", \"high\", \"xhigh\"],\n default: \"medium\",\n description: \"Reasoning effort\",\n },\n },\n vercel: {\n // Loose validation — proxies to many providers with varying ranges\n temperature: {\n type: \"number\",\n min: 0,\n max: 2,\n default: 0.7,\n description: \"Controls randomness\",\n },\n max_tokens: {\n type: \"number\",\n min: 1,\n default: 4096,\n description: \"Maximum output tokens\",\n },\n top_p: {\n type: \"number\",\n min: 0,\n max: 1,\n default: 1,\n description: \"Nucleus sampling\",\n },\n top_k: {\n type: \"number\",\n min: 0,\n default: 40,\n description: \"Top-K sampling\",\n },\n frequency_penalty: {\n type: \"number\",\n min: -2,\n max: 2,\n default: 0,\n description: \"Penalize frequent tokens\",\n },\n presence_penalty: {\n type: \"number\",\n min: -2,\n max: 2,\n default: 0,\n description: \"Penalize repeated topics\",\n },\n stop: { type: \"string\", description: \"Stop sequences\" },\n n: { type: \"number\", min: 1, default: 1, description: \"Completions count\" },\n seed: { type: \"number\", description: \"Random seed\" },\n stream: { type: \"boolean\", default: false, description: \"Stream response\" },\n reasoning_effort: {\n type: \"string\",\n values: [\"none\", \"minimal\", \"low\", \"medium\", \"high\", \"xhigh\"],\n default: \"medium\",\n description: \"Reasoning effort\",\n },\n },\n};\n\n/** OpenAI reasoning models don't support standard sampling params. */\nexport function isReasoningModel(model: string): boolean {\n // Strip gateway prefix: \"openai/o3\" → \"o3\"\n const name = model.includes(\"/\") ? model.split(\"/\").pop()! : model;\n return /^o[134]/.test(name);\n}\n\n/** Providers that can route to OpenAI models (and need reasoning-model checks). */\nexport function canHostOpenAIModels(provider: Provider): boolean {\n return (\n provider === \"openai\" || provider === \"openrouter\" || provider === \"vercel\"\n );\n}\n\n/** Whether this provider is a gateway/router that proxies to other providers. */\nexport function isGatewayProvider(provider: Provider): boolean {\n return provider === \"openrouter\" || provider === \"vercel\";\n}\n\n/**\n * Extract the underlying provider from a gateway model string.\n * e.g. \"anthropic/claude-sonnet-4-5\" → \"anthropic\"\n * Returns undefined for unknown prefixes (qwen, deepseek, etc.) or models without \"/\".\n */\nexport function detectGatewaySubProvider(model: string): Provider | undefined {\n const slash = model.indexOf(\"/\");\n if (slash < 1) return undefined;\n const prefix = model.slice(0, slash);\n const direct: Provider[] = [\n \"openai\",\n \"anthropic\",\n \"google\",\n \"mistral\",\n \"cohere\",\n ];\n return direct.find((p) => p === prefix);\n}\n\nexport const REASONING_MODEL_UNSUPPORTED = new Set([\n \"temperature\",\n \"top_p\",\n \"frequency_penalty\",\n \"presence_penalty\",\n \"n\",\n]);\n\n/**\n * Bedrock model IDs are prefixed with the vendor name.\n * e.g. \"anthropic.claude-sonnet-4-5-20250929-v1:0\"\n */\nexport type BedrockModelFamily =\n | \"anthropic\"\n | \"meta\"\n | \"amazon\"\n | \"mistral\"\n | \"cohere\"\n | \"ai21\";\n\nexport function detectBedrockModelFamily(\n model: string,\n): BedrockModelFamily | undefined {\n // Handle cross-region inference profiles (e.g. \"us.anthropic.claude-sonnet-4-5...\")\n // and global inference profiles (e.g. \"global.anthropic.claude-sonnet-4-5...\")\n const parts = model.split(\".\");\n\n // If first part is a region prefix (us, eu, apac) or global, skip it\n let prefix = parts[0];\n if ([\"us\", \"eu\", \"apac\", \"global\"].includes(prefix) && parts.length > 1) {\n prefix = parts[1];\n }\n\n const families: BedrockModelFamily[] = [\n \"anthropic\",\n \"meta\",\n \"amazon\",\n \"mistral\",\n \"cohere\",\n \"ai21\",\n ];\n return families.find((f) => prefix === f);\n}\n\n/** Whether a Bedrock model supports prompt caching (Claude and Nova only). */\nexport function bedrockSupportsCaching(model: string): boolean {\n const family = detectBedrockModelFamily(model);\n if (family === \"anthropic\") return true;\n if (family === \"amazon\" && model.includes(\"nova\")) return true;\n return false;\n}\n\n/** Cache value normalization per provider. */\nexport const CACHE_VALUES: Record<Provider, string | undefined> = {\n openai: undefined, // OpenAI auto-caches; no explicit param\n anthropic: \"ephemeral\",\n google: undefined, // Google uses explicit caching API, not a param\n mistral: undefined,\n cohere: undefined,\n bedrock: \"ephemeral\", // Supported for Claude models on Bedrock\n openrouter: undefined, // Depends on underlying provider\n vercel: undefined, // Depends on underlying provider\n};\n\n/** Valid cache TTL values per provider. */\nexport const CACHE_TTLS: Record<Provider, string[] | undefined> = {\n openai: undefined,\n anthropic: [\"5m\", \"1h\"],\n google: undefined,\n mistral: undefined,\n cohere: undefined,\n bedrock: [\"5m\", \"1h\"], // Claude on Bedrock uses same TTLs as direct Anthropic\n openrouter: undefined,\n vercel: undefined,\n};\n\n/** Match a duration expression like \"5m\", \"1h\", \"30m\". */\nexport const DURATION_RE = /^\\d+[mh]$/;\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/parse.ts","../src/provider-core.ts"],"sourcesContent":["import { resolveHostAlias } from \"./provider-core.js\";\nimport type { HostAlias } from \"./provider-core.js\";\n\nexport interface LlmConnectionConfig {\n /** The original connection string */\n raw: string;\n /** Provider's API host (e.g. \"api.openai.com\") */\n host: string;\n /** Short provider alias that was expanded to host, if any. */\n hostAlias?: HostAlias;\n /** Model name (e.g. \"gpt-5.2\") */\n model: string;\n /** Optional label or app name */\n label?: string;\n /** Optional API key or password */\n apiKey?: string;\n /** Additional config parameters (temp, max_tokens, etc.) */\n params: Record<string, string>;\n}\n\n/**\n * Parse an LLM connection string into its component parts.\n *\n * Format: `llm://[label[:apiKey]@]host/model[?key=value&...]`\n *\n * @example\n * ```ts\n * parse(\"llm://api.openai.com/gpt-5.2?temp=0.7&max_tokens=1500\")\n * parse(\"llm://app-name:sk-proj-123456@api.openai.com/gpt-5.2?temp=0.7\")\n * ```\n */\nexport function parse(connectionString: string): LlmConnectionConfig {\n const url = new URL(connectionString);\n\n if (url.protocol !== \"llm:\") {\n throw new Error(\n `Invalid scheme: expected \"llm://\", got \"${url.protocol}//\"`,\n );\n }\n\n const { host, alias: hostAlias } = resolveHostAlias(url.host);\n const model = url.pathname.replace(/^\\//, \"\");\n const label = url.username || undefined;\n const apiKey = url.password || undefined;\n\n const params: Record<string, string> = {};\n for (const [key, value] of url.searchParams) {\n params[key] = value;\n }\n\n return {\n raw: connectionString,\n host,\n hostAlias,\n model,\n label,\n apiKey,\n params,\n };\n}\n\n/**\n * Build an LLM connection string from a config object.\n */\nexport function build(config: Omit<LlmConnectionConfig, \"raw\">): string {\n const { host } = resolveHostAlias(config.host);\n const auth =\n config.label || config.apiKey\n ? `${config.label ?? \"\"}${config.apiKey ? `:${config.apiKey}` : \"\"}@`\n : \"\";\n\n const query = new URLSearchParams(config.params).toString();\n const qs = query ? `?${query}` : \"\";\n\n return `llm://${auth}${host}/${config.model}${qs}`;\n}\n","export type Provider =\n | \"openai\"\n | \"azure\"\n | \"anthropic\"\n | \"google\"\n | \"google-vertex\"\n | \"mistral\"\n | \"cohere\"\n | \"bedrock\"\n | \"openrouter\"\n | \"vercel\"\n | \"xai\"\n | \"groq\"\n | \"fal\"\n | \"deepinfra\"\n | \"black-forest-labs\"\n | \"together\"\n | \"fireworks\"\n | \"deepseek\"\n | \"moonshotai\"\n | \"perplexity\"\n | \"alibaba\"\n | \"cerebras\"\n | \"replicate\"\n | \"prodia\"\n | \"luma\"\n | \"bytedance\"\n | \"kling\"\n | \"elevenlabs\"\n | \"assemblyai\"\n | \"deepgram\"\n | \"gladia\"\n | \"lmnt\"\n | \"hume\"\n | \"revai\"\n | \"baseten\"\n | \"huggingface\";\n\nexport type HostAlias =\n | Provider\n | \"aistudio\"\n | \"alibaba\"\n | \"alibabacloud\"\n | \"atlascloud\"\n | \"baidu\"\n | \"cerebras\"\n | \"dashscope\"\n | \"deepinfra\"\n | \"deepseek\"\n | \"fireworks\"\n | \"fireworksai\"\n | \"groq\"\n | \"grok\"\n | \"bfl\"\n | \"minimax\"\n | \"moonshot\"\n | \"novita\"\n | \"novitaai\"\n | \"parasail\"\n | \"perplexity\"\n | \"qianfan\"\n | \"together\"\n | \"togetherai\"\n | \"vertex\"\n | \"venice\"\n | \"wandb\"\n | \"weightsandbiases\"\n | \"xai\"\n | \"xiaomi\";\n\ntype Env = Record<string, string | undefined>;\n\ndeclare const process:\n | {\n env?: Env;\n }\n | undefined;\n\nfunction hasOwn<T extends object>(object: T, key: PropertyKey): key is keyof T {\n return Object.prototype.hasOwnProperty.call(object, key);\n}\n\nexport const HOST_ALIASES: Record<HostAlias, string> = {\n openai: \"api.openai.com\",\n azure: \"models.inference.ai.azure.com\",\n anthropic: \"api.anthropic.com\",\n google: \"generativelanguage.googleapis.com\",\n \"google-vertex\": \"aiplatform.googleapis.com\",\n aistudio: \"generativelanguage.googleapis.com\",\n mistral: \"api.mistral.ai\",\n cohere: \"api.cohere.com\",\n bedrock: \"bedrock-runtime.us-east-1.amazonaws.com\",\n openrouter: \"openrouter.ai\",\n vercel: \"gateway.ai.vercel.app\",\n alibaba: \"dashscope-intl.aliyuncs.com\",\n alibabacloud: \"dashscope-intl.aliyuncs.com\",\n dashscope: \"dashscope-intl.aliyuncs.com\",\n groq: \"api.groq.com\",\n fal: \"fal.run\",\n fireworks: \"api.fireworks.ai\",\n fireworksai: \"api.fireworks.ai\",\n \"black-forest-labs\": \"api.bfl.ai\",\n bfl: \"api.bfl.ai\",\n deepseek: \"api.deepseek.com\",\n moonshotai: \"api.moonshot.ai\",\n moonshot: \"api.moonshot.ai\",\n perplexity: \"api.perplexity.ai\",\n venice: \"api.venice.ai\",\n parasail: \"api.parasail.io\",\n deepinfra: \"api.deepinfra.com\",\n atlascloud: \"api.atlascloud.ai\",\n novita: \"api.novita.ai\",\n novitaai: \"api.novita.ai\",\n grok: \"api.x.ai\",\n xai: \"api.x.ai\",\n together: \"api.together.xyz\",\n togetherai: \"api.together.xyz\",\n cerebras: \"api.cerebras.ai\",\n replicate: \"api.replicate.com\",\n prodia: \"api.prodia.com\",\n luma: \"api.lumalabs.ai\",\n bytedance: \"ark.cn-beijing.volces.com\",\n kling: \"api.klingai.com\",\n elevenlabs: \"api.elevenlabs.io\",\n assemblyai: \"api.assemblyai.com\",\n deepgram: \"api.deepgram.com\",\n gladia: \"api.gladia.io\",\n lmnt: \"api.lmnt.com\",\n hume: \"api.hume.ai\",\n revai: \"api.rev.ai\",\n baseten: \"api.baseten.co\",\n huggingface: \"api-inference.huggingface.co\",\n wandb: \"api.inference.wandb.ai\",\n weightsandbiases: \"api.inference.wandb.ai\",\n baidu: \"qianfan.baidubce.com\",\n qianfan: \"qianfan.baidubce.com\",\n vertex: \"aiplatform.googleapis.com\",\n xiaomi: \"api.xiaomimimo.com\",\n minimax: \"api.minimax.io\",\n};\n\nconst HOST_ALIAS_PROVIDERS: Partial<Record<HostAlias, Provider>> = {\n aistudio: \"google\",\n vertex: \"google-vertex\",\n grok: \"xai\",\n bfl: \"black-forest-labs\",\n moonshot: \"moonshotai\",\n alibaba: \"alibaba\",\n alibabacloud: \"alibaba\",\n dashscope: \"alibaba\",\n togetherai: \"together\",\n fireworksai: \"fireworks\",\n};\n\nexport interface HostResolution {\n /** The hostname/host to use for requests. */\n host: string;\n /** The provider alias that was expanded, if any. */\n alias?: HostAlias;\n}\n\nfunction readProcessEnv(): Env {\n return typeof process !== \"undefined\" && process.env ? process.env : {};\n}\n\nfunction normalizeHostValue(value: string): string {\n const trimmed = value.trim();\n if (!trimmed) return trimmed;\n\n try {\n if (trimmed.includes(\"://\")) {\n return new URL(trimmed).host;\n }\n } catch {\n // Fall through and treat it as a host-ish string.\n }\n\n return trimmed.replace(/^\\/\\//, \"\").split(\"/\")[0] ?? trimmed;\n}\n\nfunction envHostOverride(alias: HostAlias, env: Env): string | undefined {\n const upper = alias.toUpperCase();\n const override =\n env[`LLM_STRINGS_${upper}_HOST`] ?? env[`LLM_STRINGS_HOST_${upper}`];\n return override?.trim() ? override : undefined;\n}\n\n/**\n * Resolve short provider host aliases (`openai`, `anthropic`, etc.) to their\n * canonical hostnames. Per-provider environment overrides can redirect aliases\n * to regional or private endpoints:\n *\n * - `LLM_STRINGS_OPENAI_HOST`\n * - `LLM_STRINGS_HOST_OPENAI`\n */\nexport function resolveHostAlias(\n host: string,\n env: Env = readProcessEnv(),\n): HostResolution {\n const normalizedHost = host.toLowerCase();\n if (!hasOwn(HOST_ALIASES, normalizedHost)) {\n return { host };\n }\n\n const alias = normalizedHost as HostAlias;\n const override = envHostOverride(alias, env);\n return {\n host: normalizeHostValue(override ?? HOST_ALIASES[alias]),\n alias,\n };\n}\n\nexport function providerFromHostAlias(alias: string): Provider | undefined {\n const normalizedAlias = alias.toLowerCase();\n if (hasOwn(PROVIDER_PARAMS, normalizedAlias)) {\n return normalizedAlias as Provider;\n }\n if (hasOwn(HOST_ALIAS_PROVIDERS, normalizedAlias)) {\n return HOST_ALIAS_PROVIDERS[normalizedAlias as HostAlias];\n }\n return undefined;\n}\n\nexport function detectProvider(host: string): Provider | undefined {\n host = host.toLowerCase();\n\n // Gateways and aggregators first — they proxy to other providers\n if (host.includes(\"openrouter\")) return \"openrouter\";\n if (host.includes(\"gateway.ai.vercel\")) return \"vercel\";\n // Bedrock before native providers since it hosts models from multiple vendors\n if (host.includes(\"amazonaws\") || host.includes(\"bedrock\")) return \"bedrock\";\n if (host.includes(\"aiplatform.googleapis\")) return \"google-vertex\";\n if (host.includes(\"api.x.ai\")) return \"xai\";\n if (host.includes(\"groq\")) return \"groq\";\n if (host.includes(\"fal.run\") || host.includes(\"fal.ai\")) return \"fal\";\n if (host.includes(\"deepinfra\")) return \"deepinfra\";\n if (host.includes(\"bfl.ai\")) return \"black-forest-labs\";\n if (host.includes(\"together\")) return \"together\";\n if (host.includes(\"fireworks\")) return \"fireworks\";\n if (host.includes(\"deepseek\")) return \"deepseek\";\n if (host.includes(\"moonshot\")) return \"moonshotai\";\n if (host.includes(\"perplexity\")) return \"perplexity\";\n if (host.includes(\"dashscope\") || host.includes(\"aliyuncs\")) return \"alibaba\";\n if (host.includes(\"cerebras\")) return \"cerebras\";\n if (host.includes(\"replicate\")) return \"replicate\";\n if (host.includes(\"prodia\")) return \"prodia\";\n if (host.includes(\"lumalabs\") || host.includes(\"luma\")) return \"luma\";\n if (host.includes(\"volces\") || host.includes(\"bytedance\")) return \"bytedance\";\n if (host.includes(\"kling\")) return \"kling\";\n if (host.includes(\"elevenlabs\")) return \"elevenlabs\";\n if (host.includes(\"assemblyai\")) return \"assemblyai\";\n if (host.includes(\"deepgram\")) return \"deepgram\";\n if (host.includes(\"gladia\")) return \"gladia\";\n if (host.includes(\"lmnt\")) return \"lmnt\";\n if (host.includes(\"hume\")) return \"hume\";\n if (host.includes(\"rev.ai\")) return \"revai\";\n if (host.includes(\"baseten\")) return \"baseten\";\n if (host.includes(\"huggingface\")) return \"huggingface\";\n if (host.includes(\"azure\")) return \"azure\";\n if (host.includes(\"openai\")) return \"openai\";\n if (host.includes(\"anthropic\") || host.includes(\"claude\")) return \"anthropic\";\n if (host.includes(\"googleapis\") || host.includes(\"google\")) return \"google\";\n if (host.includes(\"mistral\")) return \"mistral\";\n if (host.includes(\"cohere\")) return \"cohere\";\n return undefined;\n}\n\n/**\n * Shorthand aliases → canonical param name.\n * Canonical names use snake_case and follow OpenAI conventions where possible.\n */\nexport const ALIASES: Record<string, string> = {\n // temperature\n temp: \"temperature\",\n\n // max_tokens\n max: \"max_tokens\",\n max_out: \"max_tokens\",\n max_output: \"max_tokens\",\n max_output_tokens: \"max_tokens\",\n max_completion_tokens: \"max_tokens\",\n maxOutputTokens: \"max_tokens\",\n maxTokens: \"max_tokens\",\n\n // top_p\n topp: \"top_p\",\n topP: \"top_p\",\n nucleus: \"top_p\",\n\n // top_k\n topk: \"top_k\",\n topK: \"top_k\",\n\n // frequency_penalty\n freq: \"frequency_penalty\",\n freq_penalty: \"frequency_penalty\",\n frequencyPenalty: \"frequency_penalty\",\n repetition_penalty: \"frequency_penalty\",\n\n // presence_penalty\n pres: \"presence_penalty\",\n pres_penalty: \"presence_penalty\",\n presencePenalty: \"presence_penalty\",\n\n // stop\n stop_sequences: \"stop\",\n stopSequences: \"stop\",\n stop_sequence: \"stop\",\n\n // seed\n random_seed: \"seed\",\n randomSeed: \"seed\",\n\n // n (completions count)\n candidateCount: \"n\",\n candidate_count: \"n\",\n num_completions: \"n\",\n\n // effort / reasoning\n reasoning_effort: \"effort\",\n reasoning: \"effort\",\n\n // cache\n cache_control: \"cache\",\n cacheControl: \"cache\",\n cachePoint: \"cache\",\n cache_point: \"cache\",\n};\n\nconst OPENAI_COMPATIBLE_PARAMS: Record<string, string> = {\n temperature: \"temperature\",\n max_tokens: \"max_tokens\",\n top_p: \"top_p\",\n top_k: \"top_k\",\n frequency_penalty: \"frequency_penalty\",\n presence_penalty: \"presence_penalty\",\n stop: \"stop\",\n n: \"n\",\n seed: \"seed\",\n stream: \"stream\",\n effort: \"reasoning_effort\",\n};\n\nconst GOOGLE_COMPATIBLE_PARAMS: Record<string, string> = {\n temperature: \"temperature\",\n max_tokens: \"maxOutputTokens\",\n top_p: \"topP\",\n top_k: \"topK\",\n frequency_penalty: \"frequencyPenalty\",\n presence_penalty: \"presencePenalty\",\n stop: \"stopSequences\",\n n: \"candidateCount\",\n stream: \"stream\",\n seed: \"seed\",\n responseMimeType: \"responseMimeType\",\n responseSchema: \"responseSchema\",\n};\n\n/**\n * Canonical param name → provider-specific API param name.\n * Only includes params the provider actually supports.\n */\nexport const PROVIDER_PARAMS: Record<Provider, Record<string, string>> = {\n openai: {\n temperature: \"temperature\",\n max_tokens: \"max_tokens\",\n top_p: \"top_p\",\n frequency_penalty: \"frequency_penalty\",\n presence_penalty: \"presence_penalty\",\n stop: \"stop\",\n n: \"n\",\n seed: \"seed\",\n stream: \"stream\",\n effort: \"reasoning_effort\",\n },\n azure: OPENAI_COMPATIBLE_PARAMS,\n anthropic: {\n temperature: \"temperature\",\n max_tokens: \"max_tokens\",\n top_p: \"top_p\",\n top_k: \"top_k\",\n stop: \"stop_sequences\",\n stream: \"stream\",\n effort: \"effort\",\n cache: \"cache_control\",\n cache_ttl: \"cache_ttl\",\n },\n google: {\n temperature: \"temperature\",\n max_tokens: \"maxOutputTokens\",\n top_p: \"topP\",\n top_k: \"topK\",\n frequency_penalty: \"frequencyPenalty\",\n presence_penalty: \"presencePenalty\",\n stop: \"stopSequences\",\n n: \"candidateCount\",\n stream: \"stream\",\n seed: \"seed\",\n responseMimeType: \"responseMimeType\",\n responseSchema: \"responseSchema\",\n },\n \"google-vertex\": GOOGLE_COMPATIBLE_PARAMS,\n mistral: {\n temperature: \"temperature\",\n max_tokens: \"max_tokens\",\n top_p: \"top_p\",\n frequency_penalty: \"frequency_penalty\",\n presence_penalty: \"presence_penalty\",\n stop: \"stop\",\n n: \"n\",\n seed: \"random_seed\",\n stream: \"stream\",\n safe_prompt: \"safe_prompt\",\n min_tokens: \"min_tokens\",\n },\n cohere: {\n temperature: \"temperature\",\n max_tokens: \"max_tokens\",\n top_p: \"p\",\n top_k: \"k\",\n frequency_penalty: \"frequency_penalty\",\n presence_penalty: \"presence_penalty\",\n stop: \"stop_sequences\",\n stream: \"stream\",\n seed: \"seed\",\n },\n bedrock: {\n // Bedrock Converse API uses camelCase\n temperature: \"temperature\",\n max_tokens: \"maxTokens\",\n top_p: \"topP\",\n top_k: \"topK\", // Claude models via additionalModelRequestFields\n stop: \"stopSequences\",\n stream: \"stream\",\n cache: \"cache_control\",\n cache_ttl: \"cache_ttl\",\n },\n openrouter: {\n // OpenAI-compatible API with extra routing params\n temperature: \"temperature\",\n max_tokens: \"max_tokens\",\n top_p: \"top_p\",\n top_k: \"top_k\",\n frequency_penalty: \"frequency_penalty\",\n presence_penalty: \"presence_penalty\",\n stop: \"stop\",\n n: \"n\",\n seed: \"seed\",\n stream: \"stream\",\n effort: \"reasoning_effort\",\n },\n vercel: {\n // OpenAI-compatible gateway\n temperature: \"temperature\",\n max_tokens: \"max_tokens\",\n top_p: \"top_p\",\n top_k: \"top_k\",\n frequency_penalty: \"frequency_penalty\",\n presence_penalty: \"presence_penalty\",\n stop: \"stop\",\n n: \"n\",\n seed: \"seed\",\n stream: \"stream\",\n effort: \"reasoning_effort\",\n },\n xai: OPENAI_COMPATIBLE_PARAMS,\n groq: OPENAI_COMPATIBLE_PARAMS,\n fal: {},\n deepinfra: OPENAI_COMPATIBLE_PARAMS,\n \"black-forest-labs\": {},\n together: OPENAI_COMPATIBLE_PARAMS,\n fireworks: OPENAI_COMPATIBLE_PARAMS,\n deepseek: OPENAI_COMPATIBLE_PARAMS,\n moonshotai: OPENAI_COMPATIBLE_PARAMS,\n perplexity: OPENAI_COMPATIBLE_PARAMS,\n alibaba: OPENAI_COMPATIBLE_PARAMS,\n cerebras: OPENAI_COMPATIBLE_PARAMS,\n replicate: {},\n prodia: {},\n luma: {},\n bytedance: {},\n kling: {},\n elevenlabs: {},\n assemblyai: {},\n deepgram: {},\n gladia: {},\n lmnt: {},\n hume: {},\n revai: {},\n baseten: OPENAI_COMPATIBLE_PARAMS,\n huggingface: OPENAI_COMPATIBLE_PARAMS,\n};\n\n/**\n * Validation specs per provider, keyed by provider-specific param name.\n */\nexport interface ParamSpec {\n type: \"number\" | \"string\" | \"boolean\";\n min?: number;\n max?: number;\n values?: string[];\n default?: string | number | boolean;\n description?: string;\n}\n\nconst OPENAI_COMPATIBLE_PARAM_SPECS: Record<string, ParamSpec> = {\n temperature: {\n type: \"number\",\n min: 0,\n max: 2,\n default: 0.7,\n description: \"Controls randomness\",\n },\n max_tokens: {\n type: \"number\",\n min: 1,\n default: 4096,\n description: \"Maximum output tokens\",\n },\n top_p: {\n type: \"number\",\n min: 0,\n max: 1,\n default: 1,\n description: \"Nucleus sampling\",\n },\n top_k: {\n type: \"number\",\n min: 0,\n default: 40,\n description: \"Top-K sampling\",\n },\n frequency_penalty: {\n type: \"number\",\n min: -2,\n max: 2,\n default: 0,\n description: \"Penalize frequent tokens\",\n },\n presence_penalty: {\n type: \"number\",\n min: -2,\n max: 2,\n default: 0,\n description: \"Penalize repeated topics\",\n },\n stop: { type: \"string\", description: \"Stop sequences\" },\n n: { type: \"number\", min: 1, default: 1, description: \"Completions count\" },\n seed: { type: \"number\", description: \"Random seed\" },\n stream: { type: \"boolean\", default: false, description: \"Stream response\" },\n reasoning_effort: {\n type: \"string\",\n values: [\"none\", \"minimal\", \"low\", \"medium\", \"high\", \"xhigh\"],\n default: \"medium\",\n description: \"Reasoning effort\",\n },\n};\n\nconst GOOGLE_COMPATIBLE_PARAM_SPECS: Record<string, ParamSpec> = {\n temperature: {\n type: \"number\",\n min: 0,\n max: 2,\n default: 0.7,\n description: \"Controls randomness\",\n },\n maxOutputTokens: {\n type: \"number\",\n min: 1,\n default: 4096,\n description: \"Maximum output tokens\",\n },\n topP: {\n type: \"number\",\n min: 0,\n max: 1,\n default: 1,\n description: \"Nucleus sampling\",\n },\n topK: {\n type: \"number\",\n min: 0,\n default: 40,\n description: \"Top-K sampling\",\n },\n frequencyPenalty: {\n type: \"number\",\n min: -2,\n max: 2,\n default: 0,\n description: \"Penalize frequent tokens\",\n },\n presencePenalty: {\n type: \"number\",\n min: -2,\n max: 2,\n default: 0,\n description: \"Penalize repeated topics\",\n },\n stopSequences: { type: \"string\", description: \"Stop sequences\" },\n candidateCount: {\n type: \"number\",\n min: 1,\n default: 1,\n description: \"Candidate count\",\n },\n stream: { type: \"boolean\", default: false, description: \"Stream response\" },\n seed: { type: \"number\", description: \"Random seed\" },\n responseMimeType: { type: \"string\", description: \"Response MIME type\" },\n responseSchema: { type: \"string\", description: \"Response schema\" },\n};\n\nexport const PARAM_SPECS: Record<Provider, Record<string, ParamSpec>> = {\n openai: {\n temperature: {\n type: \"number\",\n min: 0,\n max: 2,\n default: 0.7,\n description: \"Controls randomness\",\n },\n max_tokens: {\n type: \"number\",\n min: 1,\n default: 4096,\n description: \"Maximum output tokens\",\n },\n top_p: {\n type: \"number\",\n min: 0,\n max: 1,\n default: 1,\n description: \"Nucleus sampling\",\n },\n frequency_penalty: {\n type: \"number\",\n min: -2,\n max: 2,\n default: 0,\n description: \"Penalize frequent tokens\",\n },\n presence_penalty: {\n type: \"number\",\n min: -2,\n max: 2,\n default: 0,\n description: \"Penalize repeated topics\",\n },\n stop: { type: \"string\", description: \"Stop sequences\" },\n n: { type: \"number\", min: 1, default: 1, description: \"Completions count\" },\n seed: { type: \"number\", description: \"Random seed\" },\n stream: { type: \"boolean\", default: false, description: \"Stream response\" },\n reasoning_effort: {\n type: \"string\",\n values: [\"none\", \"minimal\", \"low\", \"medium\", \"high\", \"xhigh\"],\n default: \"medium\",\n description: \"Reasoning effort\",\n },\n },\n azure: OPENAI_COMPATIBLE_PARAM_SPECS,\n anthropic: {\n temperature: {\n type: \"number\",\n min: 0,\n max: 1,\n default: 0.7,\n description: \"Controls randomness\",\n },\n max_tokens: {\n type: \"number\",\n min: 1,\n default: 4096,\n description: \"Maximum output tokens\",\n },\n top_p: {\n type: \"number\",\n min: 0,\n max: 1,\n default: 1,\n description: \"Nucleus sampling\",\n },\n top_k: {\n type: \"number\",\n min: 0,\n default: 40,\n description: \"Top-K sampling\",\n },\n stop_sequences: { type: \"string\", description: \"Stop sequences\" },\n stream: { type: \"boolean\", default: false, description: \"Stream response\" },\n effort: {\n type: \"string\",\n values: [\"low\", \"medium\", \"high\", \"max\"],\n default: \"medium\",\n description: \"Thinking effort\",\n },\n cache_control: {\n type: \"string\",\n values: [\"ephemeral\"],\n default: \"ephemeral\",\n description: \"Cache control\",\n },\n cache_ttl: {\n type: \"string\",\n values: [\"5m\", \"1h\"],\n default: \"5m\",\n description: \"Cache TTL\",\n },\n },\n google: {\n temperature: {\n type: \"number\",\n min: 0,\n max: 2,\n default: 0.7,\n description: \"Controls randomness\",\n },\n maxOutputTokens: {\n type: \"number\",\n min: 1,\n default: 4096,\n description: \"Maximum output tokens\",\n },\n topP: {\n type: \"number\",\n min: 0,\n max: 1,\n default: 1,\n description: \"Nucleus sampling\",\n },\n topK: {\n type: \"number\",\n min: 0,\n default: 40,\n description: \"Top-K sampling\",\n },\n frequencyPenalty: {\n type: \"number\",\n min: -2,\n max: 2,\n default: 0,\n description: \"Penalize frequent tokens\",\n },\n presencePenalty: {\n type: \"number\",\n min: -2,\n max: 2,\n default: 0,\n description: \"Penalize repeated topics\",\n },\n stopSequences: { type: \"string\", description: \"Stop sequences\" },\n candidateCount: {\n type: \"number\",\n min: 1,\n default: 1,\n description: \"Candidate count\",\n },\n stream: { type: \"boolean\", default: false, description: \"Stream response\" },\n seed: { type: \"number\", description: \"Random seed\" },\n responseMimeType: { type: \"string\", description: \"Response MIME type\" },\n responseSchema: { type: \"string\", description: \"Response schema\" },\n },\n \"google-vertex\": GOOGLE_COMPATIBLE_PARAM_SPECS,\n mistral: {\n temperature: {\n type: \"number\",\n min: 0,\n max: 1,\n default: 0.7,\n description: \"Controls randomness\",\n },\n max_tokens: {\n type: \"number\",\n min: 1,\n default: 4096,\n description: \"Maximum output tokens\",\n },\n top_p: {\n type: \"number\",\n min: 0,\n max: 1,\n default: 1,\n description: \"Nucleus sampling\",\n },\n frequency_penalty: {\n type: \"number\",\n min: -2,\n max: 2,\n default: 0,\n description: \"Penalize frequent tokens\",\n },\n presence_penalty: {\n type: \"number\",\n min: -2,\n max: 2,\n default: 0,\n description: \"Penalize repeated topics\",\n },\n stop: { type: \"string\", description: \"Stop sequences\" },\n n: { type: \"number\", min: 1, default: 1, description: \"Completions count\" },\n random_seed: { type: \"number\", description: \"Random seed\" },\n stream: { type: \"boolean\", default: false, description: \"Stream response\" },\n safe_prompt: {\n type: \"boolean\",\n default: false,\n description: \"Enable safe prompt\",\n },\n min_tokens: {\n type: \"number\",\n min: 0,\n default: 0,\n description: \"Minimum tokens\",\n },\n },\n cohere: {\n temperature: {\n type: \"number\",\n min: 0,\n max: 1,\n default: 0.7,\n description: \"Controls randomness\",\n },\n max_tokens: {\n type: \"number\",\n min: 1,\n default: 4096,\n description: \"Maximum output tokens\",\n },\n p: {\n type: \"number\",\n min: 0,\n max: 1,\n default: 1,\n description: \"Nucleus sampling (p)\",\n },\n k: {\n type: \"number\",\n min: 0,\n max: 500,\n default: 40,\n description: \"Top-K sampling (k)\",\n },\n frequency_penalty: {\n type: \"number\",\n min: 0,\n max: 1,\n default: 0,\n description: \"Penalize frequent tokens\",\n },\n presence_penalty: {\n type: \"number\",\n min: 0,\n max: 1,\n default: 0,\n description: \"Penalize repeated topics\",\n },\n stop_sequences: { type: \"string\", description: \"Stop sequences\" },\n stream: { type: \"boolean\", default: false, description: \"Stream response\" },\n seed: { type: \"number\", description: \"Random seed\" },\n },\n bedrock: {\n // Converse API inferenceConfig params\n temperature: {\n type: \"number\",\n min: 0,\n max: 1,\n default: 0.7,\n description: \"Controls randomness\",\n },\n maxTokens: {\n type: \"number\",\n min: 1,\n default: 4096,\n description: \"Maximum output tokens\",\n },\n topP: {\n type: \"number\",\n min: 0,\n max: 1,\n default: 1,\n description: \"Nucleus sampling\",\n },\n topK: {\n type: \"number\",\n min: 0,\n default: 40,\n description: \"Top-K sampling\",\n },\n stopSequences: { type: \"string\", description: \"Stop sequences\" },\n stream: { type: \"boolean\", default: false, description: \"Stream response\" },\n cache_control: {\n type: \"string\",\n values: [\"ephemeral\"],\n default: \"ephemeral\",\n description: \"Cache control\",\n },\n cache_ttl: {\n type: \"string\",\n values: [\"5m\", \"1h\"],\n default: \"5m\",\n description: \"Cache TTL\",\n },\n },\n openrouter: {\n // Loose validation — proxies to many providers with varying ranges\n temperature: {\n type: \"number\",\n min: 0,\n max: 2,\n default: 0.7,\n description: \"Controls randomness\",\n },\n max_tokens: {\n type: \"number\",\n min: 1,\n default: 4096,\n description: \"Maximum output tokens\",\n },\n top_p: {\n type: \"number\",\n min: 0,\n max: 1,\n default: 1,\n description: \"Nucleus sampling\",\n },\n top_k: {\n type: \"number\",\n min: 0,\n default: 40,\n description: \"Top-K sampling\",\n },\n frequency_penalty: {\n type: \"number\",\n min: -2,\n max: 2,\n default: 0,\n description: \"Penalize frequent tokens\",\n },\n presence_penalty: {\n type: \"number\",\n min: -2,\n max: 2,\n default: 0,\n description: \"Penalize repeated topics\",\n },\n stop: { type: \"string\", description: \"Stop sequences\" },\n n: { type: \"number\", min: 1, default: 1, description: \"Completions count\" },\n seed: { type: \"number\", description: \"Random seed\" },\n stream: { type: \"boolean\", default: false, description: \"Stream response\" },\n reasoning_effort: {\n type: \"string\",\n values: [\"none\", \"minimal\", \"low\", \"medium\", \"high\", \"xhigh\"],\n default: \"medium\",\n description: \"Reasoning effort\",\n },\n },\n vercel: {\n // Loose validation — proxies to many providers with varying ranges\n temperature: {\n type: \"number\",\n min: 0,\n max: 2,\n default: 0.7,\n description: \"Controls randomness\",\n },\n max_tokens: {\n type: \"number\",\n min: 1,\n default: 4096,\n description: \"Maximum output tokens\",\n },\n top_p: {\n type: \"number\",\n min: 0,\n max: 1,\n default: 1,\n description: \"Nucleus sampling\",\n },\n top_k: {\n type: \"number\",\n min: 0,\n default: 40,\n description: \"Top-K sampling\",\n },\n frequency_penalty: {\n type: \"number\",\n min: -2,\n max: 2,\n default: 0,\n description: \"Penalize frequent tokens\",\n },\n presence_penalty: {\n type: \"number\",\n min: -2,\n max: 2,\n default: 0,\n description: \"Penalize repeated topics\",\n },\n stop: { type: \"string\", description: \"Stop sequences\" },\n n: { type: \"number\", min: 1, default: 1, description: \"Completions count\" },\n seed: { type: \"number\", description: \"Random seed\" },\n stream: { type: \"boolean\", default: false, description: \"Stream response\" },\n reasoning_effort: {\n type: \"string\",\n values: [\"none\", \"minimal\", \"low\", \"medium\", \"high\", \"xhigh\"],\n default: \"medium\",\n description: \"Reasoning effort\",\n },\n },\n xai: OPENAI_COMPATIBLE_PARAM_SPECS,\n groq: OPENAI_COMPATIBLE_PARAM_SPECS,\n fal: {},\n deepinfra: OPENAI_COMPATIBLE_PARAM_SPECS,\n \"black-forest-labs\": {},\n together: OPENAI_COMPATIBLE_PARAM_SPECS,\n fireworks: OPENAI_COMPATIBLE_PARAM_SPECS,\n deepseek: OPENAI_COMPATIBLE_PARAM_SPECS,\n moonshotai: OPENAI_COMPATIBLE_PARAM_SPECS,\n perplexity: OPENAI_COMPATIBLE_PARAM_SPECS,\n alibaba: OPENAI_COMPATIBLE_PARAM_SPECS,\n cerebras: OPENAI_COMPATIBLE_PARAM_SPECS,\n replicate: {},\n prodia: {},\n luma: {},\n bytedance: {},\n kling: {},\n elevenlabs: {},\n assemblyai: {},\n deepgram: {},\n gladia: {},\n lmnt: {},\n hume: {},\n revai: {},\n baseten: OPENAI_COMPATIBLE_PARAM_SPECS,\n huggingface: OPENAI_COMPATIBLE_PARAM_SPECS,\n};\n\n/** OpenAI reasoning models don't support standard sampling params. */\nexport function isReasoningModel(model: string): boolean {\n // Strip gateway prefix: \"openai/o3\" → \"o3\"\n const name = model.includes(\"/\") ? model.split(\"/\").pop()! : model;\n return /^o[134]/.test(name);\n}\n\n/** Providers that can route to OpenAI models (and need reasoning-model checks). */\nexport function canHostOpenAIModels(provider: Provider): boolean {\n return (\n provider === \"openai\" || provider === \"openrouter\" || provider === \"vercel\"\n );\n}\n\n/** Whether this provider is a gateway/router that proxies to other providers. */\nexport function isGatewayProvider(provider: Provider): boolean {\n return provider === \"openrouter\" || provider === \"vercel\";\n}\n\n/**\n * Extract the underlying provider from a gateway model string.\n * e.g. \"anthropic/claude-sonnet-4-5\" → \"anthropic\"\n * Returns undefined for unknown prefixes (qwen, deepseek, etc.) or models without \"/\".\n */\nexport function detectGatewaySubProvider(model: string): Provider | undefined {\n const slash = model.indexOf(\"/\");\n if (slash < 1) return undefined;\n const prefix = model.slice(0, slash);\n const direct: Provider[] = [\n \"openai\",\n \"anthropic\",\n \"google\",\n \"mistral\",\n \"cohere\",\n ];\n return direct.find((p) => p === prefix);\n}\n\nexport const REASONING_MODEL_UNSUPPORTED = new Set([\n \"temperature\",\n \"top_p\",\n \"frequency_penalty\",\n \"presence_penalty\",\n \"n\",\n]);\n\n/**\n * Bedrock model IDs are prefixed with the vendor name.\n * e.g. \"anthropic.claude-sonnet-4-5-20250929-v1:0\"\n */\nexport type BedrockModelFamily =\n | \"anthropic\"\n | \"meta\"\n | \"amazon\"\n | \"mistral\"\n | \"cohere\"\n | \"ai21\";\n\nexport function detectBedrockModelFamily(\n model: string,\n): BedrockModelFamily | undefined {\n // Handle cross-region inference profiles (e.g. \"us.anthropic.claude-sonnet-4-5...\")\n // and global inference profiles (e.g. \"global.anthropic.claude-sonnet-4-5...\")\n const parts = model.split(\".\");\n\n // If first part is a region prefix (us, eu, apac) or global, skip it\n let prefix = parts[0];\n if ([\"us\", \"eu\", \"apac\", \"global\"].includes(prefix) && parts.length > 1) {\n prefix = parts[1];\n }\n\n const families: BedrockModelFamily[] = [\n \"anthropic\",\n \"meta\",\n \"amazon\",\n \"mistral\",\n \"cohere\",\n \"ai21\",\n ];\n return families.find((f) => prefix === f);\n}\n\n/** Whether a Bedrock model supports prompt caching (Claude and Nova only). */\nexport function bedrockSupportsCaching(model: string): boolean {\n const family = detectBedrockModelFamily(model);\n if (family === \"anthropic\") return true;\n if (family === \"amazon\" && model.includes(\"nova\")) return true;\n return false;\n}\n\n/** Cache value normalization per provider. */\nexport const CACHE_VALUES: Record<Provider, string | undefined> = {\n openai: undefined, // OpenAI auto-caches; no explicit param\n azure: undefined,\n anthropic: \"ephemeral\",\n google: undefined, // Google uses explicit caching API, not a param\n \"google-vertex\": undefined,\n mistral: undefined,\n cohere: undefined,\n bedrock: \"ephemeral\", // Supported for Claude models on Bedrock\n openrouter: undefined, // Depends on underlying provider\n vercel: undefined, // Depends on underlying provider\n xai: undefined,\n groq: undefined,\n fal: undefined,\n deepinfra: undefined,\n \"black-forest-labs\": undefined,\n together: undefined,\n fireworks: undefined,\n deepseek: undefined,\n moonshotai: undefined,\n perplexity: undefined,\n alibaba: undefined,\n cerebras: undefined,\n replicate: undefined,\n prodia: undefined,\n luma: undefined,\n bytedance: undefined,\n kling: undefined,\n elevenlabs: undefined,\n assemblyai: undefined,\n deepgram: undefined,\n gladia: undefined,\n lmnt: undefined,\n hume: undefined,\n revai: undefined,\n baseten: undefined,\n huggingface: undefined,\n};\n\n/** Valid cache TTL values per provider. */\nexport const CACHE_TTLS: Record<Provider, string[] | undefined> = {\n openai: undefined,\n azure: undefined,\n anthropic: [\"5m\", \"1h\"],\n google: undefined,\n \"google-vertex\": undefined,\n mistral: undefined,\n cohere: undefined,\n bedrock: [\"5m\", \"1h\"], // Claude on Bedrock uses same TTLs as direct Anthropic\n openrouter: undefined,\n vercel: undefined,\n xai: undefined,\n groq: undefined,\n fal: undefined,\n deepinfra: undefined,\n \"black-forest-labs\": undefined,\n together: undefined,\n fireworks: undefined,\n deepseek: undefined,\n moonshotai: undefined,\n perplexity: undefined,\n alibaba: undefined,\n cerebras: undefined,\n replicate: undefined,\n prodia: undefined,\n luma: undefined,\n bytedance: undefined,\n kling: undefined,\n elevenlabs: undefined,\n assemblyai: undefined,\n deepgram: undefined,\n gladia: undefined,\n lmnt: undefined,\n hume: undefined,\n revai: undefined,\n baseten: undefined,\n huggingface: undefined,\n};\n\n/** Match a duration expression like \"5m\", \"1h\", \"30m\". */\nexport const DURATION_RE = /^\\d+[mh]$/;\n"],"mappings":"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","names":[]}
package/dist/parse.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { a as HostAlias } from './provider-core-BiAl8MCV.cjs';
1
+ import { a as HostAlias } from './provider-core-B934MuhJ.cjs';
2
2
 
3
3
  interface LlmConnectionConfig {
4
4
  /** The original connection string */
package/dist/parse.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { a as HostAlias } from './provider-core-BiAl8MCV.js';
1
+ import { a as HostAlias } from './provider-core-B934MuhJ.js';
2
2
 
3
3
  interface LlmConnectionConfig {
4
4
  /** The original connection string */
package/dist/parse.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  build,
3
3
  parse
4
- } from "./chunk-BCOUH7LH.js";
5
- import "./chunk-W4NIQY7M.js";
4
+ } from "./chunk-TQJ2ABCT.js";
5
+ import "./chunk-OCJX4QFJ.js";
6
6
  export {
7
7
  build,
8
8
  parse
@@ -1,5 +1,5 @@
1
- type Provider = "openai" | "anthropic" | "google" | "mistral" | "cohere" | "bedrock" | "openrouter" | "vercel";
2
- type HostAlias = Provider | "aistudio" | "alibaba" | "alibabacloud" | "atlascloud" | "baidu" | "dashscope" | "deepinfra" | "fireworks" | "fireworksai" | "grok" | "minimax" | "novita" | "novitaai" | "parasail" | "qianfan" | "vertex" | "venice" | "wandb" | "weightsandbiases" | "xai" | "xiaomi";
1
+ type Provider = "openai" | "azure" | "anthropic" | "google" | "google-vertex" | "mistral" | "cohere" | "bedrock" | "openrouter" | "vercel" | "xai" | "groq" | "fal" | "deepinfra" | "black-forest-labs" | "together" | "fireworks" | "deepseek" | "moonshotai" | "perplexity" | "alibaba" | "cerebras" | "replicate" | "prodia" | "luma" | "bytedance" | "kling" | "elevenlabs" | "assemblyai" | "deepgram" | "gladia" | "lmnt" | "hume" | "revai" | "baseten" | "huggingface";
2
+ type HostAlias = Provider | "aistudio" | "alibaba" | "alibabacloud" | "atlascloud" | "baidu" | "cerebras" | "dashscope" | "deepinfra" | "deepseek" | "fireworks" | "fireworksai" | "groq" | "grok" | "bfl" | "minimax" | "moonshot" | "novita" | "novitaai" | "parasail" | "perplexity" | "qianfan" | "together" | "togetherai" | "vertex" | "venice" | "wandb" | "weightsandbiases" | "xai" | "xiaomi";
3
3
  type Env = Record<string, string | undefined>;
4
4
  declare const HOST_ALIASES: Record<HostAlias, string>;
5
5
  interface HostResolution {
@@ -1,5 +1,5 @@
1
- type Provider = "openai" | "anthropic" | "google" | "mistral" | "cohere" | "bedrock" | "openrouter" | "vercel";
2
- type HostAlias = Provider | "aistudio" | "alibaba" | "alibabacloud" | "atlascloud" | "baidu" | "dashscope" | "deepinfra" | "fireworks" | "fireworksai" | "grok" | "minimax" | "novita" | "novitaai" | "parasail" | "qianfan" | "vertex" | "venice" | "wandb" | "weightsandbiases" | "xai" | "xiaomi";
1
+ type Provider = "openai" | "azure" | "anthropic" | "google" | "google-vertex" | "mistral" | "cohere" | "bedrock" | "openrouter" | "vercel" | "xai" | "groq" | "fal" | "deepinfra" | "black-forest-labs" | "together" | "fireworks" | "deepseek" | "moonshotai" | "perplexity" | "alibaba" | "cerebras" | "replicate" | "prodia" | "luma" | "bytedance" | "kling" | "elevenlabs" | "assemblyai" | "deepgram" | "gladia" | "lmnt" | "hume" | "revai" | "baseten" | "huggingface";
2
+ type HostAlias = Provider | "aistudio" | "alibaba" | "alibabacloud" | "atlascloud" | "baidu" | "cerebras" | "dashscope" | "deepinfra" | "deepseek" | "fireworks" | "fireworksai" | "groq" | "grok" | "bfl" | "minimax" | "moonshot" | "novita" | "novitaai" | "parasail" | "perplexity" | "qianfan" | "together" | "togetherai" | "vertex" | "venice" | "wandb" | "weightsandbiases" | "xai" | "xiaomi";
3
3
  type Env = Record<string, string | undefined>;
4
4
  declare const HOST_ALIASES: Record<HostAlias, string>;
5
5
  interface HostResolution {