@gajae-code/ai 0.6.3 → 0.6.5

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/src/models.ts CHANGED
@@ -33,14 +33,21 @@ function getProviderModels(provider: GeneratedProvider): Map<string, Model<Api>>
33
33
  * Mythos rejecting forced tool use) without a full regeneration.
34
34
  */
35
35
  function applyBundledCompatDefaults(model: Model<Api>): Model<Api> {
36
+ let normalized = model;
37
+ if (normalized.id === "minimax-m3" && normalized.name === "MiniMax M3") {
38
+ normalized = { ...normalized, name: "MiniMax-M3" };
39
+ }
36
40
  if (
37
- (model.api === "anthropic-messages" || model.api === "bedrock-converse-stream") &&
38
- isClaudeForcedToolChoiceIncapableModelId(model.id) &&
39
- (model.compat as { toolChoiceSupport?: string } | undefined)?.toolChoiceSupport === undefined
41
+ (normalized.api === "anthropic-messages" || normalized.api === "bedrock-converse-stream") &&
42
+ isClaudeForcedToolChoiceIncapableModelId(normalized.id) &&
43
+ (normalized.compat as { toolChoiceSupport?: string } | undefined)?.toolChoiceSupport === undefined
40
44
  ) {
41
- return { ...model, compat: { ...(model.compat ?? {}), toolChoiceSupport: "auto" } as Model<Api>["compat"] };
45
+ return {
46
+ ...normalized,
47
+ compat: { ...(normalized.compat ?? {}), toolChoiceSupport: "auto" } as Model<Api>["compat"],
48
+ };
42
49
  }
43
- return model;
50
+ return normalized;
44
51
  }
45
52
 
46
53
  export type GeneratedProvider = keyof typeof MODELS;
@@ -812,6 +812,8 @@ function openCodeModelManagerOptions(
812
812
  ): ModelManagerOptions<"openai-completions"> {
813
813
  const apiKey = config?.apiKey;
814
814
  const baseUrl = config?.baseUrl ?? defaultBaseUrl;
815
+ const references =
816
+ providerId === "opencode-go" ? createBundledReferenceMap<"openai-completions">(providerId) : undefined;
815
817
  return {
816
818
  providerId,
817
819
  ...(apiKey && {
@@ -821,6 +823,13 @@ function openCodeModelManagerOptions(
821
823
  provider: providerId,
822
824
  baseUrl,
823
825
  apiKey,
826
+ ...(providerId === "opencode-go" && {
827
+ mapModel: (entry, defaults) => {
828
+ const reference = references?.get(defaults.id);
829
+ const model = mapWithBundledReference(entry, defaults, reference);
830
+ return applyOpenCodeGoOfficialMetadata(model);
831
+ },
832
+ }),
824
833
  }),
825
834
  }),
826
835
  };
@@ -1921,6 +1930,8 @@ export interface ModelsDevProviderDescriptor {
1921
1930
  * Can return null to skip the model, or an array to emit multiple models.
1922
1931
  */
1923
1932
  transformModel?: (model: Model<Api>, modelId: string, raw: ModelsDevModel) => Model<Api> | Model<Api>[] | null;
1933
+ /** Optional static rows appended after mapped models. Used only for official provider catalogs missing from models.dev. */
1934
+ appendModels?: readonly Model<Api>[];
1924
1935
  /**
1925
1936
  * Optional: override the API type per-model.
1926
1937
  * Called with (modelId, raw). Return the API type to use.
@@ -1937,56 +1948,59 @@ export function mapModelsDevToModels(
1937
1948
  const models: Model<Api>[] = [];
1938
1949
  for (const desc of descriptors) {
1939
1950
  const providerData = (data as Record<string, Record<string, unknown>>)[desc.modelsDevKey];
1940
- if (!isRecord(providerData) || !isRecord(providerData.models)) continue;
1941
-
1942
- for (const [modelId, rawModel] of Object.entries(providerData.models)) {
1943
- if (!isRecord(rawModel)) continue;
1944
- const m = rawModel as ModelsDevModel;
1945
-
1946
- // Default filter: tool_call must be true
1947
- if (desc.filterModel) {
1948
- if (!desc.filterModel(modelId, m)) continue;
1949
- } else {
1950
- if (m.tool_call !== true) continue;
1951
- }
1951
+ if (isRecord(providerData) && isRecord(providerData.models)) {
1952
+ for (const [modelId, rawModel] of Object.entries(providerData.models)) {
1953
+ if (!isRecord(rawModel)) continue;
1954
+ const m = rawModel as ModelsDevModel;
1955
+
1956
+ // Default filter: tool_call must be true
1957
+ if (desc.filterModel) {
1958
+ if (!desc.filterModel(modelId, m)) continue;
1959
+ } else {
1960
+ if (m.tool_call !== true) continue;
1961
+ }
1952
1962
 
1953
- // Resolve API and baseUrl (may be per-model for providers like OpenCode)
1954
- const resolved = desc.resolveApi?.(modelId, m) ?? { api: desc.api, baseUrl: desc.baseUrl };
1955
- if (!resolved) continue;
1956
-
1957
- const mapped: Model<Api> = {
1958
- id: modelId,
1959
- name: toModelName(m.name, modelId),
1960
- api: resolved.api,
1961
- provider: desc.providerId as Model<Api>["provider"],
1962
- baseUrl: resolved.baseUrl,
1963
- reasoning: m.reasoning === true,
1964
- input: toInputCapabilities(m.modalities?.input),
1965
- cost: {
1966
- input: toNumber(m.cost?.input) ?? 0,
1967
- output: toNumber(m.cost?.output) ?? 0,
1968
- cacheRead: toNumber(m.cost?.cache_read) ?? 0,
1969
- cacheWrite: toNumber(m.cost?.cache_write) ?? 0,
1970
- },
1971
- contextWindow: toPositiveNumber(m.limit?.context, desc.defaultContextWindow ?? UNK_CONTEXT_WINDOW),
1972
- maxTokens: toPositiveNumber(m.limit?.output, desc.defaultMaxTokens ?? UNK_MAX_TOKENS),
1973
- ...(desc.compat && { compat: desc.compat }),
1974
- ...(desc.headers && { headers: { ...desc.headers } }),
1975
- };
1963
+ // Resolve API and baseUrl (may be per-model for providers like OpenCode)
1964
+ const resolved = desc.resolveApi?.(modelId, m) ?? { api: desc.api, baseUrl: desc.baseUrl };
1965
+ if (!resolved) continue;
1966
+
1967
+ const mapped: Model<Api> = {
1968
+ id: modelId,
1969
+ name: toModelName(m.name, modelId),
1970
+ api: resolved.api,
1971
+ provider: desc.providerId as Model<Api>["provider"],
1972
+ baseUrl: resolved.baseUrl,
1973
+ reasoning: m.reasoning === true,
1974
+ input: toInputCapabilities(m.modalities?.input),
1975
+ cost: {
1976
+ input: toNumber(m.cost?.input) ?? 0,
1977
+ output: toNumber(m.cost?.output) ?? 0,
1978
+ cacheRead: toNumber(m.cost?.cache_read) ?? 0,
1979
+ cacheWrite: toNumber(m.cost?.cache_write) ?? 0,
1980
+ },
1981
+ contextWindow: toPositiveNumber(m.limit?.context, desc.defaultContextWindow ?? UNK_CONTEXT_WINDOW),
1982
+ maxTokens: toPositiveNumber(m.limit?.output, desc.defaultMaxTokens ?? UNK_MAX_TOKENS),
1983
+ ...(desc.compat && { compat: desc.compat }),
1984
+ ...(desc.headers && { headers: { ...desc.headers } }),
1985
+ };
1976
1986
 
1977
- // Apply per-model transform
1978
- if (desc.transformModel) {
1979
- const result = desc.transformModel(mapped, modelId, m);
1980
- if (result === null) continue;
1981
- if (Array.isArray(result)) {
1982
- models.push(...result);
1987
+ // Apply per-model transform
1988
+ if (desc.transformModel) {
1989
+ const result = desc.transformModel(mapped, modelId, m);
1990
+ if (result === null) continue;
1991
+ if (Array.isArray(result)) {
1992
+ models.push(...result);
1993
+ } else {
1994
+ models.push(result);
1995
+ }
1983
1996
  } else {
1984
- models.push(result);
1997
+ models.push(mapped);
1985
1998
  }
1986
- } else {
1987
- models.push(mapped);
1988
1999
  }
1989
2000
  }
2001
+ if (desc.appendModels) {
2002
+ models.push(...desc.appendModels);
2003
+ }
1990
2004
  }
1991
2005
  return models;
1992
2006
  }
@@ -2076,22 +2090,35 @@ function createOpenCodeApiResolution(
2076
2090
  };
2077
2091
  }
2078
2092
 
2093
+ const OPENCODE_GO_BASE_PATH = "https://opencode.ai/zen/go";
2079
2094
  const OPENCODE_ZEN_API_RESOLUTION = createOpenCodeApiResolution("https://opencode.ai/zen");
2080
- // OpenCode Go: models.dev declares minimax-m2.7 / qwen3.5-plus / qwen3.6-plus
2081
- // with `provider.npm = "@ai-sdk/anthropic"`, but the OpenCode Go gateway only
2082
- // serves them at `https://opencode.ai/zen/go/v1/chat/completions` (verified
2083
- // against https://opencode.ai/zen/go/v1/models and the upstream endpoint
2084
- // table at https://opencode.ai/docs/go/#endpoints — minimax-m2.5 works the
2085
- // same way and lacks an `npm` field on models.dev so it already falls through
2086
- // to the openai-completions default). Without this override the resolver
2087
- // would POST anthropic-style requests to /v1/messages and the gateway would
2088
- // return its `Page Not Found` HTML (issue #887). Override the resolver so
2089
- // regenerating models.json keeps the correct routing.
2090
- const OPENCODE_GO_API_RESOLUTION = createOpenCodeApiResolution("https://opencode.ai/zen/go", {
2091
- "minimax-m2.7": "openai-completions",
2092
- "qwen3.5-plus": "openai-completions",
2093
- "qwen3.6-plus": "openai-completions",
2094
- });
2095
+ const OPENCODE_GO_CHAT_COMPLETIONS_MODEL_IDS = [
2096
+ "deepseek-v4-flash",
2097
+ "deepseek-v4-pro",
2098
+ "glm-5.1",
2099
+ "glm-5.2",
2100
+ "kimi-k2.6",
2101
+ "kimi-k2.7-code",
2102
+ "mimo-v2.5",
2103
+ "mimo-v2.5-pro",
2104
+ ] as const;
2105
+ const OPENCODE_GO_MESSAGES_MODEL_IDS = [
2106
+ "minimax-m2.5",
2107
+ "minimax-m2.7",
2108
+ "minimax-m3",
2109
+ "qwen3.6-plus",
2110
+ "qwen3.7-max",
2111
+ "qwen3.7-plus",
2112
+ ] as const;
2113
+ const OPENCODE_GO_API_OVERRIDES: Readonly<Record<string, Api>> = {
2114
+ ...Object.fromEntries(OPENCODE_GO_CHAT_COMPLETIONS_MODEL_IDS.map(id => [id, "openai-completions"])),
2115
+ ...Object.fromEntries(OPENCODE_GO_MESSAGES_MODEL_IDS.map(id => [id, "anthropic-messages"])),
2116
+ } as Record<string, Api>;
2117
+ // OpenCode Go has a provider-specific endpoint table at
2118
+ // https://opencode.ai/docs/go/#endpoints. Keep routing aligned with that table:
2119
+ // GLM/Kimi/DeepSeek/MiMo rows use /v1/chat/completions, while MiniMax and
2120
+ // current Qwen Plus/Max rows use /v1/messages via the Anthropic client.
2121
+ const OPENCODE_GO_API_RESOLUTION = createOpenCodeApiResolution(OPENCODE_GO_BASE_PATH, OPENCODE_GO_API_OVERRIDES);
2095
2122
 
2096
2123
  const COPILOT_BASE_URL = "https://api.githubcopilot.com";
2097
2124
 
@@ -2320,6 +2347,215 @@ const filterActiveToolCallModels = (_id: string, m: ModelsDevModel): boolean =>
2320
2347
  return true;
2321
2348
  };
2322
2349
 
2350
+ interface OpenCodeGoOfficialModelMetadata {
2351
+ name: string;
2352
+ contextWindow: number;
2353
+ maxTokens: number;
2354
+ input: ("text" | "image")[];
2355
+ reasoning: boolean;
2356
+ cost: Model["cost"];
2357
+ }
2358
+
2359
+ const OPENCODE_GO_OFFICIAL_MODELS: Readonly<Record<string, OpenCodeGoOfficialModelMetadata>> = {
2360
+ "deepseek-v4-flash": {
2361
+ name: "DeepSeek V4 Flash",
2362
+ contextWindow: 1_000_000,
2363
+ maxTokens: 384_000,
2364
+ input: ["text"],
2365
+ reasoning: true,
2366
+ cost: { input: 0.14, output: 0.28, cacheRead: 0.0028, cacheWrite: 0 },
2367
+ },
2368
+ "deepseek-v4-pro": {
2369
+ name: "DeepSeek V4 Pro",
2370
+ contextWindow: 1_000_000,
2371
+ maxTokens: 384_000,
2372
+ input: ["text"],
2373
+ reasoning: true,
2374
+ cost: { input: 1.74, output: 3.48, cacheRead: 0.0145, cacheWrite: 0 },
2375
+ },
2376
+ "glm-5": {
2377
+ name: "GLM-5",
2378
+ contextWindow: 204_800,
2379
+ maxTokens: 131_072,
2380
+ input: ["text"],
2381
+ reasoning: true,
2382
+ cost: { input: 1, output: 3.2, cacheRead: 0.2, cacheWrite: 0 },
2383
+ },
2384
+ "glm-5.1": {
2385
+ name: "GLM-5.1",
2386
+ contextWindow: 200_000,
2387
+ maxTokens: 131_072,
2388
+ input: ["text"],
2389
+ reasoning: true,
2390
+ cost: { input: 1.4, output: 4.4, cacheRead: 0.26, cacheWrite: 0 },
2391
+ },
2392
+ "glm-5.2": {
2393
+ name: "GLM-5.2",
2394
+ contextWindow: 1_000_000,
2395
+ maxTokens: 131_072,
2396
+ input: ["text"],
2397
+ reasoning: true,
2398
+ cost: { input: 1.4, output: 4.4, cacheRead: 0.26, cacheWrite: 0 },
2399
+ },
2400
+ "kimi-k2.5": {
2401
+ name: "Kimi K2.5",
2402
+ contextWindow: 262_144,
2403
+ maxTokens: 262_144,
2404
+ input: ["text", "image"],
2405
+ reasoning: true,
2406
+ cost: { input: 0.3, output: 1.9, cacheRead: 0, cacheWrite: 0 },
2407
+ },
2408
+ "kimi-k2.6": {
2409
+ name: "Kimi K2.6",
2410
+ contextWindow: 262_144,
2411
+ maxTokens: 262_144,
2412
+ input: ["text", "image"],
2413
+ reasoning: true,
2414
+ cost: { input: 0.95, output: 4, cacheRead: 0.2, cacheWrite: 0 },
2415
+ },
2416
+ "kimi-k2.7-code": {
2417
+ name: "Kimi K2.7 Code",
2418
+ contextWindow: 262_144,
2419
+ maxTokens: 262_144,
2420
+ input: ["text", "image"],
2421
+ reasoning: true,
2422
+ cost: { input: 0.95, output: 4, cacheRead: 0.19, cacheWrite: 0 },
2423
+ },
2424
+ "minimax-m2.5": {
2425
+ name: "MiniMax M2.5",
2426
+ contextWindow: 204_800,
2427
+ maxTokens: 131_072,
2428
+ input: ["text"],
2429
+ reasoning: true,
2430
+ cost: { input: 0.3, output: 1.2, cacheRead: 0.06, cacheWrite: 0.375 },
2431
+ },
2432
+ "minimax-m2.7": {
2433
+ name: "MiniMax M2.7",
2434
+ contextWindow: 204_800,
2435
+ maxTokens: 131_072,
2436
+ input: ["text"],
2437
+ reasoning: true,
2438
+ cost: { input: 0.3, output: 1.2, cacheRead: 0.06, cacheWrite: 0.375 },
2439
+ },
2440
+ "minimax-m3": {
2441
+ name: "MiniMax M3",
2442
+ contextWindow: 512_000,
2443
+ maxTokens: 128_000,
2444
+ input: ["text", "image"],
2445
+ reasoning: true,
2446
+ cost: { input: 0.3, output: 1.2, cacheRead: 0.06, cacheWrite: 0 },
2447
+ },
2448
+ "qwen3.5-plus": {
2449
+ name: "Qwen3.5 Plus",
2450
+ contextWindow: 1_000_000,
2451
+ maxTokens: 65_536,
2452
+ input: ["text", "image"],
2453
+ reasoning: true,
2454
+ cost: { input: 0.4, output: 2.4, cacheRead: 0, cacheWrite: 0 },
2455
+ },
2456
+ "qwen3.6-plus": {
2457
+ name: "Qwen3.6 Plus",
2458
+ contextWindow: 1_000_000,
2459
+ maxTokens: 65_536,
2460
+ input: ["text", "image"],
2461
+ reasoning: true,
2462
+ cost: { input: 2, output: 6, cacheRead: 0.2, cacheWrite: 2.5 },
2463
+ },
2464
+ "qwen3.7-max": {
2465
+ name: "Qwen3.7 Max",
2466
+ contextWindow: 1_000_000,
2467
+ maxTokens: 65_536,
2468
+ input: ["text"],
2469
+ reasoning: true,
2470
+ cost: { input: 2.5, output: 7.5, cacheRead: 0.5, cacheWrite: 3.125 },
2471
+ },
2472
+ "qwen3.7-plus": {
2473
+ name: "Qwen3.7 Plus",
2474
+ contextWindow: 1_000_000,
2475
+ maxTokens: 64_000,
2476
+ input: ["text", "image"],
2477
+ reasoning: true,
2478
+ cost: { input: 1.2, output: 4.8, cacheRead: 0.12, cacheWrite: 1.5 },
2479
+ },
2480
+ "mimo-v2-omni": {
2481
+ name: "MiMo-V2-Omni",
2482
+ contextWindow: 262_144,
2483
+ maxTokens: 131_072,
2484
+ input: ["text", "image"],
2485
+ reasoning: true,
2486
+ cost: { input: 0.4, output: 2, cacheRead: 0.08, cacheWrite: 0 },
2487
+ },
2488
+ "mimo-v2-pro": {
2489
+ name: "MiMo-V2-Pro",
2490
+ contextWindow: 1_048_576,
2491
+ maxTokens: 131_072,
2492
+ input: ["text"],
2493
+ reasoning: true,
2494
+ cost: { input: 1, output: 3, cacheRead: 0.2, cacheWrite: 0 },
2495
+ },
2496
+ "mimo-v2.5": {
2497
+ name: "MiMo-V2.5",
2498
+ contextWindow: 1_048_576,
2499
+ maxTokens: 131_072,
2500
+ input: ["text", "image"],
2501
+ reasoning: true,
2502
+ cost: { input: 0.14, output: 0.28, cacheRead: 0.0028, cacheWrite: 0 },
2503
+ },
2504
+ "mimo-v2.5-pro": {
2505
+ name: "MiMo-V2.5-Pro",
2506
+ contextWindow: 1_048_576,
2507
+ maxTokens: 131_072,
2508
+ input: ["text"],
2509
+ reasoning: true,
2510
+ cost: { input: 1.74, output: 3.48, cacheRead: 0.0145, cacheWrite: 0 },
2511
+ },
2512
+ "hy3-preview": {
2513
+ name: "Hy3 preview",
2514
+ contextWindow: 256_000,
2515
+ maxTokens: 64_000,
2516
+ input: ["text"],
2517
+ reasoning: true,
2518
+ cost: { input: 0.066, output: 0.26, cacheRead: 0.029, cacheWrite: 0 },
2519
+ },
2520
+ };
2521
+
2522
+ function applyOpenCodeGoOfficialMetadata<TApi extends Api>(model: Model<TApi>): Model<TApi> {
2523
+ const metadata = OPENCODE_GO_OFFICIAL_MODELS[model.id];
2524
+ if (!metadata) return model;
2525
+ return {
2526
+ ...model,
2527
+ name: metadata.name,
2528
+ reasoning: metadata.reasoning,
2529
+ input: [...metadata.input],
2530
+ cost: { ...metadata.cost },
2531
+ contextWindow: metadata.contextWindow,
2532
+ maxTokens: metadata.maxTokens,
2533
+ };
2534
+ }
2535
+
2536
+ function createOpenCodeGoOfficialModels(): Model<Api>[] {
2537
+ return Object.entries(OPENCODE_GO_OFFICIAL_MODELS).map(([id, metadata]) => {
2538
+ const resolved = resolveApiByRules(
2539
+ id,
2540
+ {},
2541
+ OPENCODE_GO_API_RESOLUTION.rules,
2542
+ OPENCODE_GO_API_RESOLUTION.defaultResolution,
2543
+ );
2544
+ return {
2545
+ id,
2546
+ name: metadata.name,
2547
+ api: resolved.api,
2548
+ provider: "opencode-go",
2549
+ baseUrl: resolved.baseUrl,
2550
+ reasoning: metadata.reasoning,
2551
+ input: [...metadata.input],
2552
+ cost: { ...metadata.cost },
2553
+ contextWindow: metadata.contextWindow,
2554
+ maxTokens: metadata.maxTokens,
2555
+ };
2556
+ });
2557
+ }
2558
+
2323
2559
  const MODELS_DEV_PROVIDER_DESCRIPTORS_SPECIALIZED: readonly ModelsDevProviderDescriptor[] = [
2324
2560
  // --- Cloudflare AI Gateway ---
2325
2561
  anthropicMessagesDescriptor(
@@ -2350,6 +2586,8 @@ const MODELS_DEV_PROVIDER_DESCRIPTORS_SPECIALIZED: readonly ModelsDevProviderDes
2350
2586
  OPENCODE_GO_API_RESOLUTION.rules,
2351
2587
  OPENCODE_GO_API_RESOLUTION.defaultResolution,
2352
2588
  ),
2589
+ transformModel: model => applyOpenCodeGoOfficialMetadata(model),
2590
+ appendModels: createOpenCodeGoOfficialModels(),
2353
2591
  }),
2354
2592
  // --- GitHub Copilot ---
2355
2593
  openAiCompletionsDescriptor("github-copilot", "github-copilot", COPILOT_BASE_URL, {