@linxiraos/pi-catalog 1.0.0 → 1.0.2

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 (86) hide show
  1. package/CHANGELOG.md +88 -6
  2. package/package.json +98 -99
  3. package/src/build.ts +3 -3
  4. package/src/compat/openai.ts +0 -6
  5. package/src/discovery/antigravity.ts +5 -1
  6. package/src/discovery/openai-compatible.ts +21 -4
  7. package/src/identity/classify.ts +32 -9
  8. package/src/identity/family.ts +18 -4
  9. package/src/model-manager.ts +13 -2
  10. package/src/model-thinking.ts +15 -7
  11. package/src/models.json +7999 -2099
  12. package/src/models.ts +28 -14
  13. package/src/provider-models/bundled-references.ts +4 -2
  14. package/src/provider-models/openai-compat.ts +379 -301
  15. package/src/types.ts +24 -16
  16. package/src/variant-collapse.ts +34 -17
  17. package/src/wire/gemini-headers.ts +79 -18
  18. package/dist/types/build.d.ts +0 -14
  19. package/dist/types/compat/anthropic.d.ts +0 -18
  20. package/dist/types/compat/apply.d.ts +0 -7
  21. package/dist/types/compat/bedrock.d.ts +0 -3
  22. package/dist/types/compat/devin.d.ts +0 -9
  23. package/dist/types/compat/openai.d.ts +0 -26
  24. package/dist/types/discovery/antigravity.d.ts +0 -70
  25. package/dist/types/discovery/codex.d.ts +0 -36
  26. package/dist/types/discovery/cursor-gen/agent_pb.d.ts +0 -16769
  27. package/dist/types/discovery/cursor.d.ts +0 -23
  28. package/dist/types/discovery/devin-gen/buf/validate/validate_pb.d.ts +0 -1715
  29. package/dist/types/discovery/devin-gen/exa/analytics_pb/analytics_pb.d.ts +0 -693
  30. package/dist/types/discovery/devin-gen/exa/api_server_pb/api_server_pb.d.ts +0 -9158
  31. package/dist/types/discovery/devin-gen/exa/auth_pb/auth_pb.d.ts +0 -52
  32. package/dist/types/discovery/devin-gen/exa/auto_cascade_common_pb/auto_cascade_common_pb.d.ts +0 -276
  33. package/dist/types/discovery/devin-gen/exa/bug_checker_pb/bug_checker_pb.d.ts +0 -78
  34. package/dist/types/discovery/devin-gen/exa/cascade_plugins_pb/cascade_plugins_pb.d.ts +0 -999
  35. package/dist/types/discovery/devin-gen/exa/chat_pb/chat_pb.d.ts +0 -1704
  36. package/dist/types/discovery/devin-gen/exa/code_edit/code_edit_pb/code_edit_pb.d.ts +0 -656
  37. package/dist/types/discovery/devin-gen/exa/codeium_common_pb/codeium_common_pb.d.ts +0 -15014
  38. package/dist/types/discovery/devin-gen/exa/context_module_pb/context_module_pb.d.ts +0 -607
  39. package/dist/types/discovery/devin-gen/exa/cortex_pb/cortex_pb.d.ts +0 -11830
  40. package/dist/types/discovery/devin-gen/exa/diff_action_pb/diff_action_pb.d.ts +0 -248
  41. package/dist/types/discovery/devin-gen/exa/index_pb/index_pb.d.ts +0 -1747
  42. package/dist/types/discovery/devin-gen/exa/knowledge_base_pb/knowledge_base_pb.d.ts +0 -509
  43. package/dist/types/discovery/devin-gen/exa/language_server_pb/language_server_pb.d.ts +0 -9048
  44. package/dist/types/discovery/devin-gen/exa/opensearch_clients_pb/opensearch_clients_pb.d.ts +0 -1760
  45. package/dist/types/discovery/devin-gen/exa/prompt_pb/prompt_pb.d.ts +0 -286
  46. package/dist/types/discovery/devin-gen/exa/reactive_component_pb/reactive_component_pb.d.ts +0 -405
  47. package/dist/types/discovery/devin-gen/exa/trust_pb/trust_pb.d.ts +0 -582
  48. package/dist/types/discovery/devin.d.ts +0 -24
  49. package/dist/types/discovery/gemini.d.ts +0 -25
  50. package/dist/types/discovery/gitlab-duo-workflow.d.ts +0 -30
  51. package/dist/types/discovery/index.d.ts +0 -5
  52. package/dist/types/discovery/openai-compatible.d.ts +0 -74
  53. package/dist/types/effort.d.ts +0 -10
  54. package/dist/types/fireworks-model-id.d.ts +0 -23
  55. package/dist/types/hosts.d.ts +0 -164
  56. package/dist/types/identity/bundled.d.ts +0 -3
  57. package/dist/types/identity/classify.d.ts +0 -69
  58. package/dist/types/identity/dialect.d.ts +0 -3
  59. package/dist/types/identity/family.d.ts +0 -181
  60. package/dist/types/identity/id.d.ts +0 -12
  61. package/dist/types/identity/index.d.ts +0 -8
  62. package/dist/types/identity/markers.d.ts +0 -4
  63. package/dist/types/identity/priority.d.ts +0 -1
  64. package/dist/types/identity/reference.d.ts +0 -29
  65. package/dist/types/index.d.ts +0 -17
  66. package/dist/types/model-cache.d.ts +0 -23
  67. package/dist/types/model-manager.d.ts +0 -77
  68. package/dist/types/model-thinking.d.ts +0 -66
  69. package/dist/types/models.d.ts +0 -12
  70. package/dist/types/provider-models/bundled-references.d.ts +0 -13
  71. package/dist/types/provider-models/cache-provider-id.d.ts +0 -9
  72. package/dist/types/provider-models/descriptor-types.d.ts +0 -76
  73. package/dist/types/provider-models/descriptors.d.ts +0 -504
  74. package/dist/types/provider-models/google.d.ts +0 -27
  75. package/dist/types/provider-models/index.d.ts +0 -7
  76. package/dist/types/provider-models/ollama.d.ts +0 -22
  77. package/dist/types/provider-models/openai-compat.d.ts +0 -599
  78. package/dist/types/provider-models/special.d.ts +0 -53
  79. package/dist/types/types.d.ts +0 -811
  80. package/dist/types/utils.d.ts +0 -20
  81. package/dist/types/variant-collapse.d.ts +0 -133
  82. package/dist/types/wire/alibaba-token-plan.d.ts +0 -22
  83. package/dist/types/wire/codex.d.ts +0 -42
  84. package/dist/types/wire/coreweave.d.ts +0 -12
  85. package/dist/types/wire/gemini-headers.d.ts +0 -37
  86. package/dist/types/wire/github-copilot.d.ts +0 -39
@@ -1,4 +1,4 @@
1
- import { VERSION } from "@linxiraos/pi-utils";
1
+ import { USER_AGENT } from "@linxiraos/pi-utils";
2
2
  import * as logger from "@linxiraos/pi-utils/logger";
3
3
  import {
4
4
  fetchOpenAICompatibleModels,
@@ -18,7 +18,7 @@ import {
18
18
  } from "../identity/family";
19
19
  import { resolveModelReference } from "../identity/reference";
20
20
  import type { ModelManagerOptions } from "../model-manager";
21
- import { getBundledModels } from "../models";
21
+ import { type GeneratedProvider, getBundledModels } from "../models";
22
22
  import type { Api, FetchImpl, Model, ModelSpec, OpenAICompat, Provider, ThinkingConfig } from "../types";
23
23
  import { discoveryFetch, isAnthropicOAuthToken, isRecord, toBoolean, toNumber, toPositiveNumber } from "../utils";
24
24
  import { ALIBABA_TOKEN_PLAN_BASE_URL, parseAlibabaTokenPlanCredential } from "../wire/alibaba-token-plan";
@@ -111,7 +111,7 @@ const catalogSession: {
111
111
  hasPayload: boolean;
112
112
  } = { inflight: null, payload: undefined, etag: null, hasPayload: false };
113
113
 
114
- const CATALOG_USER_AGENT = `zeta/${VERSION} (+https://linxira-os.github.io)`;
114
+ const CATALOG_USER_AGENT = USER_AGENT;
115
115
 
116
116
  /**
117
117
  * Fetches the models.dev catalog via catalog.stencil.so, which serves a
@@ -592,60 +592,68 @@ function resolveSimpleProviderHeaders(
592
592
  return typeof headers === "function" ? headers() : headers;
593
593
  }
594
594
 
595
- export function createSimpleOpenAICompletionsOptions(
596
- providerId: Parameters<typeof getBundledModels>[0],
597
- defaultBaseUrl: string,
598
- config?: SimpleProviderConfig,
599
- ): ModelManagerOptions<"openai-completions"> {
600
- const apiKey = config?.apiKey;
601
- const baseUrl = config?.baseUrl ?? defaultBaseUrl;
602
- const references = createBundledReferenceMap<"openai-completions">(providerId);
595
+ type OpenAICompatibleModelManagerBuilderOptions<TApi extends Api> = {
596
+ api: TApi;
597
+ providerId: GeneratedProvider;
598
+ defaultBaseUrl: string;
599
+ config?: SimpleProviderConfig;
600
+ headers?: SimpleProviderDiscoveryHeaders;
601
+ dynamicModelsAuthoritative?: true;
602
+ requireApiKey?: true;
603
+ filterModel?: (
604
+ entry: OpenAICompatibleModelRecord,
605
+ model: ModelSpec<TApi>,
606
+ references: Map<string, ModelSpec<TApi>>,
607
+ ) => boolean;
608
+ mapModel: (
609
+ entry: OpenAICompatibleModelRecord,
610
+ defaults: ModelSpec<TApi>,
611
+ reference: ModelSpec<TApi> | undefined,
612
+ ) => ModelSpec<TApi> | null;
613
+ };
614
+
615
+ function createOpenAICompatibleModelManagerOptions<TApi extends Api>(
616
+ options: OpenAICompatibleModelManagerBuilderOptions<TApi>,
617
+ ): ModelManagerOptions<TApi> {
618
+ const apiKey = options.config?.apiKey;
619
+ const baseUrl = options.config?.baseUrl ?? options.defaultBaseUrl;
620
+ const references = createBundledReferenceMap<TApi>(options.providerId);
621
+ const filterModel = options.filterModel;
603
622
  return {
604
- providerId,
605
- ...(apiKey && {
623
+ providerId: options.providerId,
624
+ ...(options.dynamicModelsAuthoritative && { dynamicModelsAuthoritative: true }),
625
+ ...((!options.requireApiKey || apiKey) && {
606
626
  fetchDynamicModels: () =>
607
627
  fetchOpenAICompatibleModels({
608
- api: "openai-completions",
609
- provider: providerId,
628
+ api: options.api,
629
+ provider: options.providerId,
610
630
  baseUrl,
611
631
  apiKey,
612
- headers: resolveSimpleProviderHeaders(config?.headers),
613
- mapModel: (entry, defaults) => {
614
- const reference = references.get(defaults.id);
615
- return mapWithBundledReference(entry, defaults, reference);
616
- },
617
- fetch: config?.fetch,
632
+ ...(options.headers && { headers: resolveSimpleProviderHeaders(options.headers) }),
633
+ ...(filterModel && {
634
+ filterModel: (entry, model) => filterModel(entry, model, references),
635
+ }),
636
+ mapModel: (entry, defaults) => options.mapModel(entry, defaults, references.get(defaults.id)),
637
+ fetch: options.config?.fetch,
618
638
  }),
619
639
  }),
620
640
  };
621
641
  }
622
642
 
623
- function createSimpleOpenAIResponsesOptions(
643
+ export function createSimpleOpenAICompletionsOptions(
624
644
  providerId: Parameters<typeof getBundledModels>[0],
625
645
  defaultBaseUrl: string,
626
646
  config?: SimpleProviderConfig,
627
- ): ModelManagerOptions<"openai-responses"> {
628
- const apiKey = config?.apiKey;
629
- const baseUrl = config?.baseUrl ?? defaultBaseUrl;
630
- const references = createBundledReferenceMap<"openai-responses">(providerId);
631
- return {
647
+ ): ModelManagerOptions<"openai-completions"> {
648
+ return createOpenAICompatibleModelManagerOptions({
649
+ api: "openai-completions",
632
650
  providerId,
633
- ...(apiKey && {
634
- fetchDynamicModels: () =>
635
- fetchOpenAICompatibleModels({
636
- api: "openai-responses",
637
- provider: providerId,
638
- baseUrl,
639
- apiKey,
640
- headers: resolveSimpleProviderHeaders(config?.headers),
641
- mapModel: (entry, defaults) => {
642
- const reference = references.get(defaults.id);
643
- return mapWithBundledReference(entry, defaults, reference);
644
- },
645
- fetch: config?.fetch,
646
- }),
647
- }),
648
- };
651
+ defaultBaseUrl,
652
+ config,
653
+ headers: config?.headers,
654
+ requireApiKey: true,
655
+ mapModel: mapWithBundledReference,
656
+ });
649
657
  }
650
658
 
651
659
  function createSimpleAnthropicProviderOptions(
@@ -858,6 +866,44 @@ export function umansModelManagerOptions(config?: UmansModelManagerConfig): Mode
858
866
  // 1. OpenAI
859
867
  // ---------------------------------------------------------------------------
860
868
 
869
+ const OPENAI_API_BASE_URL = "https://api.openai.com/v1";
870
+ export const OPENAI_GPT_56_LONG_CONTEXT_COSTS = {
871
+ luna: {
872
+ inputThreshold: 272_000,
873
+ input: 0.4,
874
+ output: 1.8,
875
+ cacheRead: 0.04,
876
+ cacheWrite: 0.5,
877
+ },
878
+ sol: {
879
+ inputThreshold: 272_000,
880
+ input: 10,
881
+ output: 45,
882
+ cacheRead: 1,
883
+ cacheWrite: 12.5,
884
+ },
885
+ terra: {
886
+ inputThreshold: 272_000,
887
+ input: 4,
888
+ output: 18,
889
+ cacheRead: 0.4,
890
+ cacheWrite: 5,
891
+ },
892
+ } as const;
893
+ const OPENAI_GPT_56_SOL_STANDARD_COST = {
894
+ input: 5,
895
+ output: 30,
896
+ cacheRead: 0.5,
897
+ cacheWrite: 6.25,
898
+ longContext: OPENAI_GPT_56_LONG_CONTEXT_COSTS.sol,
899
+ } as const;
900
+ const OPENAI_GPT_56_CYBER_STANDARD_COST = {
901
+ input: 12.5,
902
+ output: 75,
903
+ cacheRead: 1.25,
904
+ cacheWrite: 15.625,
905
+ } as const;
906
+
861
907
  export interface OpenAIModelManagerConfig {
862
908
  apiKey?: string;
863
909
  baseUrl?: string;
@@ -865,29 +911,61 @@ export interface OpenAIModelManagerConfig {
865
911
  }
866
912
 
867
913
  export function openaiModelManagerOptions(config?: OpenAIModelManagerConfig): ModelManagerOptions<"openai-responses"> {
868
- const apiKey = config?.apiKey;
869
- const baseUrl = config?.baseUrl ?? "https://api.openai.com/v1";
870
- const references = createBundledReferenceMap<"openai-responses">("openai");
871
- return {
914
+ return createOpenAICompatibleModelManagerOptions({
915
+ api: "openai-responses",
872
916
  providerId: "openai",
873
- ...(apiKey && {
874
- fetchDynamicModels: () =>
875
- fetchOpenAICompatibleModels({
876
- api: "openai-responses",
877
- provider: "openai",
878
- baseUrl,
879
- apiKey,
880
- filterModel: (_entry, model) => isLikelyOpenAIResponsesModelId(model.id, references),
881
- mapModel: (entry, defaults) => {
882
- const reference = references.get(defaults.id);
883
- return mapWithBundledReference(entry, defaults, reference);
884
- },
885
- fetch: config?.fetch,
886
- }),
887
- }),
888
- };
917
+ defaultBaseUrl: OPENAI_API_BASE_URL,
918
+ config,
919
+ requireApiKey: true,
920
+ filterModel: (_entry, model, references) => isLikelyOpenAIResponsesModelId(model.id, references),
921
+ mapModel: mapWithBundledReference,
922
+ });
889
923
  }
890
924
 
925
+ /**
926
+ * Daybreak models are approval-gated first-party Responses models that are not
927
+ * yet present in stencil.so. Seed the documented aliases and current Cyber
928
+ * snapshot so fresh installs expose them without credentialed discovery.
929
+ */
930
+ export const OPENAI_DAYBREAK_CURATED_FALLBACK_MODELS: readonly ModelSpec<"openai-responses">[] = [
931
+ {
932
+ id: "daybreak-blue-latest",
933
+ name: "Daybreak Blue",
934
+ api: "openai-responses",
935
+ provider: "openai",
936
+ baseUrl: OPENAI_API_BASE_URL,
937
+ reasoning: true,
938
+ input: ["text", "image"],
939
+ cost: OPENAI_GPT_56_SOL_STANDARD_COST,
940
+ contextWindow: 1_050_000,
941
+ maxTokens: 128_000,
942
+ },
943
+ {
944
+ id: "daybreak-red-latest",
945
+ name: "Daybreak Red",
946
+ api: "openai-responses",
947
+ provider: "openai",
948
+ baseUrl: OPENAI_API_BASE_URL,
949
+ reasoning: true,
950
+ input: ["text", "image"],
951
+ cost: OPENAI_GPT_56_CYBER_STANDARD_COST,
952
+ contextWindow: 400_000,
953
+ maxTokens: 128_000,
954
+ },
955
+ {
956
+ id: "gpt-5.6-cyber",
957
+ name: "GPT-5.6 Cyber",
958
+ api: "openai-responses",
959
+ provider: "openai",
960
+ baseUrl: OPENAI_API_BASE_URL,
961
+ reasoning: true,
962
+ input: ["text", "image"],
963
+ cost: OPENAI_GPT_56_CYBER_STANDARD_COST,
964
+ contextWindow: 400_000,
965
+ maxTokens: 128_000,
966
+ },
967
+ ];
968
+
891
969
  /** First-party gpt-5.6 SKUs that accept `reasoning: { mode: "pro" }` on the Responses APIs. */
892
970
  const OPENAI_PRO_REASONING_BASE_IDS: Record<string, true> = {
893
971
  "gpt-5.6-luna": true,
@@ -1036,27 +1114,15 @@ export interface CerebrasModelManagerConfig {
1036
1114
  export function cerebrasModelManagerOptions(
1037
1115
  config?: CerebrasModelManagerConfig,
1038
1116
  ): ModelManagerOptions<"openai-completions"> {
1039
- const apiKey = config?.apiKey;
1040
- const baseUrl = config?.baseUrl ?? "https://api.cerebras.ai/v1";
1041
- const references = createBundledReferenceMap<"openai-completions">("cerebras");
1042
- return {
1117
+ return createOpenAICompatibleModelManagerOptions({
1118
+ api: "openai-completions",
1043
1119
  providerId: "cerebras",
1044
- ...(apiKey && {
1045
- fetchDynamicModels: () =>
1046
- fetchOpenAICompatibleModels({
1047
- api: "openai-completions",
1048
- provider: "cerebras",
1049
- baseUrl,
1050
- apiKey,
1051
- mapModel: (entry, defaults) => {
1052
- const reference = references.get(defaults.id);
1053
- const model = mapWithBundledReference(entry, defaults, reference);
1054
- return applyCerebrasDiscoveryOverrides(model);
1055
- },
1056
- fetch: config?.fetch,
1057
- }),
1058
- }),
1059
- };
1120
+ defaultBaseUrl: "https://api.cerebras.ai/v1",
1121
+ config,
1122
+ requireApiKey: true,
1123
+ mapModel: (entry, defaults, reference) =>
1124
+ applyCerebrasDiscoveryOverrides(mapWithBundledReference(entry, defaults, reference)),
1125
+ });
1060
1126
  }
1061
1127
 
1062
1128
  // ---------------------------------------------------------------------------
@@ -1160,31 +1226,23 @@ function mapNovitaModel(
1160
1226
  export function novitaModelManagerOptions(
1161
1227
  config?: NovitaModelManagerConfig,
1162
1228
  ): ModelManagerOptions<"openai-completions"> {
1163
- const apiKey = config?.apiKey;
1164
- const baseUrl = config?.baseUrl ?? "https://api.novita.ai/openai/v1";
1165
- const references = createBundledReferenceMap<"openai-completions">("novita");
1166
- return {
1229
+ return createOpenAICompatibleModelManagerOptions({
1230
+ api: "openai-completions",
1167
1231
  providerId: "novita",
1232
+ defaultBaseUrl: "https://api.novita.ai/openai/v1",
1233
+ config,
1168
1234
  dynamicModelsAuthoritative: true,
1169
- fetchDynamicModels: async () =>
1170
- fetchOpenAICompatibleModels({
1171
- api: "openai-completions",
1172
- provider: "novita",
1173
- baseUrl,
1174
- apiKey,
1175
- mapModel: (entry, defaults) => mapNovitaModel(entry, defaults, references.get(defaults.id)),
1176
- filterModel: (entry, model) => {
1177
- const active = typeof entry.status !== "number" || entry.status === 1;
1178
- return (
1179
- active &&
1180
- isPublicNovitaModelId(model.id) &&
1181
- novitaArrayIncludes(entry.endpoints, "chat/completions") &&
1182
- toPositiveNumber(entry.max_output_tokens, 0) > 0
1183
- );
1184
- },
1185
- fetch: config?.fetch,
1186
- }),
1187
- };
1235
+ filterModel: (entry, model) => {
1236
+ const active = typeof entry.status !== "number" || entry.status === 1;
1237
+ return (
1238
+ active &&
1239
+ isPublicNovitaModelId(model.id) &&
1240
+ novitaArrayIncludes(entry.endpoints, "chat/completions") &&
1241
+ toPositiveNumber(entry.max_output_tokens, 0) > 0
1242
+ );
1243
+ },
1244
+ mapModel: mapNovitaModel,
1245
+ });
1188
1246
  }
1189
1247
 
1190
1248
  // ---------------------------------------------------------------------------
@@ -1453,11 +1511,14 @@ export function xaiOAuthModelManagerOptions(
1453
1511
  ): ModelManagerOptions<"openai-responses"> {
1454
1512
  const defaultBaseUrl = "https://api.x.ai/v1";
1455
1513
  const resolvedBaseUrl = config?.baseUrl ?? defaultBaseUrl;
1456
- const base = createSimpleOpenAIResponsesOptions(
1457
- "xai-oauth" as Parameters<typeof getBundledModels>[0],
1514
+ const base = createOpenAICompatibleModelManagerOptions({
1515
+ api: "openai-responses",
1516
+ providerId: "xai-oauth",
1458
1517
  defaultBaseUrl,
1459
1518
  config,
1460
- );
1519
+ requireApiKey: true,
1520
+ mapModel: mapWithBundledReference,
1521
+ });
1461
1522
  // Static seed handed to the runtime model manager so the picker populates on
1462
1523
  // a fresh login even before `fetchDynamicModels` fires (it is gated on
1463
1524
  // `config.apiKey` at construction time, and OAuth tokens resolve later via
@@ -1510,28 +1571,16 @@ export interface AimlApiModelManagerConfig {
1510
1571
  export function aimlApiModelManagerOptions(
1511
1572
  config?: AimlApiModelManagerConfig,
1512
1573
  ): ModelManagerOptions<"openai-completions"> {
1513
- const apiKey = config?.apiKey;
1514
- const baseUrl = config?.baseUrl ?? "https://api.aimlapi.com/v1";
1515
- const references = createBundledReferenceMap<"openai-completions">("aimlapi");
1516
- return {
1574
+ return createOpenAICompatibleModelManagerOptions({
1575
+ api: "openai-completions",
1517
1576
  providerId: "aimlapi",
1577
+ defaultBaseUrl: "https://api.aimlapi.com/v1",
1578
+ config,
1518
1579
  dynamicModelsAuthoritative: true,
1519
- ...(apiKey && {
1520
- fetchDynamicModels: () =>
1521
- fetchOpenAICompatibleModels({
1522
- api: "openai-completions",
1523
- provider: "aimlapi",
1524
- baseUrl,
1525
- apiKey,
1526
- filterModel: (_entry, model) => isLikelyAimlApiChatModelId(model.id),
1527
- mapModel: (entry, defaults) => {
1528
- const reference = references.get(defaults.id);
1529
- return mapWithBundledReference(entry, defaults, reference);
1530
- },
1531
- fetch: config?.fetch,
1532
- }),
1533
- }),
1534
- };
1580
+ requireApiKey: true,
1581
+ filterModel: (_entry, model) => isLikelyAimlApiChatModelId(model.id),
1582
+ mapModel: mapWithBundledReference,
1583
+ });
1535
1584
  }
1536
1585
 
1537
1586
  // ---------------------------------------------------------------------------
@@ -1845,6 +1894,7 @@ const FIREWORKS_FAST_VARIANT_SPECS: ReadonlyArray<{
1845
1894
  { base: "kimi-k2.7-code", name: "Kimi K2.7 Code Fast", cost: { input: 1.9, output: 8, cacheRead: 0.38 } },
1846
1895
  { base: "kimi-k2.6", name: "Kimi K2.6 Fast", cost: { input: 2, output: 8, cacheRead: 0.3 } },
1847
1896
  { base: "glm-5.1", name: "GLM-5.1 Fast", cost: { input: 2.8, output: 8.8, cacheRead: 0.52 } },
1897
+ { base: "glm-5.2", name: "GLM-5.2 Fast", cost: { input: 2.1, output: 6.6, cacheRead: 0.21 } },
1848
1898
  ];
1849
1899
 
1850
1900
  /**
@@ -2699,24 +2749,13 @@ export interface AlibabaCodingPlanModelManagerConfig {
2699
2749
  export function alibabaCodingPlanModelManagerOptions(
2700
2750
  config?: AlibabaCodingPlanModelManagerConfig,
2701
2751
  ): ModelManagerOptions<"openai-completions"> {
2702
- const apiKey = config?.apiKey;
2703
- const baseUrl = config?.baseUrl ?? "https://coding-intl.dashscope.aliyuncs.com/v1";
2704
- const references = createBundledReferenceMap<"openai-completions">("alibaba-coding-plan");
2705
- return {
2752
+ return createOpenAICompatibleModelManagerOptions({
2753
+ api: "openai-completions",
2706
2754
  providerId: "alibaba-coding-plan",
2707
- fetchDynamicModels: () =>
2708
- fetchOpenAICompatibleModels({
2709
- api: "openai-completions",
2710
- provider: "alibaba-coding-plan",
2711
- baseUrl,
2712
- apiKey,
2713
- mapModel: (entry, defaults) => {
2714
- const reference = references.get(defaults.id);
2715
- return mapWithBundledReference(entry, defaults, reference);
2716
- },
2717
- fetch: config?.fetch,
2718
- }),
2719
- };
2755
+ defaultBaseUrl: "https://coding-intl.dashscope.aliyuncs.com/v1",
2756
+ config,
2757
+ mapModel: mapWithBundledReference,
2758
+ });
2720
2759
  }
2721
2760
 
2722
2761
  // ---------------------------------------------------------------------------
@@ -2733,6 +2772,17 @@ const ALIBABA_TOKEN_PLAN_REASONING: ThinkingConfig = {
2733
2772
  mode: "effort",
2734
2773
  efforts: [Effort.Minimal, Effort.Low, Effort.Medium, Effort.High],
2735
2774
  };
2775
+ // Qwen3.8-Max combines Qwen's binary thinking toggle with OpenAI-style
2776
+ // `reasoning_effort`. The base Qwen view encodes disabled turns; reasoning
2777
+ // requests swap to the OpenAI effort dialect and explicitly enable thinking.
2778
+ const ALIBABA_TOKEN_PLAN_QWEN_EFFORT_COMPAT: OpenAICompat = {
2779
+ ...ALIBABA_TOKEN_PLAN_COMPAT,
2780
+ supportsReasoningEffort: true,
2781
+ whenThinking: {
2782
+ thinkingFormat: "openai",
2783
+ extraBody: { enable_thinking: true },
2784
+ },
2785
+ };
2736
2786
 
2737
2787
  export const ALIBABA_TOKEN_PLAN_STATIC_MODELS: readonly ModelSpec<"openai-completions">[] = [
2738
2788
  {
@@ -2756,6 +2806,24 @@ export const ALIBABA_TOKEN_PLAN_STATIC_MODELS: readonly ModelSpec<"openai-comple
2756
2806
  supportsReasoningEffort: true,
2757
2807
  },
2758
2808
  },
2809
+ {
2810
+ id: "qwen3.8-max",
2811
+ name: "Qwen3.8 Max",
2812
+ api: "openai-completions",
2813
+ provider: "alibaba-token-plan",
2814
+ baseUrl: ALIBABA_TOKEN_PLAN_BASE_URL,
2815
+ reasoning: true,
2816
+ input: ["text", "image"],
2817
+ cost: ALIBABA_TOKEN_PLAN_COST,
2818
+ contextWindow: 1_000_000,
2819
+ maxTokens: 131_072,
2820
+ thinking: {
2821
+ mode: "effort",
2822
+ efforts: [Effort.Low, Effort.Medium, Effort.XHigh],
2823
+ defaultLevel: Effort.XHigh,
2824
+ },
2825
+ compat: ALIBABA_TOKEN_PLAN_QWEN_EFFORT_COMPAT,
2826
+ },
2759
2827
  {
2760
2828
  id: "qwen3.7-max",
2761
2829
  name: "Qwen3.7 Max",
@@ -3519,29 +3587,20 @@ export interface VeniceModelManagerConfig {
3519
3587
  export function veniceModelManagerOptions(
3520
3588
  config?: VeniceModelManagerConfig,
3521
3589
  ): ModelManagerOptions<"openai-completions"> {
3522
- const apiKey = config?.apiKey;
3523
- const baseUrl = config?.baseUrl ?? "https://api.venice.ai/api/v1";
3524
- const references = createBundledReferenceMap<"openai-completions">("venice");
3525
- return {
3590
+ return createOpenAICompatibleModelManagerOptions({
3591
+ api: "openai-completions",
3526
3592
  providerId: "venice",
3527
- fetchDynamicModels: () =>
3528
- fetchOpenAICompatibleModels({
3529
- api: "openai-completions",
3530
- provider: "venice",
3531
- baseUrl,
3532
- apiKey,
3533
- mapModel: (entry, defaults) => {
3534
- const reference = references.get(defaults.id);
3535
- const model = mapWithBundledReference(entry, defaults, reference);
3536
- return {
3537
- ...model,
3538
- maxTokens: clampKimiK27CodeMaxTokens(defaults.id, model.maxTokens),
3539
- compat: { ...model.compat, supportsUsageInStreaming: false },
3540
- };
3541
- },
3542
- fetch: config?.fetch,
3543
- }),
3544
- };
3593
+ defaultBaseUrl: "https://api.venice.ai/api/v1",
3594
+ config,
3595
+ mapModel: (entry, defaults, reference) => {
3596
+ const model = mapWithBundledReference(entry, defaults, reference);
3597
+ return {
3598
+ ...model,
3599
+ maxTokens: clampKimiK27CodeMaxTokens(defaults.id, model.maxTokens),
3600
+ compat: { ...model.compat, supportsUsageInStreaming: false },
3601
+ };
3602
+ },
3603
+ });
3545
3604
  }
3546
3605
 
3547
3606
  // ---------------------------------------------------------------------------
@@ -3557,83 +3616,65 @@ export interface BasetenModelManagerConfig {
3557
3616
  export function basetenModelManagerOptions(
3558
3617
  config?: BasetenModelManagerConfig,
3559
3618
  ): ModelManagerOptions<"openai-completions"> {
3560
- const apiKey = config?.apiKey;
3561
- const baseUrl = config?.baseUrl ?? "https://inference.baseten.co/v1";
3562
- const references = createBundledReferenceMap<"openai-completions">("baseten");
3563
- return {
3619
+ return createOpenAICompatibleModelManagerOptions({
3620
+ api: "openai-completions",
3564
3621
  providerId: "baseten",
3622
+ defaultBaseUrl: "https://inference.baseten.co/v1",
3623
+ config,
3565
3624
  dynamicModelsAuthoritative: true,
3566
- ...(apiKey && {
3567
- fetchDynamicModels: () =>
3568
- fetchOpenAICompatibleModels({
3569
- api: "openai-completions",
3570
- provider: "baseten",
3571
- baseUrl,
3572
- apiKey,
3573
- mapModel: (entry, defaults) => {
3574
- const reference = references.get(defaults.id);
3575
- const raw = entry as Record<string, unknown> & {
3576
- supported_features?: unknown;
3577
- input_modalities?: unknown;
3578
- pricing?: Record<string, unknown>;
3579
- };
3580
- const features = Array.isArray(raw.supported_features) ? raw.supported_features : [];
3581
- const modalities = Array.isArray(raw.input_modalities) ? raw.input_modalities : [];
3582
-
3583
- const isBasetenNativeReasoning =
3584
- defaults.id === "openai/gpt-oss-120b" ||
3585
- defaults.id === "deepseek-ai/DeepSeek-V4-Pro" ||
3586
- defaults.id === "zai-org/GLM-5.2";
3587
- const reasoning =
3588
- isBasetenNativeReasoning &&
3589
- (features.includes("reasoning") || features.includes("reasoning_effort"));
3590
- const supportsTools = features.includes("tools") ? undefined : false;
3591
- const vision = modalities.includes("image") || (reference?.input.includes("image") ?? false);
3592
-
3593
- const pricing = raw.pricing ?? {};
3594
- const cost = {
3595
- input: toPositiveNumber(pricing.prompt, 0) * 1_000_000,
3596
- output: toPositiveNumber(pricing.completion, 0) * 1_000_000,
3597
- cacheRead: toPositiveNumber(pricing.input_cache_read, 0) * 1_000_000,
3598
- cacheWrite: 0,
3599
- };
3600
-
3601
- const contextWindow = toPositiveNumber(
3602
- raw.context_length,
3603
- reference?.contextWindow ?? defaults.contextWindow,
3604
- );
3605
- const maxTokens = toPositiveNumber(
3606
- raw.max_completion_tokens,
3607
- reference?.maxTokens ?? defaults.maxTokens,
3608
- );
3625
+ requireApiKey: true,
3626
+ mapModel: (entry, defaults, reference) => {
3627
+ const raw = entry as Record<string, unknown> & {
3628
+ supported_features?: unknown;
3629
+ input_modalities?: unknown;
3630
+ pricing?: Record<string, unknown>;
3631
+ };
3632
+ const features = Array.isArray(raw.supported_features) ? raw.supported_features : [];
3633
+ const modalities = Array.isArray(raw.input_modalities) ? raw.input_modalities : [];
3634
+
3635
+ // Baseten's reasoning router accepts only the high/max
3636
+ // effort tiers for its GLM-5.2 and gpt-oss routes.
3637
+ const isEffortReasoning =
3638
+ defaults.id === "openai/gpt-oss-120b" ||
3639
+ defaults.id === "zai-org/GLM-5.2" ||
3640
+ defaults.id === "zai-org/GLM-5.2-Fast";
3641
+ const isBasetenNativeReasoning = isEffortReasoning || defaults.id === "deepseek-ai/DeepSeek-V4-Pro";
3642
+ const reasoning =
3643
+ isBasetenNativeReasoning && (features.includes("reasoning") || features.includes("reasoning_effort"));
3644
+ const supportsTools = features.includes("tools") ? undefined : false;
3645
+ const vision = modalities.includes("image") || (reference?.input.includes("image") ?? false);
3646
+
3647
+ const pricing = raw.pricing ?? {};
3648
+ const cost = {
3649
+ input: toPositiveNumber(pricing.prompt, 0) * 1_000_000,
3650
+ output: toPositiveNumber(pricing.completion, 0) * 1_000_000,
3651
+ cacheRead: toPositiveNumber(pricing.input_cache_read, 0) * 1_000_000,
3652
+ cacheWrite: 0,
3653
+ };
3609
3654
 
3610
- const baseModel = mapWithBundledReference(entry, defaults, reference);
3655
+ const contextWindow = toPositiveNumber(raw.context_length, reference?.contextWindow ?? defaults.contextWindow);
3656
+ const maxTokens = toPositiveNumber(raw.max_completion_tokens, reference?.maxTokens ?? defaults.maxTokens);
3611
3657
 
3612
- // Baseten's reasoning router accepts only the high/max
3613
- // effort tiers for its GLM-5.2 and gpt-oss routes.
3614
- const isEffortReasoning = defaults.id === "openai/gpt-oss-120b" || defaults.id === "zai-org/GLM-5.2";
3615
- const thinking = isEffortReasoning
3616
- ? {
3617
- mode: "effort" as const,
3618
- efforts: [Effort.High, Effort.Max],
3619
- }
3620
- : undefined;
3658
+ const baseModel = mapWithBundledReference(entry, defaults, reference);
3659
+ const thinking = isEffortReasoning
3660
+ ? {
3661
+ mode: "effort" as const,
3662
+ efforts: [Effort.High, Effort.Max],
3663
+ }
3664
+ : undefined;
3621
3665
 
3622
- return {
3623
- ...baseModel,
3624
- reasoning,
3625
- input: vision ? ["text", "image"] : ["text"],
3626
- cost,
3627
- contextWindow,
3628
- maxTokens,
3629
- ...(thinking ? { thinking } : {}),
3630
- ...(supportsTools === false ? { supportsTools } : {}),
3631
- };
3632
- },
3633
- fetch: config?.fetch,
3634
- }),
3635
- }),
3636
- };
3666
+ return {
3667
+ ...baseModel,
3668
+ reasoning,
3669
+ input: vision ? ["text", "image"] : ["text"],
3670
+ cost,
3671
+ contextWindow,
3672
+ maxTokens,
3673
+ ...(thinking ? { thinking } : {}),
3674
+ ...(supportsTools === false ? { supportsTools } : {}),
3675
+ };
3676
+ },
3677
+ });
3637
3678
  }
3638
3679
 
3639
3680
  // ---------------------------------------------------------------------------
@@ -3700,6 +3741,40 @@ export const META_MUSE_STATIC_MODELS: readonly ModelSpec<"openai-responses">[] =
3700
3741
  includeEncryptedReasoning: true,
3701
3742
  },
3702
3743
  },
3744
+ {
3745
+ id: "muse-spark-1.2",
3746
+ name: "Muse Spark 1.2",
3747
+ api: "openai-responses",
3748
+ provider: "meta",
3749
+ baseUrl: META_MODEL_API_BASE_URL,
3750
+ reasoning: true,
3751
+ input: ["text", "image"],
3752
+ cost: META_MUSE_SPARK_COST,
3753
+ contextWindow: 1_048_576,
3754
+ maxTokens: 131_072,
3755
+ thinking: META_MUSE_SPARK_THINKING,
3756
+ compat: {
3757
+ supportsReasoningEffort: true,
3758
+ includeEncryptedReasoning: true,
3759
+ },
3760
+ },
3761
+ {
3762
+ id: "muse-spark-1.2-contributor",
3763
+ name: "Muse Spark 1.2 Contributor (Data Used for Training)",
3764
+ api: "openai-responses",
3765
+ provider: "meta",
3766
+ baseUrl: META_MODEL_API_BASE_URL,
3767
+ reasoning: true,
3768
+ input: ["text", "image"],
3769
+ cost: { input: 0.1, output: 0.2, cacheRead: 0.002, cacheWrite: 0 },
3770
+ contextWindow: 1_048_576,
3771
+ maxTokens: 131_072,
3772
+ thinking: META_MUSE_SPARK_THINKING,
3773
+ compat: {
3774
+ supportsReasoningEffort: true,
3775
+ includeEncryptedReasoning: true,
3776
+ },
3777
+ },
3703
3778
  ];
3704
3779
 
3705
3780
  // ---------------------------------------------------------------------------
@@ -3830,7 +3905,14 @@ export interface MetaModelManagerConfig {
3830
3905
 
3831
3906
  export function metaModelManagerOptions(config?: MetaModelManagerConfig): ModelManagerOptions<"openai-responses"> {
3832
3907
  return {
3833
- ...createSimpleOpenAIResponsesOptions("meta", META_MODEL_API_BASE_URL, config),
3908
+ ...createOpenAICompatibleModelManagerOptions({
3909
+ api: "openai-responses",
3910
+ providerId: "meta",
3911
+ defaultBaseUrl: META_MODEL_API_BASE_URL,
3912
+ config,
3913
+ requireApiKey: true,
3914
+ mapModel: mapWithBundledReference,
3915
+ }),
3834
3916
  staticModels: META_MUSE_STATIC_MODELS,
3835
3917
  };
3836
3918
  }
@@ -3865,67 +3947,56 @@ const MOONSHOT_KIMI_K3_THINKING: ThinkingConfig = { mode: "effort", efforts: [Ef
3865
3947
  export function moonshotModelManagerOptions(
3866
3948
  config?: MoonshotModelManagerConfig,
3867
3949
  ): ModelManagerOptions<"openai-completions"> {
3868
- const apiKey = config?.apiKey;
3869
- // `MOONSHOT_BASE_URL` redirects discovery (and the streaming request that
3870
- // inherits this baseUrl) at the Kimi China platform `api.moonshot.cn`; an
3871
- // explicit `config.baseUrl` still wins. Mirrors LITELLM_BASE_URL/LM_STUDIO_BASE_URL. (#2883)
3872
- const baseUrl = config?.baseUrl ?? Bun.env.MOONSHOT_BASE_URL ?? "https://api.moonshot.ai/v1";
3873
- const references = createBundledReferenceMap<"openai-completions">("moonshot");
3874
- return {
3950
+ return createOpenAICompatibleModelManagerOptions({
3951
+ api: "openai-completions",
3875
3952
  providerId: "moonshot",
3876
- ...(apiKey && {
3877
- fetchDynamicModels: () =>
3878
- fetchOpenAICompatibleModels({
3879
- api: "openai-completions",
3880
- provider: "moonshot",
3881
- baseUrl,
3882
- apiKey,
3883
- mapModel: (entry, defaults) => {
3884
- const reference = references.get(defaults.id);
3885
- const model = mapWithBundledReference(entry, defaults, reference);
3886
- const id = model.id.toLowerCase();
3887
- // Kimi K3 is discovered but has no bundled/models.dev reference, so the
3888
- // generic dynamic defaults would report it "Free" with no capabilities
3889
- // (#5756). Stamp the official pricing/limits when the endpoint doesn't
3890
- // carry them, and mark it reasoning + vision. K3 always reasons via
3891
- // `reasoning_effort: "max"` and does NOT use the K2.x `thinking` block,
3892
- // so its thinking config is the single-tier `max` scale — the wire path
3893
- // routes it through `reasoning_effort` (see `buildOpenAICompat`).
3894
- if (!reference && isKimiK3ModelId(id)) {
3895
- const isZeroCost = model.cost.input === 0 && model.cost.output === 0 && model.cost.cacheRead === 0;
3896
- return {
3897
- ...model,
3898
- reasoning: true,
3899
- input: ["text", "image"],
3900
- cost: isZeroCost ? { ...MOONSHOT_KIMI_K3_COST } : model.cost,
3901
- contextWindow: model.contextWindow ?? MOONSHOT_KIMI_K3_CONTEXT_WINDOW,
3902
- maxTokens: model.maxTokens ?? MOONSHOT_KIMI_K3_MAX_TOKENS,
3903
- thinking: model.thinking ?? { ...MOONSHOT_KIMI_K3_THINKING },
3904
- };
3905
- }
3906
- // Moonshot's K2.x family (K2.5, K2.6, kimi-k2-thinking, …) is reasoning-capable
3907
- // and vision-capable on the native API. Without these flags the openai-completions
3908
- // path skips the z.ai-format `thinking` block, and Moonshot K2.6 stalls on first
3909
- // turn because its endpoint expects an explicit `thinking: {type}` (#2113). Match
3910
- // the bundled K2.5 metadata for every K2.x id we discover.
3911
- const isKimiK2Reasoning = id.includes("thinking") || /(^|\/)kimi-k2(?:\.\d+)?(?:[-:]|$)/.test(id);
3912
- const isVision =
3913
- id.includes("vision") || id.includes("vl") || /(^|\/)kimi-k2(?:\.\d+)?(?:[-:]|$)/.test(id);
3914
- return {
3915
- ...model,
3916
- reasoning: isKimiK2Reasoning || model.reasoning,
3917
- input: isVision ? ["text", "image"] : model.input,
3918
- thinking:
3919
- model.thinking ??
3920
- (isKimiK2Reasoning
3921
- ? { mode: "effort", efforts: [Effort.Minimal, Effort.Low, Effort.Medium, Effort.High] }
3922
- : undefined),
3923
- };
3924
- },
3925
- fetch: config?.fetch,
3926
- }),
3927
- }),
3928
- };
3953
+ // `MOONSHOT_BASE_URL` redirects discovery (and the streaming request that
3954
+ // inherits this baseUrl) at the Kimi China platform `api.moonshot.cn`; an
3955
+ // explicit `config.baseUrl` still wins. Mirrors LITELLM_BASE_URL/LM_STUDIO_BASE_URL. (#2883)
3956
+ defaultBaseUrl: Bun.env.MOONSHOT_BASE_URL ?? "https://api.moonshot.ai/v1",
3957
+ config,
3958
+ requireApiKey: true,
3959
+ mapModel: (entry, defaults, reference) => {
3960
+ const model = mapWithBundledReference(entry, defaults, reference);
3961
+ const id = model.id.toLowerCase();
3962
+ // Kimi K3 is discovered but has no bundled/models.dev reference, so the
3963
+ // generic dynamic defaults would report it "Free" with no capabilities
3964
+ // (#5756). Stamp the official pricing/limits when the endpoint doesn't
3965
+ // carry them, and mark it reasoning + vision. K3 always reasons via
3966
+ // `reasoning_effort: "max"` and does NOT use the K2.x `thinking` block,
3967
+ // so its thinking config is the single-tier `max` scale — the wire path
3968
+ // routes it through `reasoning_effort` (see `buildOpenAICompat`).
3969
+ if (!reference && isKimiK3ModelId(id)) {
3970
+ const isZeroCost = model.cost.input === 0 && model.cost.output === 0 && model.cost.cacheRead === 0;
3971
+ return {
3972
+ ...model,
3973
+ reasoning: true,
3974
+ input: ["text", "image"],
3975
+ cost: isZeroCost ? { ...MOONSHOT_KIMI_K3_COST } : model.cost,
3976
+ contextWindow: model.contextWindow ?? MOONSHOT_KIMI_K3_CONTEXT_WINDOW,
3977
+ maxTokens: model.maxTokens ?? MOONSHOT_KIMI_K3_MAX_TOKENS,
3978
+ thinking: model.thinking ?? { ...MOONSHOT_KIMI_K3_THINKING },
3979
+ };
3980
+ }
3981
+ // Moonshot's K2.x family (K2.5, K2.6, kimi-k2-thinking, …) is reasoning-capable
3982
+ // and vision-capable on the native API. Without these flags the openai-completions
3983
+ // path skips the z.ai-format `thinking` block, and Moonshot K2.6 stalls on first
3984
+ // turn because its endpoint expects an explicit `thinking: {type}` (#2113). Match
3985
+ // the bundled K2.5 metadata for every K2.x id we discover.
3986
+ const isKimiK2Reasoning = id.includes("thinking") || /(^|\/)kimi-k2(?:\.\d+)?(?:[-:]|$)/.test(id);
3987
+ const isVision = id.includes("vision") || id.includes("vl") || /(^|\/)kimi-k2(?:\.\d+)?(?:[-:]|$)/.test(id);
3988
+ return {
3989
+ ...model,
3990
+ reasoning: isKimiK2Reasoning || model.reasoning,
3991
+ input: isVision ? ["text", "image"] : model.input,
3992
+ thinking:
3993
+ model.thinking ??
3994
+ (isKimiK2Reasoning
3995
+ ? { mode: "effort", efforts: [Effort.Minimal, Effort.Low, Effort.Medium, Effort.High] }
3996
+ : undefined),
3997
+ };
3998
+ },
3999
+ });
3929
4000
  }
3930
4001
 
3931
4002
  // ---------------------------------------------------------------------------
@@ -5575,7 +5646,14 @@ const OPENCODE_ZEN_API_RESOLUTION = createOpenCodeApiResolution("https://opencod
5575
5646
  // anthropic-style requests to /v1/messages and the gateway would return its
5576
5647
  // `Page Not Found` HTML (issue #887 for the qwen/m2.7 entries; minimax-m3
5577
5648
  // and minimax-m3-free added under #1617 for the same root cause).
5649
+ //
5650
+ // deepseek-v4-flash is the inverse case: it falls through to
5651
+ // openai-completions by default, but the Go gateway's
5652
+ // /zen/go/v1/chat/completions route does not work for this model while
5653
+ // /zen/go/v1/responses does (user-verified against the live gateway,
5654
+ // 2026-08-08; Flash only — deepseek-v4-pro serves fine on chat completions).
5578
5655
  const OPENCODE_GO_API_RESOLUTION = createOpenCodeApiResolution("https://opencode.ai/zen/go", {
5656
+ "deepseek-v4-flash": "openai-responses",
5579
5657
  "minimax-m2.7": "openai-completions",
5580
5658
  "minimax-m3": "openai-completions",
5581
5659
  "minimax-m3-free": "openai-completions",