@oh-my-pi/pi-catalog 16.1.13 → 16.1.14

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.
@@ -35,6 +35,7 @@ import {
35
35
  openrouterModelManagerOptions,
36
36
  qianfanModelManagerOptions,
37
37
  qwenPortalModelManagerOptions,
38
+ sakanaModelManagerOptions,
38
39
  syntheticModelManagerOptions,
39
40
  togetherModelManagerOptions,
40
41
  umansModelManagerOptions,
@@ -308,6 +309,14 @@ export const CATALOG_PROVIDERS = [
308
309
  oauthProvider: "qwen-portal",
309
310
  },
310
311
  },
312
+ {
313
+ id: "sakana",
314
+ defaultModel: "fugu",
315
+ envVars: ["SAKANA_API_KEY", "FUGU_API_KEY"],
316
+ createModelManagerOptions: (config: ModelManagerConfig) => sakanaModelManagerOptions(config),
317
+ dynamicModelsAuthoritative: true,
318
+ catalogDiscovery: { label: "Sakana AI" },
319
+ },
311
320
  {
312
321
  id: "synthetic",
313
322
  defaultModel: "hf:zai-org/GLM-5.1",
@@ -2520,6 +2520,101 @@ export function moonshotModelManagerOptions(
2520
2520
  };
2521
2521
  }
2522
2522
 
2523
+ // ---------------------------------------------------------------------------
2524
+ // 16.5 Sakana AI
2525
+ // ---------------------------------------------------------------------------
2526
+
2527
+ const SAKANA_DEFAULT_BASE_URL = "https://api.sakana.ai/v1";
2528
+ const SAKANA_FREE_ROUTER_COST = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 } as const;
2529
+ const SAKANA_FUGU_ULTRA_COST = { input: 5, output: 30, cacheRead: 0.5, cacheWrite: 0 } as const;
2530
+ const SAKANA_FUGU_THINKING: ThinkingConfig = {
2531
+ mode: "effort",
2532
+ efforts: [Effort.High, Effort.XHigh],
2533
+ effortMap: { [Effort.XHigh]: "max" },
2534
+ };
2535
+ const SAKANA_RESPONSES_COMPAT: ModelSpec<"openai-responses">["compat"] = {
2536
+ includeEncryptedReasoning: false,
2537
+ streamIdleTimeoutMs: 300_000,
2538
+ };
2539
+
2540
+ function normalizeSakanaBaseUrl(baseUrl: string | undefined): string {
2541
+ const value = baseUrl?.trim() || SAKANA_DEFAULT_BASE_URL;
2542
+ const normalized = value.replace(/\/+$/, "");
2543
+ return normalized.endsWith("/v1") ? normalized : `${normalized}/v1`;
2544
+ }
2545
+
2546
+ function isSakanaFuguModelId(modelId: string): boolean {
2547
+ return /^fugu(?:$|-)/i.test(modelId);
2548
+ }
2549
+
2550
+ function createSakanaFuguStaticModel(
2551
+ id: string,
2552
+ name: string,
2553
+ cost: ModelSpec<"openai-responses">["cost"],
2554
+ ): ModelSpec<"openai-responses"> {
2555
+ return {
2556
+ id,
2557
+ name,
2558
+ api: "openai-responses",
2559
+ provider: "sakana",
2560
+ baseUrl: SAKANA_DEFAULT_BASE_URL,
2561
+ reasoning: true,
2562
+ input: ["text"],
2563
+ cost: { ...cost },
2564
+ contextWindow: null,
2565
+ maxTokens: null,
2566
+ thinking: { ...SAKANA_FUGU_THINKING },
2567
+ compat: { ...SAKANA_RESPONSES_COMPAT },
2568
+ };
2569
+ }
2570
+
2571
+ export const SAKANA_FUGU_STATIC_MODELS: readonly ModelSpec<"openai-responses">[] = [
2572
+ createSakanaFuguStaticModel("fugu", "Fugu", SAKANA_FREE_ROUTER_COST),
2573
+ createSakanaFuguStaticModel("fugu-ultra", "Fugu Ultra", SAKANA_FUGU_ULTRA_COST),
2574
+ createSakanaFuguStaticModel("fugu-ultra-20260615", "Fugu Ultra 20260615", SAKANA_FUGU_ULTRA_COST),
2575
+ ];
2576
+
2577
+ const SAKANA_FUGU_STATIC_MODEL_BY_ID = new Map(SAKANA_FUGU_STATIC_MODELS.map(model => [model.id, model] as const));
2578
+
2579
+ export interface SakanaModelManagerConfig {
2580
+ apiKey?: string;
2581
+ baseUrl?: string;
2582
+ fetch?: FetchImpl;
2583
+ }
2584
+
2585
+ export function sakanaModelManagerOptions(config?: SakanaModelManagerConfig): ModelManagerOptions<"openai-responses"> {
2586
+ const apiKey = config?.apiKey;
2587
+ const baseUrl = normalizeSakanaBaseUrl(config?.baseUrl ?? Bun.env.SAKANA_BASE_URL ?? Bun.env.FUGU_BASE_URL);
2588
+ const references = createBundledReferenceMap<"openai-responses">("sakana");
2589
+ return {
2590
+ providerId: "sakana",
2591
+ dynamicModelsAuthoritative: true,
2592
+ ...(apiKey && {
2593
+ fetchDynamicModels: () =>
2594
+ fetchOpenAICompatibleModels({
2595
+ api: "openai-responses",
2596
+ provider: "sakana",
2597
+ baseUrl,
2598
+ apiKey,
2599
+ mapModel: (entry, defaults) => {
2600
+ const reference = references.get(defaults.id) ?? SAKANA_FUGU_STATIC_MODEL_BY_ID.get(defaults.id);
2601
+ const model = mapWithBundledReference(entry, defaults, reference);
2602
+ if (!reference && isSakanaFuguModelId(model.id)) {
2603
+ return {
2604
+ ...model,
2605
+ reasoning: true,
2606
+ thinking: { ...SAKANA_FUGU_THINKING },
2607
+ compat: { ...SAKANA_RESPONSES_COMPAT },
2608
+ };
2609
+ }
2610
+ return model;
2611
+ },
2612
+ fetch: config?.fetch,
2613
+ }),
2614
+ }),
2615
+ };
2616
+ }
2617
+
2523
2618
  // ---------------------------------------------------------------------------
2524
2619
  // 17. Qwen Portal
2525
2620
  // ---------------------------------------------------------------------------
package/src/types.ts CHANGED
@@ -534,6 +534,7 @@ export interface ResolvedOpenAIResponsesCompat extends ResolvedOpenAISharedCompa
534
534
  supportsImageDetailOriginal: boolean;
535
535
  requiresJuiceZeroHack: boolean;
536
536
  supportsObfuscationOptOut: boolean;
537
+ streamIdleTimeoutMs?: number;
537
538
  }
538
539
 
539
540
  /**
@@ -554,6 +555,26 @@ export type ResolvedAnthropicCompat = Required<AnthropicCompat> & {
554
555
  officialEndpoint: boolean;
555
556
  };
556
557
 
558
+ /**
559
+ * Compatibility settings for the devin-agent (Codeium Cascade) API. Cascade
560
+ * selects reasoning effort only by routing to a sibling model id (the
561
+ * `thinking.effortRouting` baked by variant-collapse), never by a wire
562
+ * reasoning/effort field, so the model-thinking deriver must not invent an
563
+ * effort ladder from identity for these models.
564
+ */
565
+ export interface DevinCompat {
566
+ /**
567
+ * Trust only explicit `thinking` metadata; never derive a thinking surface
568
+ * from model identity. A reasoning model with no explicit routed thinking
569
+ * resolves to `thinking: undefined` (`reasoning: true`, no controllable
570
+ * effort) instead of a fabricated minimal/low/medium/high ladder.
571
+ */
572
+ trustExplicitThinkingOnly?: boolean;
573
+ }
574
+
575
+ /** Fully-resolved devin-agent compat view. */
576
+ export type ResolvedDevinCompat = Required<DevinCompat>;
577
+
557
578
  /** Sparse, user-authored compat overrides for a given API (models.json / config vocabulary). */
558
579
  export type CompatConfigOf<TApi extends Api> = TApi extends
559
580
  | "openai-completions"
@@ -564,7 +585,9 @@ export type CompatConfigOf<TApi extends Api> = TApi extends
564
585
  ? OpenAICompat
565
586
  : TApi extends "anthropic-messages"
566
587
  ? AnthropicCompat
567
- : undefined;
588
+ : TApi extends "devin-agent"
589
+ ? DevinCompat
590
+ : undefined;
568
591
 
569
592
  /** Resolved compat for a given API: complete record, materialized once by `buildModel`. */
570
593
  export type CompatOf<TApi extends Api> = TApi extends "openrouter"
@@ -575,7 +598,9 @@ export type CompatOf<TApi extends Api> = TApi extends "openrouter"
575
598
  ? ResolvedOpenAIResponsesCompat
576
599
  : TApi extends "anthropic-messages"
577
600
  ? ResolvedAnthropicCompat
578
- : undefined;
601
+ : TApi extends "devin-agent"
602
+ ? ResolvedDevinCompat
603
+ : undefined;
579
604
 
580
605
  // Model interface for the unified model system
581
606
  export interface Model<TApi extends Api = Api> {
@@ -111,6 +111,55 @@ function thinkingPair(baseId: string, name: string): EffortVariantFamily {
111
111
  };
112
112
  }
113
113
 
114
+ type DevinTierRoutes = Partial<Record<"off" | "minimal" | "low" | "medium" | "high" | "xhigh", string>>;
115
+
116
+ function devinTierFamily(
117
+ id: string,
118
+ name: string,
119
+ routes: DevinTierRoutes,
120
+ efforts: readonly Effort[],
121
+ ): EffortVariantFamily {
122
+ const routing: Partial<Record<Effort | "off", string>> = {};
123
+ if (routes.off) routing.off = routes.off;
124
+ for (const effort of efforts) {
125
+ switch (effort) {
126
+ case Effort.Minimal:
127
+ if (routes.minimal) {
128
+ routing[effort] = routes.minimal;
129
+ } else if (routes.low) {
130
+ routing[effort] = routes.low;
131
+ }
132
+ break;
133
+ case Effort.Low:
134
+ if (routes.low) routing[effort] = routes.low;
135
+ break;
136
+ case Effort.Medium:
137
+ if (routes.medium) routing[effort] = routes.medium;
138
+ break;
139
+ case Effort.High:
140
+ if (routes.high) routing[effort] = routes.high;
141
+ break;
142
+ case Effort.XHigh:
143
+ if (routes.xhigh) routing[effort] = routes.xhigh;
144
+ break;
145
+ }
146
+ }
147
+ const members = [routes.off, routes.minimal, routes.low, routes.medium, routes.high, routes.xhigh].filter(
148
+ (member, index, items): member is string => typeof member === "string" && items.indexOf(member) === index,
149
+ );
150
+ return {
151
+ id,
152
+ name,
153
+ members,
154
+ routing,
155
+ thinking: {
156
+ mode: "effort",
157
+ efforts,
158
+ ...(routes.off ? undefined : { requiresEffort: true }),
159
+ },
160
+ };
161
+ }
162
+
114
163
  const GEMINI_3_FLASH_FAMILY_EFFORTS: readonly Effort[] = [Effort.Minimal, Effort.Low, Effort.Medium, Effort.High];
115
164
  const GEMINI_3_PRO_FAMILY_EFFORTS: readonly Effort[] = [Effort.Low, Effort.High];
116
165
 
@@ -237,6 +286,7 @@ const SHARED_CCA_FAMILIES: readonly EffortVariantFamily[] = [
237
286
  routing: {},
238
287
  thinking: { mode: "budget", efforts: [Effort.Minimal, Effort.Low, Effort.Medium, Effort.High] },
239
288
  },
289
+
240
290
  {
241
291
  id: "claude-opus-4-6",
242
292
  name: "Claude Opus 4.6",
@@ -259,11 +309,232 @@ export const ANTIGRAVITY_VARIANT_COLLAPSE_TABLE: VariantCollapseTable = {
259
309
  export const GEMINI_CLI_VARIANT_COLLAPSE_TABLE: VariantCollapseTable = {
260
310
  families: [geminiFlashFamily("google-level"), geminiProFamily("google-level"), ...SHARED_CCA_FAMILIES],
261
311
  };
312
+ export const DEVIN_VARIANT_COLLAPSE_TABLE: VariantCollapseTable = {
313
+ families: [
314
+ {
315
+ id: "claude-opus-4-7",
316
+ name: "Claude Opus 4.7",
317
+ members: [
318
+ "claude-opus-4-7-low",
319
+ "claude-opus-4-7-medium",
320
+ "claude-opus-4-7-high",
321
+ "claude-opus-4-7-xhigh",
322
+ "claude-opus-4-7-max",
323
+ ],
324
+ routing: {
325
+ [Effort.Minimal]: "claude-opus-4-7-low",
326
+ [Effort.Low]: "claude-opus-4-7-medium",
327
+ [Effort.Medium]: "claude-opus-4-7-high",
328
+ [Effort.High]: "claude-opus-4-7-xhigh",
329
+ [Effort.XHigh]: "claude-opus-4-7-max",
330
+ },
331
+ thinking: {
332
+ mode: "effort",
333
+ efforts: [Effort.Minimal, Effort.Low, Effort.Medium, Effort.High, Effort.XHigh],
334
+ requiresEffort: true,
335
+ },
336
+ },
337
+ {
338
+ id: "claude-opus-4-7-fast",
339
+ name: "Claude Opus 4.7 Fast",
340
+ members: [
341
+ "claude-opus-4-7-low-fast",
342
+ "claude-opus-4-7-medium-fast",
343
+ "claude-opus-4-7-high-fast",
344
+ "claude-opus-4-7-xhigh-fast",
345
+ "claude-opus-4-7-max-fast",
346
+ ],
347
+ routing: {
348
+ [Effort.Minimal]: "claude-opus-4-7-low-fast",
349
+ [Effort.Low]: "claude-opus-4-7-medium-fast",
350
+ [Effort.Medium]: "claude-opus-4-7-high-fast",
351
+ [Effort.High]: "claude-opus-4-7-xhigh-fast",
352
+ [Effort.XHigh]: "claude-opus-4-7-max-fast",
353
+ },
354
+ thinking: {
355
+ mode: "effort",
356
+ efforts: [Effort.Minimal, Effort.Low, Effort.Medium, Effort.High, Effort.XHigh],
357
+ requiresEffort: true,
358
+ },
359
+ },
360
+ {
361
+ id: "claude-opus-4-8",
362
+ name: "Claude Opus 4.8",
363
+ members: [
364
+ "claude-opus-4-8-low",
365
+ "claude-opus-4-8-medium",
366
+ "claude-opus-4-8-high",
367
+ "claude-opus-4-8-xhigh",
368
+ "claude-opus-4-8-max",
369
+ ],
370
+ routing: {
371
+ [Effort.Minimal]: "claude-opus-4-8-low",
372
+ [Effort.Low]: "claude-opus-4-8-medium",
373
+ [Effort.Medium]: "claude-opus-4-8-high",
374
+ [Effort.High]: "claude-opus-4-8-xhigh",
375
+ [Effort.XHigh]: "claude-opus-4-8-max",
376
+ },
377
+ thinking: {
378
+ mode: "effort",
379
+ efforts: [Effort.Minimal, Effort.Low, Effort.Medium, Effort.High, Effort.XHigh],
380
+ requiresEffort: true,
381
+ },
382
+ },
383
+ {
384
+ id: "claude-opus-4-8-fast",
385
+ name: "Claude Opus 4.8 Fast",
386
+ members: [
387
+ "claude-opus-4-8-low-fast",
388
+ "claude-opus-4-8-medium-fast",
389
+ "claude-opus-4-8-high-fast",
390
+ "claude-opus-4-8-xhigh-fast",
391
+ "claude-opus-4-8-max-fast",
392
+ ],
393
+ routing: {
394
+ [Effort.Minimal]: "claude-opus-4-8-low-fast",
395
+ [Effort.Low]: "claude-opus-4-8-medium-fast",
396
+ [Effort.Medium]: "claude-opus-4-8-high-fast",
397
+ [Effort.High]: "claude-opus-4-8-xhigh-fast",
398
+ [Effort.XHigh]: "claude-opus-4-8-max-fast",
399
+ },
400
+ thinking: {
401
+ mode: "effort",
402
+ efforts: [Effort.Minimal, Effort.Low, Effort.Medium, Effort.High, Effort.XHigh],
403
+ requiresEffort: true,
404
+ },
405
+ },
406
+ devinTierFamily(
407
+ "gpt-5-2",
408
+ "GPT-5.2",
409
+ {
410
+ off: "MODEL_GPT_5_2_NONE",
411
+ low: "MODEL_GPT_5_2_LOW",
412
+ medium: "MODEL_GPT_5_2_MEDIUM",
413
+ high: "MODEL_GPT_5_2_HIGH",
414
+ xhigh: "MODEL_GPT_5_2_XHIGH",
415
+ },
416
+ [Effort.Minimal, Effort.Low, Effort.Medium, Effort.High, Effort.XHigh],
417
+ ),
418
+ devinTierFamily(
419
+ "gpt-5-3-codex",
420
+ "GPT-5.3 Codex",
421
+ {
422
+ low: "gpt-5-3-codex-low",
423
+ medium: "gpt-5-3-codex-medium",
424
+ high: "gpt-5-3-codex-high",
425
+ xhigh: "gpt-5-3-codex-xhigh",
426
+ },
427
+ [Effort.Minimal, Effort.Low, Effort.Medium, Effort.High, Effort.XHigh],
428
+ ),
429
+ devinTierFamily(
430
+ "gpt-5-3-codex-fast",
431
+ "GPT-5.3 Codex Fast",
432
+ {
433
+ low: "gpt-5-3-codex-low-priority",
434
+ medium: "gpt-5-3-codex-medium-priority",
435
+ high: "gpt-5-3-codex-high-priority",
436
+ xhigh: "gpt-5-3-codex-xhigh-priority",
437
+ },
438
+ [Effort.Minimal, Effort.Low, Effort.Medium, Effort.High, Effort.XHigh],
439
+ ),
440
+ devinTierFamily(
441
+ "gpt-5-4",
442
+ "GPT-5.4",
443
+ {
444
+ off: "gpt-5-4-none",
445
+ low: "gpt-5-4-low",
446
+ medium: "gpt-5-4-medium",
447
+ high: "gpt-5-4-high",
448
+ xhigh: "gpt-5-4-xhigh",
449
+ },
450
+ [Effort.Minimal, Effort.Low, Effort.Medium, Effort.High, Effort.XHigh],
451
+ ),
452
+ devinTierFamily(
453
+ "gpt-5-4-fast",
454
+ "GPT-5.4 Fast",
455
+ {
456
+ off: "gpt-5-4-none-priority",
457
+ low: "gpt-5-4-low-priority",
458
+ medium: "gpt-5-4-medium-priority",
459
+ high: "gpt-5-4-high-priority",
460
+ xhigh: "gpt-5-4-xhigh-priority",
461
+ },
462
+ [Effort.Minimal, Effort.Low, Effort.Medium, Effort.High, Effort.XHigh],
463
+ ),
464
+ devinTierFamily(
465
+ "gpt-5-4-mini",
466
+ "GPT-5.4 Mini",
467
+ {
468
+ low: "gpt-5-4-mini-low",
469
+ medium: "gpt-5-4-mini-medium",
470
+ high: "gpt-5-4-mini-high",
471
+ xhigh: "gpt-5-4-mini-xhigh",
472
+ },
473
+ [Effort.Minimal, Effort.Low, Effort.Medium, Effort.High, Effort.XHigh],
474
+ ),
475
+ devinTierFamily(
476
+ "gpt-5-5",
477
+ "GPT-5.5",
478
+ {
479
+ off: "gpt-5-5-none",
480
+ low: "gpt-5-5-low",
481
+ medium: "gpt-5-5-medium",
482
+ high: "gpt-5-5-high",
483
+ xhigh: "gpt-5-5-xhigh",
484
+ },
485
+ [Effort.Minimal, Effort.Low, Effort.Medium, Effort.High, Effort.XHigh],
486
+ ),
487
+ devinTierFamily(
488
+ "gpt-5-5-fast",
489
+ "GPT-5.5 Fast",
490
+ {
491
+ off: "gpt-5-5-none-priority",
492
+ low: "gpt-5-5-low-priority",
493
+ medium: "gpt-5-5-medium-priority",
494
+ high: "gpt-5-5-high-priority",
495
+ xhigh: "gpt-5-5-xhigh-priority",
496
+ },
497
+ [Effort.Minimal, Effort.Low, Effort.Medium, Effort.High, Effort.XHigh],
498
+ ),
499
+ devinTierFamily(
500
+ "gemini-3-1-pro",
501
+ "Gemini 3.1 Pro",
502
+ {
503
+ low: "gemini-3-1-pro-low",
504
+ high: "gemini-3-1-pro-high",
505
+ },
506
+ [Effort.Low, Effort.High],
507
+ ),
508
+ devinTierFamily(
509
+ "gemini-3-5-flash",
510
+ "Gemini 3.5 Flash",
511
+ {
512
+ minimal: "gemini-3-5-flash-minimal",
513
+ low: "gemini-3-5-flash-low",
514
+ medium: "gemini-3-5-flash-medium",
515
+ high: "gemini-3-5-flash-high",
516
+ },
517
+ [Effort.Minimal, Effort.Low, Effort.Medium, Effort.High],
518
+ ),
519
+ devinTierFamily(
520
+ "gemini-3-flash",
521
+ "Gemini 3 Flash",
522
+ {
523
+ minimal: "MODEL_GOOGLE_GEMINI_3_0_FLASH_MINIMAL",
524
+ low: "MODEL_GOOGLE_GEMINI_3_0_FLASH_LOW",
525
+ medium: "MODEL_GOOGLE_GEMINI_3_0_FLASH_MEDIUM",
526
+ high: "MODEL_GOOGLE_GEMINI_3_0_FLASH_HIGH",
527
+ },
528
+ [Effort.Minimal, Effort.Low, Effort.Medium, Effort.High],
529
+ ),
530
+ ],
531
+ };
262
532
 
263
533
  /** Provider id → hand collapse table. The CCA providers diverge on thinking transport. */
264
534
  export const VARIANT_COLLAPSE_TABLES: Readonly<Record<string, VariantCollapseTable>> = {
265
535
  "google-antigravity": ANTIGRAVITY_VARIANT_COLLAPSE_TABLE,
266
536
  "google-gemini-cli": GEMINI_CLI_VARIANT_COLLAPSE_TABLE,
537
+ devin: DEVIN_VARIANT_COLLAPSE_TABLE,
267
538
  };
268
539
 
269
540
  /**