@oh-my-pi/pi-catalog 17.0.9 → 17.1.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/CHANGELOG.md CHANGED
@@ -2,6 +2,29 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [17.1.1] - 2026-07-24
6
+
7
+ ### Added
8
+
9
+ - Added catalog metadata for models that support native computer-use requests.
10
+ - Added resolved Bedrock Converse prompt-cache compatibility limits, including explicit 5-minute checkpoint support for bundled Nova Lite, Micro, Pro, Premier, and Nova 2 Lite models plus their documented in-region, regional, and global IDs, and model-specific 1-hour Claude retention.
11
+ - Added resolved Bedrock Converse prompt-cache compatibility limits, including explicit 5-minute checkpoint support for bundled Nova Lite, Micro, Pro, and Premier models plus Nova Premier's documented in-region model ID, and model-specific 1-hour Claude retention.
12
+ - Added the native Meta Model API provider and Muse Spark 1.1 with Responses API reasoning replay, image input, and the full supported reasoning-effort ladder ([#4941](https://github.com/can1357/oh-my-pi/issues/4941)).
13
+ - Added an opt-in Vercel AI Gateway automatic prompt-cache compatibility option alongside provider routing preferences.
14
+ - Added Vercel AI Gateway Responses cache-anchor and cache-lifetime compatibility controls.
15
+ - Added resolved OpenAI GPT-5.6 prompt-cache breakpoint capability metadata, keeping older models and compatible endpoints opt-in only.
16
+ - Added the native `alibaba-token-plan` provider with QwenCloud Token Plan Individual discovery and a curated chat-model fallback catalog ([#6151](https://github.com/can1357/oh-my-pi/issues/6151)).
17
+
18
+ ## [17.1.0] - 2026-07-24
19
+
20
+ ### Added
21
+
22
+ - Added Bedrock Converse prompt-cache compatibility limits, including 5-minute checkpoint support for Nova models (Lite, Micro, Pro, Premier, Nova 2 Lite) and 1-hour Claude retention.
23
+ - Added native Meta Model API provider and Muse Spark 1.1 support, featuring Responses API reasoning replay, image input, and reasoning-effort controls.
24
+ - Added Vercel AI Gateway integration features, including opt-in automatic prompt-cache compatibility, provider routing preferences, and Responses cache-anchor and cache-lifetime controls.
25
+ - Added prompt-cache breakpoint capability metadata for OpenAI GPT-5.6, with opt-in support for older models and compatible endpoints.
26
+ - Added native alibaba-token-plan provider with QwenCloud Token Plan Individual discovery and a curated chat-model fallback catalog.
27
+
5
28
  ## [17.0.9] - 2026-07-23
6
29
 
7
30
  ### Changed
@@ -1,3 +1,14 @@
1
+ /**
2
+ * The single Model constructor. Resolution order is a dependency chain, each
3
+ * step materialized exactly once per spec:
4
+ *
5
+ * 1. compat — URL/provider/id detection resolved into a complete record;
6
+ * 2. thinking — derived from identity + resolved compat (or trusted verbatim
7
+ * when the spec carries explicit metadata);
8
+ *
9
+ * Request handlers read fields — they never detect, parse ids, or allocate
10
+ * compat per request.
11
+ */
1
12
  import type { Api, CompatOf, Model, ModelSpec } from "./types.js";
2
13
  export declare function buildModel<TApi extends Api>(spec: ModelSpec<TApi>): Model<TApi>;
3
14
  export declare function buildCompat(spec: ModelSpec<Api>): CompatOf<Api>;
@@ -0,0 +1,3 @@
1
+ import type { ModelSpec, ResolvedBedrockCompat } from "../types.js";
2
+ /** Resolve Bedrock Converse prompt-cache capabilities once per model. */
3
+ export declare function buildBedrockCompat(spec: ModelSpec<"bedrock-converse-stream">): ResolvedBedrockCompat;
@@ -62,8 +62,8 @@ export declare const KNOWN_HOSTS: {
62
62
  readonly urlMarkers: readonly ["api.kilo.ai"];
63
63
  };
64
64
  readonly alibabaDashscope: {
65
- readonly providers: readonly ["alibaba-coding-plan"];
66
- readonly urlMarkers: readonly ["dashscope"];
65
+ readonly providers: readonly ["alibaba-coding-plan", "alibaba-token-plan"];
66
+ readonly urlMarkers: readonly ["dashscope", "token-plan."];
67
67
  };
68
68
  readonly umans: {
69
69
  readonly providers: readonly ["umans"];
@@ -23,6 +23,15 @@ export declare const CATALOG_PROVIDERS: readonly [{
23
23
  readonly catalogDiscovery: {
24
24
  readonly label: "Alibaba Coding Plan";
25
25
  };
26
+ }, {
27
+ readonly id: "alibaba-token-plan";
28
+ readonly defaultModel: "qwen3.7-plus";
29
+ readonly envVars: readonly ["ALIBABA_TOKEN_PLAN_API_KEY", "BAILIAN_TOKEN_PLAN_API_KEY"];
30
+ readonly createModelManagerOptions: (config: ModelManagerConfig) => import("../index.js").ModelManagerOptions<"openai-completions", unknown>;
31
+ readonly dynamicModelsAuthoritative: true;
32
+ readonly catalogDiscovery: {
33
+ readonly label: "QwenCloud Token Plan";
34
+ };
26
35
  }, {
27
36
  readonly id: "baseten";
28
37
  readonly defaultModel: "moonshotai/Kimi-K2.7-Code";
@@ -200,6 +209,14 @@ export declare const CATALOG_PROVIDERS: readonly [{
200
209
  readonly defaultModel: "devstral-medium-latest";
201
210
  readonly envVars: readonly ["MISTRAL_API_KEY"];
202
211
  readonly createModelManagerOptions: (config: ModelManagerConfig) => import("../index.js").ModelManagerOptions<"openai-completions", unknown>;
212
+ }, {
213
+ readonly id: "meta";
214
+ readonly defaultModel: "muse-spark-1.1";
215
+ readonly envVars: readonly ["MODEL_API_KEY", "META_API_KEY"];
216
+ readonly createModelManagerOptions: (config: ModelManagerConfig) => import("../index.js").ModelManagerOptions<"openai-responses", unknown>;
217
+ readonly catalogDiscovery: {
218
+ readonly label: "Meta Model API";
219
+ };
203
220
  }, {
204
221
  readonly id: "moonshot";
205
222
  readonly defaultModel: "kimi-k2.7-code";
@@ -299,6 +299,14 @@ export interface AlibabaCodingPlanModelManagerConfig {
299
299
  fetch?: FetchImpl;
300
300
  }
301
301
  export declare function alibabaCodingPlanModelManagerOptions(config?: AlibabaCodingPlanModelManagerConfig): ModelManagerOptions<"openai-completions">;
302
+ export declare const ALIBABA_TOKEN_PLAN_BASE_URL = "https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1";
303
+ export declare const ALIBABA_TOKEN_PLAN_STATIC_MODELS: readonly ModelSpec<"openai-completions">[];
304
+ export interface AlibabaTokenPlanModelManagerConfig {
305
+ apiKey?: string;
306
+ baseUrl?: string;
307
+ fetch?: FetchImpl;
308
+ }
309
+ export declare function alibabaTokenPlanModelManagerOptions(config?: AlibabaTokenPlanModelManagerConfig): ModelManagerOptions<"openai-completions">;
302
310
  export interface VercelAiGatewayModelManagerConfig {
303
311
  apiKey?: string;
304
312
  baseUrl?: string;
@@ -359,6 +367,13 @@ export interface CoreWeaveModelManagerConfig {
359
367
  fetch?: FetchImpl;
360
368
  }
361
369
  export declare function coreWeaveModelManagerOptions(config?: CoreWeaveModelManagerConfig): ModelManagerOptions<"openai-completions">;
370
+ export declare const META_MUSE_STATIC_MODELS: readonly ModelSpec<"openai-responses">[];
371
+ export interface MetaModelManagerConfig {
372
+ apiKey?: string;
373
+ baseUrl?: string;
374
+ fetch?: FetchImpl;
375
+ }
376
+ export declare function metaModelManagerOptions(config?: MetaModelManagerConfig): ModelManagerOptions<"openai-responses">;
362
377
  export interface MoonshotModelManagerConfig {
363
378
  apiKey?: string;
364
379
  baseUrl?: string;
@@ -268,6 +268,14 @@ export interface OpenAICompat {
268
268
  promptCacheSessionHeader?: "x-grok-conv-id";
269
269
  /** Whether chat-completions payloads should include provider-specific prompt-cache markers. */
270
270
  cacheControlFormat?: "anthropic" | undefined;
271
+ /**
272
+ * Whether this endpoint/model accepts OpenAI's explicit
273
+ * `prompt_cache_breakpoint` content markers and `prompt_cache_options`.
274
+ * Defaults to the exact first-party model generation allowlist.
275
+ */
276
+ supportsPromptCacheBreakpoints?: boolean;
277
+ /** The only currently supported minimum lifetime for explicit OpenAI cache breakpoints. */
278
+ promptCacheBreakpointTtl?: "30m";
271
279
  /** Whether the provider supports the `strict` field in tool definitions. Default: auto-detected per provider/baseUrl (conservative for unknown providers). */
272
280
  supportsStrictMode?: boolean;
273
281
  /**
@@ -419,6 +427,34 @@ export interface AnthropicCompat {
419
427
  */
420
428
  escapeBuiltinToolNames?: boolean;
421
429
  }
430
+ /**
431
+ * Compatibility settings for Bedrock Converse prompt caching. Cache pricing is
432
+ * deliberately not used to infer these request-shape capabilities.
433
+ */
434
+ export interface BedrockCompat {
435
+ /** Whether this endpoint accepts no checkpoints, automatic caching, or explicit cachePoint blocks. */
436
+ promptCacheMode?: "none" | "automatic" | "explicit";
437
+ /** Whether explicit cachePoint blocks accept `ttl: "1h"`; omitted TTL means Bedrock's 5-minute default. */
438
+ supportsLongPromptCacheRetention?: boolean;
439
+ /**
440
+ * Bedrock-enforced minimum prompt-prefix tokens for an effective checkpoint.
441
+ * Capability metadata only: emitters must not estimate local token counts.
442
+ * Zero means no explicit checkpoints.
443
+ */
444
+ promptCacheMinimumTokens?: number;
445
+ /**
446
+ * Bedrock-enforced maximum explicit cache checkpoints per request.
447
+ * Capability metadata only; zero means no explicit checkpoints.
448
+ */
449
+ promptCacheMaximumCheckpoints?: number;
450
+ }
451
+ /** Fully-resolved Bedrock Converse prompt-cache capabilities, materialized once by `buildModel`. */
452
+ export interface ResolvedBedrockCompat {
453
+ promptCacheMode: NonNullable<BedrockCompat["promptCacheMode"]>;
454
+ supportsLongPromptCacheRetention: boolean;
455
+ promptCacheMinimumTokens: number;
456
+ promptCacheMaximumCheckpoints: number;
457
+ }
422
458
  /**
423
459
  * OpenRouter provider routing preferences.
424
460
  * Controls which upstream providers OpenRouter routes requests to.
@@ -440,6 +476,12 @@ export interface VercelGatewayRouting {
440
476
  only?: string[];
441
477
  /** List of provider slugs to try in order (e.g., ["anthropic", "openai"]). */
442
478
  order?: string[];
479
+ /** Enables Vercel AI Gateway's provider-aware automatic prompt caching. */
480
+ caching?: "auto";
481
+ /** Stable Responses input-item prefix to anchor for automatic caching. */
482
+ cacheAnchorItems?: number;
483
+ /** Requested automatic-cache lifetime for the Responses API. */
484
+ cacheTtl?: "5m" | "1h";
443
485
  }
444
486
  type ResolvedToolStrictMode = NonNullable<OpenAICompat["toolStrictMode"]> | "mixed";
445
487
  /**
@@ -482,6 +524,14 @@ export interface ResolvedOpenAISharedCompat {
482
524
  emptyLengthFinishIsContextError: boolean;
483
525
  usesOpenAIToolCallIdLimit: boolean;
484
526
  promptCacheSessionHeader?: OpenAICompat["promptCacheSessionHeader"];
527
+ /**
528
+ * Whether this model accepts explicit OpenAI prompt-cache breakpoints.
529
+ * Built catalog models always materialize this false-by-default value;
530
+ * optionality preserves hand-authored resolved compat fixtures.
531
+ */
532
+ supportsPromptCacheBreakpoints?: boolean;
533
+ /** Minimum cache lifetime supported by explicit OpenAI prompt-cache breakpoints. */
534
+ promptCacheBreakpointTtl?: "30m";
485
535
  /** The model sits behind OpenRouter (routing prefs and max-token omission apply). */
486
536
  isOpenRouterHost: boolean;
487
537
  /** Whether this endpoint needs a max-token field even when caller did not set one. */
@@ -500,7 +550,7 @@ export interface ResolvedOpenAISharedCompat {
500
550
  * `buildModel`; request handlers read fields and never detect, resolve, or
501
551
  * allocate.
502
552
  */
503
- export type ResolvedOpenAICompat = ResolvedOpenAISharedCompat & Required<Omit<OpenAICompat, "supportsDeveloperRole" | "supportsReasoningEffort" | "reasoningEffortMap" | "supportsReasoningParams" | "supportsSamplingParams" | "thinkingFormat" | "kimiApiFormat" | "reasoningDisableMode" | "omitReasoningEffort" | "includeEncryptedReasoning" | "filterReasoningHistory" | "disableReasoningOnForcedToolChoice" | "disableReasoningOnToolChoice" | "supportsToolChoice" | "supportsForcedToolChoice" | "supportsNamedToolChoice" | "reasoningContentField" | "requiresReasoningContentForToolCalls" | "requiresReasoningContentForAllAssistantTurns" | "allowsSyntheticReasoningContentForToolCalls" | "replayReasoningContent" | "qwenPreserveThinking" | "requiresThinkingAsText" | "requiresMistralToolIds" | "requiresToolResultName" | "requiresAssistantAfterToolResult" | "requiresAssistantContentForToolCalls" | "stripDeepseekSpecialTokens" | "streamMarkupHealingPattern" | "reasoningDeltasMayBeCumulative" | "emptyLengthFinishIsContextError" | "usesOpenAIToolCallIdLimit" | "promptCacheSessionHeader" | "openRouterRouting" | "isOpenRouterHost" | "supportsStrictMode" | "supportsLongPromptCacheRetention" | "alwaysSendMaxTokens" | "wireModelIdMode" | "vercelGatewayRouting" | "extraBody" | "toolStrictMode" | "toolSchemaFlavor" | "streamIdleTimeoutMs" | "cacheControlFormat" | "thinkingKeep" | "strictResponsesPairing" | "supportsImageDetailOriginal" | "enableGeminiThinkingLoopGuard" | "whenThinking">> & {
553
+ export type ResolvedOpenAICompat = ResolvedOpenAISharedCompat & Required<Omit<OpenAICompat, "supportsDeveloperRole" | "supportsReasoningEffort" | "reasoningEffortMap" | "supportsReasoningParams" | "supportsSamplingParams" | "thinkingFormat" | "kimiApiFormat" | "reasoningDisableMode" | "omitReasoningEffort" | "includeEncryptedReasoning" | "filterReasoningHistory" | "disableReasoningOnForcedToolChoice" | "disableReasoningOnToolChoice" | "supportsToolChoice" | "supportsForcedToolChoice" | "supportsNamedToolChoice" | "reasoningContentField" | "requiresReasoningContentForToolCalls" | "requiresReasoningContentForAllAssistantTurns" | "allowsSyntheticReasoningContentForToolCalls" | "replayReasoningContent" | "qwenPreserveThinking" | "requiresThinkingAsText" | "requiresMistralToolIds" | "requiresToolResultName" | "requiresAssistantAfterToolResult" | "requiresAssistantContentForToolCalls" | "stripDeepseekSpecialTokens" | "streamMarkupHealingPattern" | "reasoningDeltasMayBeCumulative" | "emptyLengthFinishIsContextError" | "usesOpenAIToolCallIdLimit" | "promptCacheSessionHeader" | "supportsPromptCacheBreakpoints" | "promptCacheBreakpointTtl" | "openRouterRouting" | "isOpenRouterHost" | "supportsStrictMode" | "supportsLongPromptCacheRetention" | "alwaysSendMaxTokens" | "wireModelIdMode" | "vercelGatewayRouting" | "extraBody" | "toolStrictMode" | "toolSchemaFlavor" | "streamIdleTimeoutMs" | "cacheControlFormat" | "thinkingKeep" | "strictResponsesPairing" | "supportsImageDetailOriginal" | "enableGeminiThinkingLoopGuard" | "whenThinking">> & {
504
554
  vercelGatewayRouting?: OpenAICompat["vercelGatewayRouting"];
505
555
  extraBody?: OpenAICompat["extraBody"];
506
556
  cacheControlFormat?: OpenAICompat["cacheControlFormat"];
@@ -520,6 +570,9 @@ export interface ResolvedOpenAIResponsesCompat extends ResolvedOpenAISharedCompa
520
570
  supportsImageDetailOriginal: boolean;
521
571
  supportsObfuscationOptOut: boolean;
522
572
  streamIdleTimeoutMs?: number;
573
+ vercelGatewayRouting?: OpenAICompat["vercelGatewayRouting"];
574
+ /** The model sits behind Vercel AI Gateway's Responses endpoint. */
575
+ isVercelGatewayHost: boolean;
523
576
  }
524
577
  /**
525
578
  * OpenRouter is a pseudo API: runtime dispatch can use either Responses
@@ -567,9 +620,9 @@ export interface DevinCompat {
567
620
  /** Fully-resolved devin-agent compat view. */
568
621
  export type ResolvedDevinCompat = Required<DevinCompat>;
569
622
  /** Sparse, user-authored compat overrides for a given API (models.json / config vocabulary). */
570
- export type CompatConfigOf<TApi extends Api> = TApi extends "openai-completions" | "openrouter" | "openai-responses" | "azure-openai-responses" | "openai-codex-responses" ? OpenAICompat : TApi extends "anthropic-messages" ? AnthropicCompat : TApi extends "devin-agent" ? DevinCompat : undefined;
623
+ export type CompatConfigOf<TApi extends Api> = TApi extends "openai-completions" | "openrouter" | "openai-responses" | "azure-openai-responses" | "openai-codex-responses" ? OpenAICompat : TApi extends "anthropic-messages" ? AnthropicCompat : TApi extends "bedrock-converse-stream" ? BedrockCompat : TApi extends "devin-agent" ? DevinCompat : undefined;
571
624
  /** Resolved compat for a given API: complete record, materialized once by `buildModel`. */
572
- export type CompatOf<TApi extends Api> = TApi extends "openrouter" ? ResolvedOpenRouterCompat : TApi extends "openai-completions" ? ResolvedOpenAICompat : TApi extends "openai-responses" | "azure-openai-responses" | "openai-codex-responses" ? ResolvedOpenAIResponsesCompat : TApi extends "anthropic-messages" ? ResolvedAnthropicCompat : TApi extends "devin-agent" ? ResolvedDevinCompat : undefined;
625
+ export type CompatOf<TApi extends Api> = TApi extends "openrouter" ? ResolvedOpenRouterCompat : TApi extends "openai-completions" ? ResolvedOpenAICompat : TApi extends "openai-responses" | "azure-openai-responses" | "openai-codex-responses" ? ResolvedOpenAIResponsesCompat : TApi extends "anthropic-messages" ? ResolvedAnthropicCompat : TApi extends "bedrock-converse-stream" ? ResolvedBedrockCompat : TApi extends "devin-agent" ? ResolvedDevinCompat : undefined;
573
626
  /** Provider-native compaction endpoint configuration for one model. */
574
627
  export interface RemoteCompactionConfig<TApi extends Api = Api> {
575
628
  /** Enables provider-native compaction for providers not enabled by built-in policy. */
@@ -623,6 +676,8 @@ export interface Model<TApi extends Api = Api> {
623
676
  * reports that native tool calling is unsupported.
624
677
  */
625
678
  supportsTools?: boolean;
679
+ /** Whether this model accepts the GA OpenAI Responses `{ type: "computer" }` native tool. */
680
+ supportsComputerUse?: boolean;
626
681
  /** GitLab Duo Workflow root namespace selected during catalog discovery. */
627
682
  gitlabDuoWorkflowRootNamespaceId?: string;
628
683
  /** Cursor `max_mode` request flag returned by `GetUsableModels` for premium models that require max mode. */
@@ -0,0 +1,6 @@
1
+ export interface AlibabaTokenPlanCredential {
2
+ token: string;
3
+ cookie?: string;
4
+ }
5
+ export declare function parseAlibabaTokenPlanCredential(value: string): AlibabaTokenPlanCredential | null;
6
+ export declare function serializeAlibabaTokenPlanCredential(token: string, cookie: string): string;
@@ -22,6 +22,8 @@ export declare const OPENAI_HEADERS: {
22
22
  readonly SUBAGENT: "x-openai-subagent";
23
23
  /** Responses Lite transport marker (codex-rs `add_responses_lite_header`); value is always `"true"`. */
24
24
  readonly RESPONSES_LITE: "x-openai-internal-codex-responses-lite";
25
+ /** DeviceCheck attestation envelope (codex-rs `X_OAI_ATTESTATION_HEADER`); sent on ChatGPT-OAuth requests. */
26
+ readonly ATTESTATION: "x-oai-attestation";
25
27
  };
26
28
  export declare const OPENAI_HEADER_VALUES: {
27
29
  readonly BETA_RESPONSES: "responses=experimental";
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@oh-my-pi/pi-catalog",
4
- "version": "17.0.9",
4
+ "version": "17.1.1",
5
5
  "description": "Model catalog for omp: bundled model database, provider discovery descriptors, model identity, classification, and equivalence",
6
6
  "homepage": "https://omp.sh",
7
7
  "author": "Can Boluk",
@@ -34,12 +34,12 @@
34
34
  },
35
35
  "dependencies": {
36
36
  "@bufbuild/protobuf": "^2.12.1",
37
- "@oh-my-pi/pi-utils": "17.0.9",
37
+ "@oh-my-pi/pi-utils": "17.1.1",
38
38
  "arktype": "2.2.3",
39
39
  "zod": "^4"
40
40
  },
41
41
  "devDependencies": {
42
- "@oh-my-pi/pi-ai": "17.0.9",
42
+ "@oh-my-pi/pi-ai": "17.1.1",
43
43
  "@types/bun": "^1.3.14"
44
44
  },
45
45
  "engines": {
package/src/build.ts CHANGED
@@ -9,19 +9,39 @@
9
9
  * Request handlers read fields — they never detect, parse ids, or allocate
10
10
  * compat per request.
11
11
  */
12
+
12
13
  import { buildAnthropicCompat } from "./compat/anthropic";
14
+ import { buildBedrockCompat } from "./compat/bedrock";
13
15
  import { buildDevinCompat } from "./compat/devin";
14
16
  import { buildOpenAICompat, buildOpenAIResponsesCompat, buildOpenRouterCompat } from "./compat/openai";
15
17
  import { resolveModelThinking } from "./model-thinking";
16
18
  import type { Api, CompatOf, Model, ModelSpec } from "./types";
17
19
  import { cleanModelName } from "./utils";
18
20
 
21
+ const OPENAI_GA_COMPUTER_MODEL_RE = /^gpt-5\.(?:[4-9]|[1-9]\d)(?:[.-]|$)/i;
22
+
23
+ function supportsOpenAIGAComputerUse(spec: ModelSpec<Api>): boolean {
24
+ if (spec.supportsComputerUse !== undefined) return spec.supportsComputerUse;
25
+ if (
26
+ spec.api !== "openai-responses" &&
27
+ spec.api !== "openai-codex-responses" &&
28
+ spec.api !== "azure-openai-responses"
29
+ ) {
30
+ return false;
31
+ }
32
+ if (spec.api !== "azure-openai-responses" && spec.provider !== "openai" && spec.provider !== "openai-codex") {
33
+ return false;
34
+ }
35
+ return OPENAI_GA_COMPUTER_MODEL_RE.test(spec.requestModelId ?? spec.id);
36
+ }
37
+
19
38
  export function buildModel<TApi extends Api>(spec: ModelSpec<TApi>): Model<TApi> {
20
39
  const compat = buildCompat(spec) as CompatOf<TApi>;
21
40
  return {
22
41
  ...spec,
23
42
  name: cleanModelName(spec.name),
24
43
  thinking: resolveModelThinking(spec, compat),
44
+ supportsComputerUse: supportsOpenAIGAComputerUse(spec),
25
45
  compat,
26
46
  compatConfig: spec.compat,
27
47
  } as Model<TApi>;
@@ -39,6 +59,8 @@ export function buildCompat(spec: ModelSpec<Api>): CompatOf<Api> {
39
59
  return buildOpenAIResponsesCompat(spec as ModelSpec<"openai-responses">);
40
60
  case "anthropic-messages":
41
61
  return buildAnthropicCompat(spec as ModelSpec<"anthropic-messages">);
62
+ case "bedrock-converse-stream":
63
+ return buildBedrockCompat(spec as ModelSpec<"bedrock-converse-stream">);
42
64
  case "devin-agent":
43
65
  return buildDevinCompat(spec as ModelSpec<"devin-agent">);
44
66
  default:
@@ -0,0 +1,114 @@
1
+ import type { ModelSpec, ResolvedBedrockCompat } from "../types";
2
+ import { applyCompatOverrides } from "./apply";
3
+
4
+ const NO_EXPLICIT_CHECKPOINTS: ResolvedBedrockCompat = {
5
+ promptCacheMode: "none",
6
+ supportsLongPromptCacheRetention: false,
7
+ promptCacheMinimumTokens: 0,
8
+ promptCacheMaximumCheckpoints: 0,
9
+ };
10
+ const EXPLICIT_CHECKPOINTS_1024_5M: ResolvedBedrockCompat = {
11
+ promptCacheMode: "explicit",
12
+ supportsLongPromptCacheRetention: false,
13
+ promptCacheMinimumTokens: 1024,
14
+ promptCacheMaximumCheckpoints: 4,
15
+ };
16
+
17
+ const EXPLICIT_CHECKPOINTS_1024_1H: ResolvedBedrockCompat = {
18
+ promptCacheMode: "explicit",
19
+ supportsLongPromptCacheRetention: true,
20
+ promptCacheMinimumTokens: 1024,
21
+ promptCacheMaximumCheckpoints: 4,
22
+ };
23
+
24
+ const EXPLICIT_CHECKPOINTS_2048_5M: ResolvedBedrockCompat = {
25
+ promptCacheMode: "explicit",
26
+ supportsLongPromptCacheRetention: false,
27
+ promptCacheMinimumTokens: 2048,
28
+ promptCacheMaximumCheckpoints: 4,
29
+ };
30
+
31
+ const EXPLICIT_CHECKPOINTS_4096_5M: ResolvedBedrockCompat = {
32
+ promptCacheMode: "explicit",
33
+ supportsLongPromptCacheRetention: false,
34
+ promptCacheMinimumTokens: 4096,
35
+ promptCacheMaximumCheckpoints: 4,
36
+ };
37
+
38
+ const EXPLICIT_CHECKPOINTS_4096_1H: ResolvedBedrockCompat = {
39
+ promptCacheMode: "explicit",
40
+ supportsLongPromptCacheRetention: true,
41
+ promptCacheMinimumTokens: 4096,
42
+ promptCacheMaximumCheckpoints: 4,
43
+ };
44
+
45
+ /**
46
+ * Explicit Nova cache points complement Bedrock's automatic prefix caching:
47
+ * AWS recommends them for consistent cache hits and input-cost savings. Keep
48
+ * these exact documented model and inference-profile IDs conservative rather
49
+ * than treating arbitrary Nova-like application profiles as checkpoint-capable.
50
+ */
51
+ function detectedBedrockCompat(modelId: string): ResolvedBedrockCompat {
52
+ const id = modelId.toLowerCase();
53
+
54
+ if (
55
+ id === "amazon.nova-lite-v1:0" ||
56
+ id === "us.amazon.nova-lite-v1:0" ||
57
+ id === "amazon.nova-micro-v1:0" ||
58
+ id === "us.amazon.nova-micro-v1:0" ||
59
+ id === "amazon.nova-pro-v1:0" ||
60
+ id === "us.amazon.nova-pro-v1:0" ||
61
+ id === "amazon.nova-premier-v1:0" ||
62
+ id === "us.amazon.nova-premier-v1:0" ||
63
+ id === "amazon.nova-2-lite-v1:0" ||
64
+ id === "us.amazon.nova-2-lite-v1:0" ||
65
+ id === "eu.amazon.nova-2-lite-v1:0" ||
66
+ id === "jp.amazon.nova-2-lite-v1:0" ||
67
+ id === "global.amazon.nova-2-lite-v1:0"
68
+ ) {
69
+ return EXPLICIT_CHECKPOINTS_1024_5M;
70
+ }
71
+
72
+ // https://docs.aws.amazon.com/bedrock/latest/userguide/prompt-caching.html
73
+ // This list is deliberately sourced from AWS model cards, not cache pricing:
74
+ // https://docs.aws.amazon.com/bedrock/latest/userguide/model-cards.html
75
+ if (
76
+ id.includes("anthropic.claude-opus-4-5") ||
77
+ id.includes("anthropic.claude-sonnet-4-5") ||
78
+ id.includes("anthropic.claude-haiku-4-5") ||
79
+ id.includes("anthropic.claude-opus-4-7") ||
80
+ id.includes("anthropic.claude-opus-4-8") ||
81
+ id.includes("anthropic.claude-sonnet-5")
82
+ ) {
83
+ return EXPLICIT_CHECKPOINTS_4096_1H;
84
+ }
85
+ if (id.includes("anthropic.claude-opus-4-6")) {
86
+ return EXPLICIT_CHECKPOINTS_4096_5M;
87
+ }
88
+ if (id.includes("anthropic.claude-3-5-haiku")) {
89
+ return EXPLICIT_CHECKPOINTS_2048_5M;
90
+ }
91
+ if (id.includes("anthropic.claude-fable-5")) {
92
+ return EXPLICIT_CHECKPOINTS_1024_1H;
93
+ }
94
+
95
+ if (
96
+ id.includes("anthropic.claude-opus-4-1") ||
97
+ id.includes("anthropic.claude-opus-4-20250514") ||
98
+ id.includes("anthropic.claude-sonnet-4-20250514") ||
99
+ id.includes("anthropic.claude-sonnet-4-6") ||
100
+ id.includes("anthropic.claude-3-7-sonnet") ||
101
+ id.includes("anthropic.claude-3-5-sonnet-20241022-v2")
102
+ ) {
103
+ return EXPLICIT_CHECKPOINTS_1024_5M;
104
+ }
105
+
106
+ return NO_EXPLICIT_CHECKPOINTS;
107
+ }
108
+
109
+ /** Resolve Bedrock Converse prompt-cache capabilities once per model. */
110
+ export function buildBedrockCompat(spec: ModelSpec<"bedrock-converse-stream">): ResolvedBedrockCompat {
111
+ const compat = { ...detectedBedrockCompat(spec.id) };
112
+ applyCompatOverrides(compat, spec.compat);
113
+ return compat;
114
+ }
@@ -9,6 +9,7 @@
9
9
  */
10
10
  import { isFireworksFastModelId } from "../fireworks-model-id";
11
11
  import { hostMatchesUrl, modelMatchesHost } from "../hosts";
12
+ import { bareModelId, parseOpenAIModel, semverGte } from "../identity/classify";
12
13
  import {
13
14
  isAnthropicNamespacedModelId,
14
15
  isClaudeModelId,
@@ -133,6 +134,17 @@ function isOfficialOpenAIEndpoint(provider: string, baseUrl: string): boolean {
133
134
  }
134
135
  }
135
136
 
137
+ /**
138
+ * Explicit prompt-cache breakpoints are a GPT-5.6+ first-party contract. Keep
139
+ * this intentionally narrow: compatible gateways and older OpenAI models
140
+ * reject the new request fields unless their catalog compat opts in.
141
+ */
142
+ function supportsOfficialOpenAIPromptCacheBreakpoints(provider: string, modelId: string, baseUrl: string): boolean {
143
+ if (!isOfficialOpenAIEndpoint(provider, baseUrl)) return false;
144
+ const model = parseOpenAIModel(bareModelId(modelId));
145
+ return model !== null && semverGte(model.version, "5.6");
146
+ }
147
+
136
148
  /**
137
149
  * OpenCode's gateways (https://opencode.ai/zen|go) gate `reasoning_content`
138
150
  * on the request's thinking state for every model they front (Kimi K2.x,
@@ -341,6 +353,7 @@ export function buildOpenAICompat(spec: ModelSpec<"openai-completions">): Resolv
341
353
  hostMatchesUrl(baseUrl, "fireworks") ||
342
354
  isDirectDeepseekApi;
343
355
 
356
+ const supportsPromptCacheBreakpoints = supportsOfficialOpenAIPromptCacheBreakpoints(provider, spec.id, baseUrl);
344
357
  // Hosts whose chat-completions endpoints are known to accept multiple
345
358
  // leading `system`/`developer` messages (preferred for KV-cache reuse).
346
359
  // Anything outside this allowlist defaults to coalescing because
@@ -546,6 +559,8 @@ export function buildOpenAICompat(spec: ModelSpec<"openai-completions">): Resolv
546
559
  (thinkingFormat === "qwen" || thinkingFormat === "qwen-chat-template") && isLocalOpenAICompatBackend,
547
560
  requiresAssistantContentForToolCalls: isKimiModel || isDirectDeepseekReasoning,
548
561
  cacheControlFormat: isOpenRouter && spec.id.startsWith("anthropic/") ? "anthropic" : undefined,
562
+ supportsPromptCacheBreakpoints,
563
+ promptCacheBreakpointTtl: supportsPromptCacheBreakpoints ? "30m" : undefined,
549
564
  openRouterRouting: undefined,
550
565
  vercelGatewayRouting: undefined,
551
566
  isOpenRouterHost: isOpenRouter,
@@ -623,7 +638,9 @@ export function buildOpenAIResponsesCompat(spec: OpenAIResponsesSpecLike): Resol
623
638
  const isAzure = modelMatchesHost({ provider: spec.provider, baseUrl }, "azureOpenAI");
624
639
  const isOpenRouter = modelMatchesHost({ provider: spec.provider, baseUrl }, "openrouter");
625
640
  const isOpenAIUrl = hostMatchesUrl(baseUrl, "openai");
641
+ const isVercelGateway = modelMatchesHost({ provider: spec.provider, baseUrl }, "vercelAIGateway");
626
642
  const id = spec.id ?? "";
643
+ const supportsPromptCacheBreakpoints = supportsOfficialOpenAIPromptCacheBreakpoints(spec.provider, id, baseUrl);
627
644
  const thinkingFormat: ResolvedOpenAISharedCompat["thinkingFormat"] = isOpenRouter ? "openrouter" : "openai";
628
645
  const isKimiModel = id ? isKimiModelId(id) : false;
629
646
  const isAnthropicModel = id ? isClaudeModelId(id) || isAnthropicNamespacedModelId(id) : false;
@@ -642,6 +659,8 @@ export function buildOpenAIResponsesCompat(spec: OpenAIResponsesSpecLike): Resol
642
659
  supportsStrictMode: isAzure || detectStrictModeSupport(spec.provider, baseUrl),
643
660
  supportsReasoningEffort: spec.provider !== "xai-oauth" || isGrokReasoningEffortCapable(id),
644
661
  supportsLongPromptCacheRetention: isOpenAIUrl,
662
+ supportsPromptCacheBreakpoints,
663
+ promptCacheBreakpointTtl: supportsPromptCacheBreakpoints ? "30m" : undefined,
645
664
  // Azure OpenAI and GitHub Copilot Responses paths require tool results
646
665
  // to strictly match prior tool calls when building Responses inputs.
647
666
  strictResponsesPairing: isAzure || spec.provider === "github-copilot",
@@ -686,7 +705,9 @@ export function buildOpenAIResponsesCompat(spec: OpenAIResponsesSpecLike): Resol
686
705
  requiresAssistantAfterToolResult: false,
687
706
  requiresAssistantContentForToolCalls: isKimiModel,
688
707
  openRouterRouting: undefined,
708
+ vercelGatewayRouting: undefined,
689
709
  isOpenRouterHost: isOpenRouter,
710
+ isVercelGatewayHost: isVercelGateway,
690
711
  wireModelIdMode: isOpenRouter ? "openrouter" : "raw",
691
712
  // Mirrors buildOpenAICompat: Kimi behind a Responses-capable proxy still
692
713
  // lands on Moonshot's MFJS validator.
@@ -724,6 +745,7 @@ function pickResponsesOnly(compat: ResolvedOpenAIResponsesCompat): ResponsesOnly
724
745
  strictResponsesPairing: compat.strictResponsesPairing,
725
746
  supportsImageDetailOriginal: compat.supportsImageDetailOriginal,
726
747
  supportsObfuscationOptOut: compat.supportsObfuscationOptOut,
748
+ isVercelGatewayHost: compat.isVercelGatewayHost,
727
749
  } satisfies ResponsesOnlyCompat;
728
750
  }
729
751
 
@@ -8,11 +8,11 @@ const DEFAULT_MODEL_LIST_PATHS = ["/codex/models", "/models"] as const;
8
8
  const DEFAULT_CONTEXT_WINDOW = 272_000;
9
9
  const DEFAULT_MAX_TOKENS = 128_000;
10
10
  /**
11
- * GPT-5.6 luna/sol/terra hard context capacity. OpenAI's Codex model registry
12
- * declares context_window = max_context_window = 372000 (#5705), but Codex
13
- * discovery under-reports it omitting the field for some accounts and
14
- * actively returning 272000 for others (#6259). Applied as a floor for these
15
- * SKUs so the reported/absent value never regresses the real window.
11
+ * GPT-5.6 luna/sol/terra hard context capacity. Codex discovery omits
12
+ * `context_window` for these SKUs, so the generic {@link DEFAULT_CONTEXT_WINDOW}
13
+ * (272000) would understate the real window OpenAI's Codex model registry
14
+ * declares context_window = max_context_window = 372000 (#5705). Used as the
15
+ * fallback only when upstream reports no value.
16
16
  */
17
17
  const GPT_5_6_CONTEXT_WINDOW = 372_000;
18
18
  const CODEX_REMOTE_COMPACTION = {
@@ -223,18 +223,14 @@ function normalizeCodexModelEntry(entry: unknown, baseUrl: string): NormalizedCo
223
223
  }
224
224
 
225
225
  const name = toNonEmptyString(payload.display_name) ?? slug;
226
- // GPT-5.6 luna/sol/terra have a 372000 hard window, but Codex discovery
227
- // under-reports it: for some accounts the field is omitted, for others it is
228
- // actively returned as 272000 (#6259). Treat GPT_5_6_CONTEXT_WINDOW as a
229
- // floor for these SKUs so neither the omission nor the active under-report
230
- // regresses the real capacity; other models honor the reported value with
231
- // the generic 272000 fallback.
226
+ // Codex discovery omits `context_window` for GPT-5.6 luna/sol/terra; the
227
+ // generic 272000 fallback understates their real 372000 window (#5705).
232
228
  const parsed = parseKnownModel(slug);
233
- const isGpt56 = parsed.family === "openai" && semverEqual(parsed.version, "5.6");
234
- const reportedContextWindow = toPositiveInt(payload.context_window);
235
- const contextWindow = isGpt56
236
- ? Math.max(GPT_5_6_CONTEXT_WINDOW, reportedContextWindow ?? 0)
237
- : (reportedContextWindow ?? DEFAULT_CONTEXT_WINDOW);
229
+ const fallbackContextWindow =
230
+ parsed.family === "openai" && semverEqual(parsed.version, "5.6")
231
+ ? GPT_5_6_CONTEXT_WINDOW
232
+ : DEFAULT_CONTEXT_WINDOW;
233
+ const contextWindow = toPositiveInt(payload.context_window) ?? fallbackContextWindow;
238
234
  const maxTokens = Math.min(DEFAULT_MAX_TOKENS, contextWindow);
239
235
  const reasoning = supportsReasoning(payload.default_reasoning_level, payload.supported_reasoning_levels);
240
236
  const input = normalizeInputModalities(payload.input_modalities);
package/src/hosts.ts CHANGED
@@ -41,7 +41,10 @@ export const KNOWN_HOSTS = {
41
41
  zai: { providers: ["zai"], urlMarkers: ["api.z.ai"] },
42
42
  zhipu: { providers: ["zhipu-coding-plan"], urlMarkers: ["open.bigmodel.cn"] },
43
43
  kilo: { providers: ["kilo"], urlMarkers: ["api.kilo.ai"] },
44
- alibabaDashscope: { providers: ["alibaba-coding-plan"], urlMarkers: ["dashscope"] },
44
+ alibabaDashscope: {
45
+ providers: ["alibaba-coding-plan", "alibaba-token-plan"],
46
+ urlMarkers: ["dashscope", "token-plan."],
47
+ },
45
48
  umans: { providers: ["umans"], urlMarkers: ["api.code.umans.ai"] },
46
49
  xiaomi: { providers: ["xiaomi"], providerPrefixes: ["xiaomi-token-plan-"], urlMarkers: ["xiaomimimo.com"] },
47
50
  xai: { providers: ["xai"], urlMarkers: ["api.x.ai"] },
@@ -10,6 +10,7 @@ const DEFAULT_MODEL_PROVIDER_ORDER = [
10
10
  "kimi-code",
11
11
  "moonshot",
12
12
  "qwen-portal",
13
+ "alibaba-token-plan",
13
14
  "zai",
14
15
  "xai-oauth",
15
16
  "xai",