@dreb/ai 2.57.0 → 2.57.1

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.
package/README.md CHANGED
@@ -794,7 +794,7 @@ interface OpenAICompletionsCompat {
794
794
  requiresToolResultName?: boolean; // Whether tool results require the `name` field (default: false)
795
795
  requiresAssistantAfterToolResult?: boolean; // Whether tool results must be followed by an assistant message (default: false)
796
796
  requiresThinkingAsText?: boolean; // Whether thinking blocks must be converted to text (default: false)
797
- thinkingFormat?: 'openai' | 'openrouter' | 'zai' | 'qwen' | 'qwen-chat-template' | 'kimi'; // Format for reasoning param: 'openai' uses reasoning_effort, 'openrouter' uses reasoning: { effort }, 'zai' uses top-level enable_thinking: boolean, 'qwen' uses top-level enable_thinking: boolean, 'qwen-chat-template' uses chat_template_kwargs.enable_thinking, and 'kimi' uses nested thinking: { type, effort? } plus prompt_cache_key (default: openai)
797
+ thinkingFormat?: 'openai' | 'openrouter' | 'zai' | 'qwen' | 'qwen-chat-template' | 'kimi'; // Format for reasoning param: 'openai' uses reasoning_effort, 'openrouter' uses reasoning: { effort }, 'zai' uses top-level enable_thinking: boolean, 'qwen' uses top-level enable_thinking: boolean, 'qwen-chat-template' uses chat_template_kwargs.enable_thinking plus a top-level mapped reasoning_effort (Qwen3.8+ models default-map dreb levels onto the native low/medium/xhigh tiers), and 'kimi' uses nested thinking: { type, effort? } plus prompt_cache_key (default: openai)
798
798
  openRouterRouting?: OpenRouterRouting; // OpenRouter routing preferences (default: {})
799
799
  vercelGatewayRouting?: VercelGatewayRouting; // Vercel AI Gateway routing preferences (default: {})
800
800
  }
package/dist/models.d.ts CHANGED
@@ -41,6 +41,11 @@ export declare function findModelInList(pattern: string, models: Model<Api>[]):
41
41
  export declare function getProviders(): KnownProvider[];
42
42
  export declare function getModels<TProvider extends KnownProvider>(provider: TProvider): Model<ModelApi<TProvider, keyof (typeof MODELS)[TProvider]>>[];
43
43
  export declare function calculateCost<TApi extends Api>(model: Model<TApi>, usage: Usage): Usage["cost"];
44
+ /**
45
+ * Check whether a model id belongs to Qwen 3.8 or a later Qwen generation — the first
46
+ * Qwen family with graded `reasoning_effort` tiers (low/medium/xhigh, default xhigh).
47
+ */
48
+ export declare function isQwen38OrLater(modelId: string): boolean;
44
49
  /**
45
50
  * Check if a model supports xhigh thinking level.
46
51
  *
@@ -48,6 +53,7 @@ export declare function calculateCost<TApi extends Api>(model: Model<TApi>, usag
48
53
  * - GPT-5.2 through GPT-5.6 model families
49
54
  * - Claude Opus 4.6–4.x and Claude 5 model families
50
55
  * - Kimi Code K3 (xhigh maps to its advertised "max" effort)
56
+ * - Qwen 3.8+ model families (xhigh is their top native effort tier)
51
57
  */
52
58
  export declare function supportsXhigh<TApi extends Api>(model: Model<TApi>): boolean;
53
59
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"models.d.ts","sourceRoot":"","sources":["../src/models.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAC/C,OAAO,KAAK,EAAE,GAAG,EAAE,aAAa,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAanE,KAAK,QAAQ,CACZ,SAAS,SAAS,aAAa,EAC/B,QAAQ,SAAS,MAAM,CAAC,OAAO,MAAM,CAAC,CAAC,SAAS,CAAC,IAC9C,CAAC,OAAO,MAAM,CAAC,CAAC,SAAS,CAAC,CAAC,QAAQ,CAAC,SAAS;IAAE,GAAG,EAAE,MAAM,IAAI,CAAA;CAAE,GAAG,CAAC,IAAI,SAAS,GAAG,GAAG,IAAI,GAAG,KAAK,CAAC,GAAG,KAAK,CAAC;AAEjH,wBAAgB,QAAQ,CAAC,SAAS,SAAS,aAAa,EAAE,QAAQ,SAAS,MAAM,CAAC,OAAO,MAAM,CAAC,CAAC,SAAS,CAAC,EAC1G,QAAQ,EAAE,SAAS,EACnB,OAAO,EAAE,QAAQ,GACf,KAAK,CAAC,QAAQ,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC,CAGtC;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAGhD;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,SAAS,CA8BnF;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,GAAG,CAAC,EAAE,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,SAAS,CA4B7F;AAED,wBAAgB,YAAY,IAAI,aAAa,EAAE,CAE9C;AAED,wBAAgB,SAAS,CAAC,SAAS,SAAS,aAAa,EACxD,QAAQ,EAAE,SAAS,GACjB,KAAK,CAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC,OAAO,MAAM,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAGhE;AAED,wBAAgB,aAAa,CAAC,IAAI,SAAS,GAAG,EAAE,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,CAO/F;AAmBD;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,IAAI,SAAS,GAAG,EAAE,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,GAAG,OAAO,CAkB3E;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,SAAS,GAAG,EAAE,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,GAAG,OAAO,CAGtF;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,IAAI,SAAS,GAAG,EAC9C,CAAC,EAAE,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,GAAG,SAAS,EACjC,CAAC,EAAE,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,GAAG,SAAS,GAC/B,OAAO,CAGT","sourcesContent":["import { MODELS } from \"./models.generated.js\";\nimport type { Api, KnownProvider, Model, Usage } from \"./types.js\";\n\nconst modelRegistry: Map<string, Map<string, Model<Api>>> = new Map();\n\n// Initialize registry from MODELS on module load\nfor (const [provider, models] of Object.entries(MODELS)) {\n\tconst providerModels = new Map<string, Model<Api>>();\n\tfor (const [id, model] of Object.entries(models)) {\n\t\tproviderModels.set(id, model as Model<Api>);\n\t}\n\tmodelRegistry.set(provider, providerModels);\n}\n\ntype ModelApi<\n\tTProvider extends KnownProvider,\n\tTModelId extends keyof (typeof MODELS)[TProvider],\n> = (typeof MODELS)[TProvider][TModelId] extends { api: infer TApi } ? (TApi extends Api ? TApi : never) : never;\n\nexport function getModel<TProvider extends KnownProvider, TModelId extends keyof (typeof MODELS)[TProvider]>(\n\tprovider: TProvider,\n\tmodelId: TModelId,\n): Model<ModelApi<TProvider, TModelId>> {\n\tconst providerModels = modelRegistry.get(provider);\n\treturn providerModels?.get(modelId as string) as Model<ModelApi<TProvider, TModelId>>;\n}\n\n/**\n * Check if a model ID looks like an alias (no date suffix).\n * Aliases are preferred over dated versions when fuzzy matching.\n *\n * IDs ending with `-latest` are treated as aliases.\n * IDs ending with a date pattern (`-YYYYMMDD`) are treated as dated versions.\n */\nexport function isModelAlias(id: string): boolean {\n\tif (id.endsWith(\"-latest\")) return true;\n\treturn !/-\\d{8}$/.test(id);\n}\n\n/**\n * Find a model by fuzzy matching against the provider's registered models.\n *\n * Resolution order:\n * 1. Exact match by provider + model ID (via registry Map.get)\n * 2. Case-insensitive substring match against model ID and display name\n * 3. Among matches, prefer aliases (non-dated IDs) over dated versions\n * 4. Among ties, pick the lexicographically highest (latest) ID\n *\n * This is the same matching logic used by the CLI, subagent model resolution,\n * and interactive mode — centralised here so tests can exercise the real path.\n *\n * @example\n * findModel(\"anthropic\", \"sonnet\") // → latest claude-sonnet alias\n * findModel(\"anthropic\", \"haiku\") // → latest claude-haiku alias\n * findModel(\"openai\", \"gpt-5\") // → latest gpt-5 alias\n */\nexport function findModel(provider: string, pattern: string): Model<Api> | undefined {\n\tconst providerModels = modelRegistry.get(provider);\n\tif (!providerModels) return undefined;\n\n\t// Try exact match first\n\tconst exact = providerModels.get(pattern);\n\tif (exact) return exact;\n\n\t// Substring match (case-insensitive)\n\tconst normalizedPattern = pattern.toLowerCase();\n\tconst matches = Array.from(providerModels.values()).filter(\n\t\t(m) => m.id.toLowerCase().includes(normalizedPattern) || m.name?.toLowerCase().includes(normalizedPattern),\n\t);\n\n\tif (matches.length === 0) return undefined;\n\tif (matches.length === 1) return matches[0];\n\n\t// Multiple matches — separate into aliases and dated versions\n\tconst aliases = matches.filter((m) => isModelAlias(m.id));\n\tconst datedVersions = matches.filter((m) => !isModelAlias(m.id));\n\n\tif (aliases.length > 0) {\n\t\t// Prefer alias — if multiple, pick the lexicographically highest\n\t\taliases.sort((a, b) => b.id.localeCompare(a.id));\n\t\treturn aliases[0];\n\t}\n\n\t// All dated — prefer the latest\n\tdatedVersions.sort((a, b) => b.id.localeCompare(a.id));\n\treturn datedVersions[0];\n}\n\n/**\n * Find a model by fuzzy matching against a flat array of models.\n * Same algorithm as findModel() but operates on an arbitrary model list\n * instead of the built-in registry.\n *\n * Used by model-resolver.ts and other code that manages its own model lists.\n */\nexport function findModelInList(pattern: string, models: Model<Api>[]): Model<Api> | undefined {\n\tif (models.length === 0) return undefined;\n\n\tconst normalizedPattern = pattern.toLowerCase();\n\n\t// Exact ID match (case-insensitive)\n\tconst exactById = models.find((m) => m.id.toLowerCase() === normalizedPattern);\n\tif (exactById) return exactById;\n\n\t// Substring match (case-insensitive)\n\tconst matches = models.filter(\n\t\t(m) => m.id.toLowerCase().includes(normalizedPattern) || m.name?.toLowerCase().includes(normalizedPattern),\n\t);\n\n\tif (matches.length === 0) return undefined;\n\tif (matches.length === 1) return matches[0];\n\n\t// Multiple matches — separate into aliases and dated versions\n\tconst aliases = matches.filter((m) => isModelAlias(m.id));\n\tconst datedVersions = matches.filter((m) => !isModelAlias(m.id));\n\n\tif (aliases.length > 0) {\n\t\taliases.sort((a, b) => b.id.localeCompare(a.id));\n\t\treturn aliases[0];\n\t}\n\n\tdatedVersions.sort((a, b) => b.id.localeCompare(a.id));\n\treturn datedVersions[0];\n}\n\nexport function getProviders(): KnownProvider[] {\n\treturn Array.from(modelRegistry.keys()) as KnownProvider[];\n}\n\nexport function getModels<TProvider extends KnownProvider>(\n\tprovider: TProvider,\n): Model<ModelApi<TProvider, keyof (typeof MODELS)[TProvider]>>[] {\n\tconst models = modelRegistry.get(provider);\n\treturn models ? (Array.from(models.values()) as Model<ModelApi<TProvider, keyof (typeof MODELS)[TProvider]>>[]) : [];\n}\n\nexport function calculateCost<TApi extends Api>(model: Model<TApi>, usage: Usage): Usage[\"cost\"] {\n\tusage.cost.input = (model.cost.input / 1000000) * usage.input;\n\tusage.cost.output = (model.cost.output / 1000000) * usage.output;\n\tusage.cost.cacheRead = (model.cost.cacheRead / 1000000) * usage.cacheRead;\n\tusage.cost.cacheWrite = (model.cost.cacheWrite / 1000000) * usage.cacheWrite;\n\tusage.cost.total = usage.cost.input + usage.cost.output + usage.cost.cacheRead + usage.cost.cacheWrite;\n\treturn usage.cost;\n}\n\ninterface ClaudeFamilyVersion {\n\tfamily: \"opus\" | \"sonnet\";\n\tmajor: number;\n\tminor?: number;\n}\n\n/** Parse Claude family versions without mistaking long date suffixes for versions. */\nfunction claudeFamilyVersion(modelId: string): ClaudeFamilyVersion | undefined {\n\tconst match = modelId.match(/(opus|sonnet)-(\\d{1,2})(?:[.-](\\d{1,2}))?(?!\\d)/);\n\tif (!match) return undefined;\n\treturn {\n\t\tfamily: match[1] as ClaudeFamilyVersion[\"family\"],\n\t\tmajor: Number.parseInt(match[2], 10),\n\t\tminor: match[3] === undefined ? undefined : Number.parseInt(match[3], 10),\n\t};\n}\n\n/**\n * Check if a model supports xhigh thinking level.\n *\n * Supported today:\n * - GPT-5.2 through GPT-5.6 model families\n * - Claude Opus 4.6–4.x and Claude 5 model families\n * - Kimi Code K3 (xhigh maps to its advertised \"max\" effort)\n */\nexport function supportsXhigh<TApi extends Api>(model: Model<TApi>): boolean {\n\tif (model.provider === \"kimi-coding-oauth\" && model.id === \"k3\") return true;\n\tif (\n\t\tmodel.id.includes(\"gpt-5.2\") ||\n\t\tmodel.id.includes(\"gpt-5.3\") ||\n\t\tmodel.id.includes(\"gpt-5.4\") ||\n\t\tmodel.id.includes(\"gpt-5.5\") ||\n\t\tmodel.id.includes(\"gpt-5.6\")\n\t) {\n\t\treturn true;\n\t}\n\n\tconst claude = claudeFamilyVersion(model.id);\n\tif (!claude) return false;\n\tif (claude.family === \"opus\") {\n\t\treturn claude.major >= 5 || (claude.major === 4 && (claude.minor ?? 0) >= 6);\n\t}\n\treturn claude.major >= 5;\n}\n\n/**\n * Check if a model uses adaptive thinking (Opus/Sonnet 4.6–4.x and Claude 5\n * families), where the `thinkingDisplay` option is honored. Mirrors the\n * per-provider internal checks.\n */\nexport function supportsAdaptiveThinking<TApi extends Api>(model: Model<TApi>): boolean {\n\tconst claude = claudeFamilyVersion(model.id);\n\treturn claude != null && (claude.major >= 5 || (claude.major === 4 && (claude.minor ?? 0) >= 6));\n}\n\n/**\n * Check if two models are equal by comparing both their id and provider.\n * Returns false if either model is null or undefined.\n */\nexport function modelsAreEqual<TApi extends Api>(\n\ta: Model<TApi> | null | undefined,\n\tb: Model<TApi> | null | undefined,\n): boolean {\n\tif (!a || !b) return false;\n\treturn a.id === b.id && a.provider === b.provider;\n}\n"]}
1
+ {"version":3,"file":"models.d.ts","sourceRoot":"","sources":["../src/models.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAC/C,OAAO,KAAK,EAAE,GAAG,EAAE,aAAa,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAanE,KAAK,QAAQ,CACZ,SAAS,SAAS,aAAa,EAC/B,QAAQ,SAAS,MAAM,CAAC,OAAO,MAAM,CAAC,CAAC,SAAS,CAAC,IAC9C,CAAC,OAAO,MAAM,CAAC,CAAC,SAAS,CAAC,CAAC,QAAQ,CAAC,SAAS;IAAE,GAAG,EAAE,MAAM,IAAI,CAAA;CAAE,GAAG,CAAC,IAAI,SAAS,GAAG,GAAG,IAAI,GAAG,KAAK,CAAC,GAAG,KAAK,CAAC;AAEjH,wBAAgB,QAAQ,CAAC,SAAS,SAAS,aAAa,EAAE,QAAQ,SAAS,MAAM,CAAC,OAAO,MAAM,CAAC,CAAC,SAAS,CAAC,EAC1G,QAAQ,EAAE,SAAS,EACnB,OAAO,EAAE,QAAQ,GACf,KAAK,CAAC,QAAQ,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC,CAGtC;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAGhD;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,SAAS,CA8BnF;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,GAAG,CAAC,EAAE,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,SAAS,CA4B7F;AAED,wBAAgB,YAAY,IAAI,aAAa,EAAE,CAE9C;AAED,wBAAgB,SAAS,CAAC,SAAS,SAAS,aAAa,EACxD,QAAQ,EAAE,SAAS,GACjB,KAAK,CAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC,OAAO,MAAM,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAGhE;AAED,wBAAgB,aAAa,CAAC,IAAI,SAAS,GAAG,EAAE,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,CAO/F;AAoCD;;;GAGG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAIxD;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,IAAI,SAAS,GAAG,EAAE,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,GAAG,OAAO,CAmB3E;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,SAAS,GAAG,EAAE,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,GAAG,OAAO,CAGtF;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,IAAI,SAAS,GAAG,EAC9C,CAAC,EAAE,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,GAAG,SAAS,EACjC,CAAC,EAAE,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,GAAG,SAAS,GAC/B,OAAO,CAGT","sourcesContent":["import { MODELS } from \"./models.generated.js\";\nimport type { Api, KnownProvider, Model, Usage } from \"./types.js\";\n\nconst modelRegistry: Map<string, Map<string, Model<Api>>> = new Map();\n\n// Initialize registry from MODELS on module load\nfor (const [provider, models] of Object.entries(MODELS)) {\n\tconst providerModels = new Map<string, Model<Api>>();\n\tfor (const [id, model] of Object.entries(models)) {\n\t\tproviderModels.set(id, model as Model<Api>);\n\t}\n\tmodelRegistry.set(provider, providerModels);\n}\n\ntype ModelApi<\n\tTProvider extends KnownProvider,\n\tTModelId extends keyof (typeof MODELS)[TProvider],\n> = (typeof MODELS)[TProvider][TModelId] extends { api: infer TApi } ? (TApi extends Api ? TApi : never) : never;\n\nexport function getModel<TProvider extends KnownProvider, TModelId extends keyof (typeof MODELS)[TProvider]>(\n\tprovider: TProvider,\n\tmodelId: TModelId,\n): Model<ModelApi<TProvider, TModelId>> {\n\tconst providerModels = modelRegistry.get(provider);\n\treturn providerModels?.get(modelId as string) as Model<ModelApi<TProvider, TModelId>>;\n}\n\n/**\n * Check if a model ID looks like an alias (no date suffix).\n * Aliases are preferred over dated versions when fuzzy matching.\n *\n * IDs ending with `-latest` are treated as aliases.\n * IDs ending with a date pattern (`-YYYYMMDD`) are treated as dated versions.\n */\nexport function isModelAlias(id: string): boolean {\n\tif (id.endsWith(\"-latest\")) return true;\n\treturn !/-\\d{8}$/.test(id);\n}\n\n/**\n * Find a model by fuzzy matching against the provider's registered models.\n *\n * Resolution order:\n * 1. Exact match by provider + model ID (via registry Map.get)\n * 2. Case-insensitive substring match against model ID and display name\n * 3. Among matches, prefer aliases (non-dated IDs) over dated versions\n * 4. Among ties, pick the lexicographically highest (latest) ID\n *\n * This is the same matching logic used by the CLI, subagent model resolution,\n * and interactive mode — centralised here so tests can exercise the real path.\n *\n * @example\n * findModel(\"anthropic\", \"sonnet\") // → latest claude-sonnet alias\n * findModel(\"anthropic\", \"haiku\") // → latest claude-haiku alias\n * findModel(\"openai\", \"gpt-5\") // → latest gpt-5 alias\n */\nexport function findModel(provider: string, pattern: string): Model<Api> | undefined {\n\tconst providerModels = modelRegistry.get(provider);\n\tif (!providerModels) return undefined;\n\n\t// Try exact match first\n\tconst exact = providerModels.get(pattern);\n\tif (exact) return exact;\n\n\t// Substring match (case-insensitive)\n\tconst normalizedPattern = pattern.toLowerCase();\n\tconst matches = Array.from(providerModels.values()).filter(\n\t\t(m) => m.id.toLowerCase().includes(normalizedPattern) || m.name?.toLowerCase().includes(normalizedPattern),\n\t);\n\n\tif (matches.length === 0) return undefined;\n\tif (matches.length === 1) return matches[0];\n\n\t// Multiple matches — separate into aliases and dated versions\n\tconst aliases = matches.filter((m) => isModelAlias(m.id));\n\tconst datedVersions = matches.filter((m) => !isModelAlias(m.id));\n\n\tif (aliases.length > 0) {\n\t\t// Prefer alias — if multiple, pick the lexicographically highest\n\t\taliases.sort((a, b) => b.id.localeCompare(a.id));\n\t\treturn aliases[0];\n\t}\n\n\t// All dated — prefer the latest\n\tdatedVersions.sort((a, b) => b.id.localeCompare(a.id));\n\treturn datedVersions[0];\n}\n\n/**\n * Find a model by fuzzy matching against a flat array of models.\n * Same algorithm as findModel() but operates on an arbitrary model list\n * instead of the built-in registry.\n *\n * Used by model-resolver.ts and other code that manages its own model lists.\n */\nexport function findModelInList(pattern: string, models: Model<Api>[]): Model<Api> | undefined {\n\tif (models.length === 0) return undefined;\n\n\tconst normalizedPattern = pattern.toLowerCase();\n\n\t// Exact ID match (case-insensitive)\n\tconst exactById = models.find((m) => m.id.toLowerCase() === normalizedPattern);\n\tif (exactById) return exactById;\n\n\t// Substring match (case-insensitive)\n\tconst matches = models.filter(\n\t\t(m) => m.id.toLowerCase().includes(normalizedPattern) || m.name?.toLowerCase().includes(normalizedPattern),\n\t);\n\n\tif (matches.length === 0) return undefined;\n\tif (matches.length === 1) return matches[0];\n\n\t// Multiple matches — separate into aliases and dated versions\n\tconst aliases = matches.filter((m) => isModelAlias(m.id));\n\tconst datedVersions = matches.filter((m) => !isModelAlias(m.id));\n\n\tif (aliases.length > 0) {\n\t\taliases.sort((a, b) => b.id.localeCompare(a.id));\n\t\treturn aliases[0];\n\t}\n\n\tdatedVersions.sort((a, b) => b.id.localeCompare(a.id));\n\treturn datedVersions[0];\n}\n\nexport function getProviders(): KnownProvider[] {\n\treturn Array.from(modelRegistry.keys()) as KnownProvider[];\n}\n\nexport function getModels<TProvider extends KnownProvider>(\n\tprovider: TProvider,\n): Model<ModelApi<TProvider, keyof (typeof MODELS)[TProvider]>>[] {\n\tconst models = modelRegistry.get(provider);\n\treturn models ? (Array.from(models.values()) as Model<ModelApi<TProvider, keyof (typeof MODELS)[TProvider]>>[]) : [];\n}\n\nexport function calculateCost<TApi extends Api>(model: Model<TApi>, usage: Usage): Usage[\"cost\"] {\n\tusage.cost.input = (model.cost.input / 1000000) * usage.input;\n\tusage.cost.output = (model.cost.output / 1000000) * usage.output;\n\tusage.cost.cacheRead = (model.cost.cacheRead / 1000000) * usage.cacheRead;\n\tusage.cost.cacheWrite = (model.cost.cacheWrite / 1000000) * usage.cacheWrite;\n\tusage.cost.total = usage.cost.input + usage.cost.output + usage.cost.cacheRead + usage.cost.cacheWrite;\n\treturn usage.cost;\n}\n\ninterface ClaudeFamilyVersion {\n\tfamily: \"opus\" | \"sonnet\";\n\tmajor: number;\n\tminor?: number;\n}\n\n/** Parse Claude family versions without mistaking long date suffixes for versions. */\nfunction claudeFamilyVersion(modelId: string): ClaudeFamilyVersion | undefined {\n\tconst match = modelId.match(/(opus|sonnet)-(\\d{1,2})(?:[.-](\\d{1,2}))?(?!\\d)/);\n\tif (!match) return undefined;\n\treturn {\n\t\tfamily: match[1] as ClaudeFamilyVersion[\"family\"],\n\t\tmajor: Number.parseInt(match[2], 10),\n\t\tminor: match[3] === undefined ? undefined : Number.parseInt(match[3], 10),\n\t};\n}\n\n/**\n * Parse Qwen family versions without mistaking parameter counts for minor versions.\n * Only a dot separates major from minor (qwen3.8-27b, Qwen-3.8); a hyphen after the\n * major version introduces the parameter size (qwen3-32b → 3.0, not 3.32). A capture\n * directly followed by a `b`-suffixed size token is a parameter count, not a version\n * (qwen-32b, Qwen-7B-Chat, deepseek-r1-distill-qwen-32b), and Qwen majors never reach\n * two digits, so only a single digit is accepted.\n */\nfunction qwenFamilyVersion(modelId: string): { major: number; minor: number } | undefined {\n\tconst match = modelId.match(/\\bqwen[\\s_-]*v?(\\d)(?:\\.(\\d+))?(?![\\d.]*b\\b)/i);\n\tif (!match) return undefined;\n\treturn {\n\t\tmajor: Number.parseInt(match[1], 10),\n\t\tminor: match[2] === undefined ? 0 : Number.parseInt(match[2], 10),\n\t};\n}\n\n/**\n * Check whether a model id belongs to Qwen 3.8 or a later Qwen generation — the first\n * Qwen family with graded `reasoning_effort` tiers (low/medium/xhigh, default xhigh).\n */\nexport function isQwen38OrLater(modelId: string): boolean {\n\tconst qwen = qwenFamilyVersion(modelId);\n\tif (!qwen) return false;\n\treturn qwen.major > 3 || (qwen.major === 3 && qwen.minor >= 8);\n}\n\n/**\n * Check if a model supports xhigh thinking level.\n *\n * Supported today:\n * - GPT-5.2 through GPT-5.6 model families\n * - Claude Opus 4.6–4.x and Claude 5 model families\n * - Kimi Code K3 (xhigh maps to its advertised \"max\" effort)\n * - Qwen 3.8+ model families (xhigh is their top native effort tier)\n */\nexport function supportsXhigh<TApi extends Api>(model: Model<TApi>): boolean {\n\tif (model.provider === \"kimi-coding-oauth\" && model.id === \"k3\") return true;\n\tif (isQwen38OrLater(model.id)) return true;\n\tif (\n\t\tmodel.id.includes(\"gpt-5.2\") ||\n\t\tmodel.id.includes(\"gpt-5.3\") ||\n\t\tmodel.id.includes(\"gpt-5.4\") ||\n\t\tmodel.id.includes(\"gpt-5.5\") ||\n\t\tmodel.id.includes(\"gpt-5.6\")\n\t) {\n\t\treturn true;\n\t}\n\n\tconst claude = claudeFamilyVersion(model.id);\n\tif (!claude) return false;\n\tif (claude.family === \"opus\") {\n\t\treturn claude.major >= 5 || (claude.major === 4 && (claude.minor ?? 0) >= 6);\n\t}\n\treturn claude.major >= 5;\n}\n\n/**\n * Check if a model uses adaptive thinking (Opus/Sonnet 4.6–4.x and Claude 5\n * families), where the `thinkingDisplay` option is honored. Mirrors the\n * per-provider internal checks.\n */\nexport function supportsAdaptiveThinking<TApi extends Api>(model: Model<TApi>): boolean {\n\tconst claude = claudeFamilyVersion(model.id);\n\treturn claude != null && (claude.major >= 5 || (claude.major === 4 && (claude.minor ?? 0) >= 6));\n}\n\n/**\n * Check if two models are equal by comparing both their id and provider.\n * Returns false if either model is null or undefined.\n */\nexport function modelsAreEqual<TApi extends Api>(\n\ta: Model<TApi> | null | undefined,\n\tb: Model<TApi> | null | undefined,\n): boolean {\n\tif (!a || !b) return false;\n\treturn a.id === b.id && a.provider === b.provider;\n}\n"]}
@@ -3267,6 +3267,34 @@ export declare const MODELS: {
3267
3267
  contextWindow: number;
3268
3268
  maxTokens: number;
3269
3269
  };
3270
+ readonly "gemini-3.7-flash": {
3271
+ id: string;
3272
+ name: string;
3273
+ api: "openai-completions";
3274
+ provider: string;
3275
+ baseUrl: string;
3276
+ headers: {
3277
+ "User-Agent": string;
3278
+ "Editor-Version": string;
3279
+ "Editor-Plugin-Version": string;
3280
+ "Copilot-Integration-Id": string;
3281
+ };
3282
+ compat: {
3283
+ supportsStore: false;
3284
+ supportsDeveloperRole: false;
3285
+ supportsReasoningEffort: false;
3286
+ };
3287
+ reasoning: true;
3288
+ input: ("image" | "text")[];
3289
+ cost: {
3290
+ input: number;
3291
+ output: number;
3292
+ cacheRead: number;
3293
+ cacheWrite: number;
3294
+ };
3295
+ contextWindow: number;
3296
+ maxTokens: number;
3297
+ };
3270
3298
  readonly "gpt-4.1": {
3271
3299
  id: string;
3272
3300
  name: string;
@@ -3576,6 +3604,34 @@ export declare const MODELS: {
3576
3604
  contextWindow: number;
3577
3605
  maxTokens: number;
3578
3606
  };
3607
+ readonly "grok-4.6": {
3608
+ id: string;
3609
+ name: string;
3610
+ api: "openai-completions";
3611
+ provider: string;
3612
+ baseUrl: string;
3613
+ headers: {
3614
+ "User-Agent": string;
3615
+ "Editor-Version": string;
3616
+ "Editor-Plugin-Version": string;
3617
+ "Copilot-Integration-Id": string;
3618
+ };
3619
+ compat: {
3620
+ supportsStore: false;
3621
+ supportsDeveloperRole: false;
3622
+ supportsReasoningEffort: false;
3623
+ };
3624
+ reasoning: true;
3625
+ input: ("image" | "text")[];
3626
+ cost: {
3627
+ input: number;
3628
+ output: number;
3629
+ cacheRead: number;
3630
+ cacheWrite: number;
3631
+ };
3632
+ contextWindow: number;
3633
+ maxTokens: number;
3634
+ };
3579
3635
  readonly "kimi-k2.7-code": {
3580
3636
  id: string;
3581
3637
  name: string;
@@ -4735,6 +4791,26 @@ export declare const MODELS: {
4735
4791
  contextWindow: number;
4736
4792
  maxTokens: number;
4737
4793
  };
4794
+ readonly "Qwen/Qwen2.5-Coder-32B-Instruct": {
4795
+ id: string;
4796
+ name: string;
4797
+ api: "openai-completions";
4798
+ provider: string;
4799
+ baseUrl: string;
4800
+ compat: {
4801
+ supportsDeveloperRole: false;
4802
+ };
4803
+ reasoning: false;
4804
+ input: "text"[];
4805
+ cost: {
4806
+ input: number;
4807
+ output: number;
4808
+ cacheRead: number;
4809
+ cacheWrite: number;
4810
+ };
4811
+ contextWindow: number;
4812
+ maxTokens: number;
4813
+ };
4738
4814
  readonly "Qwen/Qwen3-235B-A22B": {
4739
4815
  id: string;
4740
4816
  name: string;
@@ -4795,6 +4871,26 @@ export declare const MODELS: {
4795
4871
  contextWindow: number;
4796
4872
  maxTokens: number;
4797
4873
  };
4874
+ readonly "Qwen/Qwen3-30B-A3B": {
4875
+ id: string;
4876
+ name: string;
4877
+ api: "openai-completions";
4878
+ provider: string;
4879
+ baseUrl: string;
4880
+ compat: {
4881
+ supportsDeveloperRole: false;
4882
+ };
4883
+ reasoning: true;
4884
+ input: "text"[];
4885
+ cost: {
4886
+ input: number;
4887
+ output: number;
4888
+ cacheRead: number;
4889
+ cacheWrite: number;
4890
+ };
4891
+ contextWindow: number;
4892
+ maxTokens: number;
4893
+ };
4798
4894
  readonly "Qwen/Qwen3-32B": {
4799
4895
  id: string;
4800
4896
  name: string;
@@ -5175,6 +5271,26 @@ export declare const MODELS: {
5175
5271
  contextWindow: number;
5176
5272
  maxTokens: number;
5177
5273
  };
5274
+ readonly "deepseek-ai/DeepSeek-V3-0324": {
5275
+ id: string;
5276
+ name: string;
5277
+ api: "openai-completions";
5278
+ provider: string;
5279
+ baseUrl: string;
5280
+ compat: {
5281
+ supportsDeveloperRole: false;
5282
+ };
5283
+ reasoning: false;
5284
+ input: "text"[];
5285
+ cost: {
5286
+ input: number;
5287
+ output: number;
5288
+ cacheRead: number;
5289
+ cacheWrite: number;
5290
+ };
5291
+ contextWindow: number;
5292
+ maxTokens: number;
5293
+ };
5178
5294
  readonly "deepseek-ai/DeepSeek-V3.1": {
5179
5295
  id: string;
5180
5296
  name: string;
@@ -5275,6 +5391,26 @@ export declare const MODELS: {
5275
5391
  contextWindow: number;
5276
5392
  maxTokens: number;
5277
5393
  };
5394
+ readonly "deepseek-ai/DeepSeek-V4-Pro-0813": {
5395
+ id: string;
5396
+ name: string;
5397
+ api: "openai-completions";
5398
+ provider: string;
5399
+ baseUrl: string;
5400
+ compat: {
5401
+ supportsDeveloperRole: false;
5402
+ };
5403
+ reasoning: true;
5404
+ input: "text"[];
5405
+ cost: {
5406
+ input: number;
5407
+ output: number;
5408
+ cacheRead: number;
5409
+ cacheWrite: number;
5410
+ };
5411
+ contextWindow: number;
5412
+ maxTokens: number;
5413
+ };
5278
5414
  readonly "google/gemma-4-26B-A4B-it": {
5279
5415
  id: string;
5280
5416
  name: string;
@@ -5315,6 +5451,26 @@ export declare const MODELS: {
5315
5451
  contextWindow: number;
5316
5452
  maxTokens: number;
5317
5453
  };
5454
+ readonly "meta-llama/Llama-3.1-8B-Instruct": {
5455
+ id: string;
5456
+ name: string;
5457
+ api: "openai-completions";
5458
+ provider: string;
5459
+ baseUrl: string;
5460
+ compat: {
5461
+ supportsDeveloperRole: false;
5462
+ };
5463
+ reasoning: false;
5464
+ input: "text"[];
5465
+ cost: {
5466
+ input: number;
5467
+ output: number;
5468
+ cacheRead: number;
5469
+ cacheWrite: number;
5470
+ };
5471
+ contextWindow: number;
5472
+ maxTokens: number;
5473
+ };
5318
5474
  readonly "meta-llama/Llama-3.3-70B-Instruct": {
5319
5475
  id: string;
5320
5476
  name: string;
@@ -8411,6 +8567,23 @@ export declare const MODELS: {
8411
8567
  contextWindow: number;
8412
8568
  maxTokens: number;
8413
8569
  };
8570
+ readonly "muse-spark-1.2": {
8571
+ id: string;
8572
+ name: string;
8573
+ api: "openai-responses";
8574
+ provider: string;
8575
+ baseUrl: string;
8576
+ reasoning: true;
8577
+ input: ("image" | "text")[];
8578
+ cost: {
8579
+ input: number;
8580
+ output: number;
8581
+ cacheRead: number;
8582
+ cacheWrite: number;
8583
+ };
8584
+ contextWindow: number;
8585
+ maxTokens: number;
8586
+ };
8414
8587
  readonly "nemotron-3-ultra-free": {
8415
8588
  id: string;
8416
8589
  name: string;
@@ -8549,6 +8722,23 @@ export declare const MODELS: {
8549
8722
  contextWindow: number;
8550
8723
  maxTokens: number;
8551
8724
  };
8725
+ readonly "glm-5.3": {
8726
+ id: string;
8727
+ name: string;
8728
+ api: "openai-completions";
8729
+ provider: string;
8730
+ baseUrl: string;
8731
+ reasoning: true;
8732
+ input: "text"[];
8733
+ cost: {
8734
+ input: number;
8735
+ output: number;
8736
+ cacheRead: number;
8737
+ cacheWrite: number;
8738
+ };
8739
+ contextWindow: number;
8740
+ maxTokens: number;
8741
+ };
8552
8742
  readonly "gpt-5.6-luna": {
8553
8743
  id: string;
8554
8744
  name: string;
@@ -9826,6 +10016,23 @@ export declare const MODELS: {
9826
10016
  contextWindow: number;
9827
10017
  maxTokens: number;
9828
10018
  };
10019
+ readonly "dots-studio/dots-3-note-preview:free": {
10020
+ id: string;
10021
+ name: string;
10022
+ api: "openai-completions";
10023
+ provider: string;
10024
+ baseUrl: string;
10025
+ reasoning: true;
10026
+ input: ("image" | "text")[];
10027
+ cost: {
10028
+ input: number;
10029
+ output: number;
10030
+ cacheRead: number;
10031
+ cacheWrite: number;
10032
+ };
10033
+ contextWindow: number;
10034
+ maxTokens: number;
10035
+ };
9829
10036
  readonly "google/gemini-2.5-flash": {
9830
10037
  id: string;
9831
10038
  name: string;
@@ -13855,6 +14062,23 @@ export declare const MODELS: {
13855
14062
  contextWindow: number;
13856
14063
  maxTokens: number;
13857
14064
  };
14065
+ readonly "qwen/qwen3.8-27b": {
14066
+ id: string;
14067
+ name: string;
14068
+ api: "openai-completions";
14069
+ provider: string;
14070
+ baseUrl: string;
14071
+ reasoning: true;
14072
+ input: ("image" | "text")[];
14073
+ cost: {
14074
+ input: number;
14075
+ output: number;
14076
+ cacheRead: number;
14077
+ cacheWrite: number;
14078
+ };
14079
+ contextWindow: number;
14080
+ maxTokens: number;
14081
+ };
13858
14082
  readonly "qwen/qwen3.8-max": {
13859
14083
  id: string;
13860
14084
  name: string;
@@ -15948,23 +16172,6 @@ export declare const MODELS: {
15948
16172
  contextWindow: number;
15949
16173
  maxTokens: number;
15950
16174
  };
15951
- readonly "inclusionai/ling-3.0-tiny-free": {
15952
- id: string;
15953
- name: string;
15954
- api: "anthropic-messages";
15955
- provider: string;
15956
- baseUrl: string;
15957
- reasoning: true;
15958
- input: "text"[];
15959
- cost: {
15960
- input: number;
15961
- output: number;
15962
- cacheRead: number;
15963
- cacheWrite: number;
15964
- };
15965
- contextWindow: number;
15966
- maxTokens: number;
15967
- };
15968
16175
  readonly "interfaze/interfaze-beta": {
15969
16176
  id: string;
15970
16177
  name: string;
@@ -17774,7 +17981,7 @@ export declare const MODELS: {
17774
17981
  provider: string;
17775
17982
  baseUrl: string;
17776
17983
  reasoning: true;
17777
- input: "text"[];
17984
+ input: ("image" | "text")[];
17778
17985
  cost: {
17779
17986
  input: number;
17780
17987
  output: number;