@oh-my-pi/pi-catalog 17.1.3 → 17.1.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/CHANGELOG.md +20 -0
- package/dist/types/compat/anthropic.d.ts +7 -0
- package/dist/types/identity/family.d.ts +9 -0
- package/dist/types/model-cache.d.ts +1 -1
- package/dist/types/model-manager.d.ts +10 -1
- package/dist/types/provider-models/bundled-references.d.ts +3 -1
- package/dist/types/provider-models/cache-provider-id.d.ts +7 -0
- package/dist/types/provider-models/index.d.ts +1 -0
- package/dist/types/provider-models/openai-compat.d.ts +18 -2
- package/dist/types/types.d.ts +17 -12
- package/dist/types/wire/alibaba-token-plan.d.ts +17 -1
- package/package.json +3 -3
- package/src/build.ts +34 -9
- package/src/compat/anthropic.ts +37 -9
- package/src/compat/bedrock.ts +12 -0
- package/src/identity/family.ts +13 -0
- package/src/model-cache.ts +17 -7
- package/src/model-manager.ts +42 -13
- package/src/models.json +6877 -2135
- package/src/models.ts +15 -15
- package/src/provider-models/bundled-references.ts +28 -11
- package/src/provider-models/cache-provider-id.ts +47 -0
- package/src/provider-models/index.ts +1 -0
- package/src/provider-models/ollama.ts +6 -4
- package/src/provider-models/openai-compat.ts +84 -30
- package/src/provider-models/special.ts +2 -3
- package/src/types.ts +18 -12
- package/src/wire/alibaba-token-plan.ts +31 -4
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,26 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [17.1.5] - 2026-07-27
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- Fixed Kimi Code (`kimi-code`) reporting `maxTokens: 32000` for every model — its `/coding/v1/models` discovery mapper and the bundled catalog applied a blanket constant, truncating `k3`/`k3-256k` output at ~4x below their real 131072 ceiling and `kimi-for-coding`/`kimi-for-coding-highspeed` below their 32768 ceiling. Output caps are now derived per family, and the model cache is invalidated so upgrades drop the stale `maxTokens: 32000` rows (including the discovery-only `k3-256k`) instead of serving them until the next network refresh ([#6711](https://github.com/can1357/oh-my-pi/issues/6711)).
|
|
10
|
+
- Fixed Anthropic model discovery 404ing when the registry derived the provider base URL from a bundled model without the `/v1` suffix (`https://api.anthropic.com/models` instead of `/v1/models`), which let a stale text-only cache row shadow fresh models.dev vision metadata — surfacing as snapcompact refusing to run on `claude-opus-5`. Discovery now always targets `/v1/models` while model rows keep the provider base URL ([#6563](https://github.com/can1357/oh-my-pi/issues/6563)).
|
|
11
|
+
|
|
12
|
+
## [17.1.4] - 2026-07-26
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- Added Claude Opus 5 model entries for Amazon Bedrock: `anthropic.claude-opus-5` plus its `us.`, `eu.`, `au.`, and `global.` regional/geo IDs.
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
|
|
20
|
+
- Fixed `alibaba-token-plan` locking out China (Beijing) 百炼 Token Plan subscribers: the provider hardcoded the international Singapore endpoint, so Beijing-issued `sk-sp-` keys got `401 invalid_api_key`. The wire credential now carries an optional region base URL, and model discovery targets the credential's region ([#6682](https://github.com/can1357/oh-my-pi/issues/6682)).
|
|
21
|
+
- Fixed forced `tool_choice` 400s (`tool_choice 'specified' is incompatible with thinking enabled`) on Kimi Code's Anthropic-compatible endpoint for the `kimi-for-coding`, `kimi-for-coding-highspeed`, and `k3` aliases: the Anthropic-surface compat matcher only recognised Moonshot's native `kimi-k2.7-code*` ids, so thinking-locked kimi-code models kept `supportsForcedToolChoice: true` and the forced selector was sent to a host that always thinks. These models now resolve `requiresThinkingEnabled`, keeping thinking on and downgrading forced choices to `auto`.
|
|
22
|
+
- Retried empty successful provider discovery responses after the short non-authoritative interval instead of caching them for the full catalog TTL ([#6620](https://github.com/can1357/oh-my-pi/issues/6620)).
|
|
23
|
+
- Fixed GitHub Copilot Claude models with no bundled catalog reference (e.g. a freshly served `claude-opus-5`) discovering with `reasoning: false`/`thinking: null` and no effort dial, and disappearing along with their synthesized `-1m` sibling on offline reads: reference-less Copilot models on the anthropic-messages proxy now derive the adaptive reasoning ladder from the model id, and the cache restores their compile-time `COPILOT_API_HEADERS` by value instead of dropping them as unrestorable ([#6664](https://github.com/can1357/oh-my-pi/issues/6664)).
|
|
24
|
+
|
|
5
25
|
## [17.1.3] - 2026-07-24
|
|
6
26
|
|
|
7
27
|
### Fixed
|
|
@@ -7,5 +7,12 @@ import type { ModelSpec, ResolvedAnthropicCompat } from "../types.js";
|
|
|
7
7
|
* prefix check would accept lookalikes like `https://api.anthropic.com.evil.com`.
|
|
8
8
|
*/
|
|
9
9
|
export declare function isOfficialAnthropicApiUrl(baseUrl?: string): boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Known non-official URLs that enforce Anthropic thinking signatures on replay.
|
|
12
|
+
*
|
|
13
|
+
* Runtime routing calls this with the effective URL because a model's resolved
|
|
14
|
+
* compat can be stale after Foundry or a provider base-URL override reroutes it.
|
|
15
|
+
*/
|
|
16
|
+
export declare function isAnthropicSigningProxyUrl(baseUrl?: string): boolean;
|
|
10
17
|
/** Build the resolved anthropic-messages compat record for a model spec. */
|
|
11
18
|
export declare function buildAnthropicCompat(spec: ModelSpec<"anthropic-messages">): ResolvedAnthropicCompat;
|
|
@@ -119,6 +119,15 @@ export declare const isGlmVisionModelId: (modelId: string) => boolean;
|
|
|
119
119
|
* collapsed onto the single vendor token; use {@link parseKnownModel} for finer breakdowns.
|
|
120
120
|
*/
|
|
121
121
|
export declare const modelFamilyToken: (modelId: string) => string;
|
|
122
|
+
/**
|
|
123
|
+
* True for Claude generations that support extended thinking: Sonnet/Opus 3.7+,
|
|
124
|
+
* every 4.x/5+ Opus/Sonnet, and the Fable/Mythos generation. Pre-thinking
|
|
125
|
+
* models (Claude 3.5 and older) are excluded so no thinking effort dial is
|
|
126
|
+
* fabricated for a model that rejects thinking parameters. Classifier-based, so
|
|
127
|
+
* dotted and dashed version forms both match; ids the classifier does not parse
|
|
128
|
+
* (e.g. Haiku, bare dated ids) return false.
|
|
129
|
+
*/
|
|
130
|
+
export declare const anthropicModelSupportsThinking: (modelId: string) => boolean;
|
|
122
131
|
/**
|
|
123
132
|
* Adaptive thinking `display` is supported starting with Claude Opus 4.7+,
|
|
124
133
|
* Sonnet 5+, and the Claude Fable/Mythos 5 generation. Older adaptive-thinking
|
|
@@ -19,5 +19,5 @@ interface CacheEntry<TApi extends Api = Api> {
|
|
|
19
19
|
staticFingerprint: string;
|
|
20
20
|
}
|
|
21
21
|
export declare function readModelCache<TApi extends Api>(providerId: string, ttlMs: number, now: () => number, dbPath?: string): CacheEntry<TApi> | null;
|
|
22
|
-
export declare function writeModelCache<TApi extends Api>(providerId: string, updatedAt: number, models: Model<TApi>[], authoritative: boolean, staticFingerprint: string, dbPath?: string, staticHeaderSources?: readonly Model<TApi>[]): void;
|
|
22
|
+
export declare function writeModelCache<TApi extends Api>(providerId: string, updatedAt: number, models: Model<TApi>[], authoritative: boolean, staticFingerprint: string, dbPath?: string, staticHeaderSources?: readonly Model<TApi>[], restorableHeaderFallback?: Record<string, string>): void;
|
|
23
23
|
export {};
|
|
@@ -30,6 +30,14 @@ export interface ModelManagerOptions<TApi extends Api = Api, TModelsDevPayload =
|
|
|
30
30
|
dynamicModelsAuthoritative?: boolean;
|
|
31
31
|
/** Cached model ids to ignore when the cache was written against a different static catalog fingerprint. */
|
|
32
32
|
dropCachedModelIdsOnStaticMismatch?: readonly string[];
|
|
33
|
+
/**
|
|
34
|
+
* Trusted, provider-wide request headers (compile-time constants, never
|
|
35
|
+
* credentials) that the cache may restore by value for any model whose live
|
|
36
|
+
* headers matched them at write time. Lets header-bearing dynamic models
|
|
37
|
+
* without a bundled static entry survive offline reads instead of being
|
|
38
|
+
* dropped as unrestorable (e.g. GitHub Copilot's User-Agent + API version).
|
|
39
|
+
*/
|
|
40
|
+
restorableHeaderFallback?: Record<string, string>;
|
|
33
41
|
/** Optional dynamic endpoint fetcher. */
|
|
34
42
|
fetchDynamicModels?: () => Promise<readonly ModelSpec<TApi>[] | null>;
|
|
35
43
|
/** Optional models.dev fallback hook. */
|
|
@@ -41,7 +49,8 @@ export interface ModelManagerOptions<TApi extends Api = Api, TModelsDevPayload =
|
|
|
41
49
|
* Resolution result.
|
|
42
50
|
*
|
|
43
51
|
* `stale` is false when the resolved catalog is authoritative for the selected provider:
|
|
44
|
-
* - dynamic endpoint
|
|
52
|
+
* - a dynamic endpoint fetch succeeded in this call (an empty catalog is still
|
|
53
|
+
* authoritative for the cycle, so downstream pruning of removed models runs),
|
|
45
54
|
* - a still-fresh authoritative cache was reused in `online-if-uncached` mode, or
|
|
46
55
|
* - the provider has no dynamic fetcher configured.
|
|
47
56
|
*/
|
|
@@ -8,4 +8,6 @@ import type { Api, Model, ModelSpec } from "../types.js";
|
|
|
8
8
|
*/
|
|
9
9
|
export declare function toModelSpec<TApi extends Api>(model: Model<TApi>): ModelSpec<TApi>;
|
|
10
10
|
export declare function createBundledReferenceMap<TApi extends Api>(provider: Parameters<typeof getBundledModels>[0]): Map<string, ModelSpec<TApi>>;
|
|
11
|
-
|
|
11
|
+
type ProviderReferenceSource<TApi extends Api> = Map<string, ModelSpec<TApi>> | (() => Map<string, ModelSpec<TApi>>);
|
|
12
|
+
export declare function createReferenceResolver<TApi extends Api>(providerReferenceSource: ProviderReferenceSource<TApi>): (modelId: string) => ModelSpec<TApi> | undefined;
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export interface ModelCacheProviderIdOptions {
|
|
2
|
+
apiKey?: string;
|
|
3
|
+
baseUrl?: string;
|
|
4
|
+
}
|
|
5
|
+
export declare function getDefaultModelDiscoveryBaseUrl(providerId: string): string | undefined;
|
|
6
|
+
/** Resolve the cache namespace used by a provider's model-manager options without constructing those options. */
|
|
7
|
+
export declare function resolveModelCacheProviderId(providerId: string, options?: ModelCacheProviderIdOptions): string;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { ModelManagerOptions } from "../model-manager.js";
|
|
2
2
|
import { getBundledModels } from "../models.js";
|
|
3
3
|
import type { Api, FetchImpl, ModelSpec, Provider } from "../types.js";
|
|
4
|
+
import { ALIBABA_TOKEN_PLAN_BASE_URL } from "../wire/alibaba-token-plan.js";
|
|
4
5
|
export interface ModelsDevModel {
|
|
5
6
|
id?: string;
|
|
6
7
|
name?: string;
|
|
@@ -299,7 +300,7 @@ export interface AlibabaCodingPlanModelManagerConfig {
|
|
|
299
300
|
fetch?: FetchImpl;
|
|
300
301
|
}
|
|
301
302
|
export declare function alibabaCodingPlanModelManagerOptions(config?: AlibabaCodingPlanModelManagerConfig): ModelManagerOptions<"openai-completions">;
|
|
302
|
-
export
|
|
303
|
+
export { ALIBABA_TOKEN_PLAN_BASE_URL };
|
|
303
304
|
export declare const ALIBABA_TOKEN_PLAN_STATIC_MODELS: readonly ModelSpec<"openai-completions">[];
|
|
304
305
|
export interface AlibabaTokenPlanModelManagerConfig {
|
|
305
306
|
apiKey?: string;
|
|
@@ -318,6 +319,22 @@ export interface KimiCodeModelManagerConfig {
|
|
|
318
319
|
baseUrl?: string;
|
|
319
320
|
fetch?: FetchImpl;
|
|
320
321
|
}
|
|
322
|
+
/**
|
|
323
|
+
* Kimi Code output ceilings by model family. The `/coding/v1/models` discovery
|
|
324
|
+
* envelope carries no output-limit field, so the mapper supplies the documented
|
|
325
|
+
* per-family caps instead of a blanket constant. Values match models.dev's
|
|
326
|
+
* `kimi-for-coding` and `moonshotai` kimi-k3 entries. See #6711.
|
|
327
|
+
*/
|
|
328
|
+
export declare const KIMI_CODE_K3_MAX_TOKENS = 131072;
|
|
329
|
+
export declare const KIMI_CODE_FOR_CODING_MAX_TOKENS = 32768;
|
|
330
|
+
/** Fallback output cap for Kimi Code families without a documented ceiling (legacy K2 discovery rows). */
|
|
331
|
+
export declare const KIMI_CODE_DEFAULT_MAX_TOKENS = 32000;
|
|
332
|
+
/**
|
|
333
|
+
* Resolve a Kimi Code model's output ceiling from its id: `k3` / `k3-256k` ->
|
|
334
|
+
* 131072, `kimi-for-coding[-highspeed]` -> 32768, everything else -> `fallback`.
|
|
335
|
+
*/
|
|
336
|
+
export declare function kimiCodeMaxTokens(modelId: string, fallback?: number): number;
|
|
337
|
+
export declare function kimiCodeMaxTokens(modelId: string, fallback: number | null): number | null;
|
|
321
338
|
export declare function kimiCodeModelManagerOptions(config?: KimiCodeModelManagerConfig): ModelManagerOptions<"openai-completions">;
|
|
322
339
|
/** Native LM Studio metadata keyed by model id from `/api/v0/models`. */
|
|
323
340
|
export interface LmStudioNativeModelMetadata {
|
|
@@ -506,4 +523,3 @@ export interface ModelsDevProviderDescriptor {
|
|
|
506
523
|
export declare function mapModelsDevToModels(data: Record<string, unknown>, descriptors: readonly ModelsDevProviderDescriptor[]): ModelSpec<Api>[];
|
|
507
524
|
/** All provider descriptors for models.dev data mapping in generate-models.ts. */
|
|
508
525
|
export declare const MODELS_DEV_PROVIDER_DESCRIPTORS: readonly ModelsDevProviderDescriptor[];
|
|
509
|
-
export {};
|
package/dist/types/types.d.ts
CHANGED
|
@@ -432,6 +432,20 @@ export interface AnthropicCompat {
|
|
|
432
432
|
* blocks instead of normal `tool_use` calls.
|
|
433
433
|
*/
|
|
434
434
|
escapeBuiltinToolNames?: boolean;
|
|
435
|
+
/**
|
|
436
|
+
* The configured endpoint enforces Anthropic's signature protocol on
|
|
437
|
+
* replayed thinking blocks — either the official API itself or a proxy
|
|
438
|
+
* that forwards to it (GitHub Copilot, ZenMux, Cloudflare AI Gateway's
|
|
439
|
+
* `/anthropic` route, Google Vertex's `publishers/anthropic/…`).
|
|
440
|
+
* Downstream transforms strip stale cross-model thinking signatures on
|
|
441
|
+
* these endpoints so the signing proxy doesn't 400 with
|
|
442
|
+
* `Invalid signature in thinking block` (#4297), and adaptive-thinking
|
|
443
|
+
* models keep the interleaved-thinking beta on them (#6717). Known hosts
|
|
444
|
+
* are auto-detected from provider id and baseUrl; set this to mark an
|
|
445
|
+
* opaque signing proxy the URL list can't recognize. Superset of
|
|
446
|
+
* {@link ResolvedAnthropicCompat.officialEndpoint}.
|
|
447
|
+
*/
|
|
448
|
+
signingEndpoint?: boolean;
|
|
435
449
|
}
|
|
436
450
|
/**
|
|
437
451
|
* Compatibility settings for Bedrock Converse prompt caching. Cache pricing is
|
|
@@ -597,17 +611,6 @@ export type ResolvedAnthropicCompat = Required<AnthropicCompat> & {
|
|
|
597
611
|
* env headers, and cache-TTL shaping without per-request URL parsing.
|
|
598
612
|
*/
|
|
599
613
|
officialEndpoint: boolean;
|
|
600
|
-
/**
|
|
601
|
-
* The configured endpoint enforces Anthropic's signature protocol on
|
|
602
|
-
* replayed thinking blocks — either the official API itself or a proxy
|
|
603
|
-
* that forwards to it (GitHub Copilot, ZenMux, Cloudflare AI Gateway's
|
|
604
|
-
* `/anthropic` route, Google Vertex's `publishers/anthropic/…`).
|
|
605
|
-
* Downstream transforms strip stale cross-model thinking signatures on
|
|
606
|
-
* these endpoints so the signing proxy doesn't 400 with
|
|
607
|
-
* `Invalid signature in thinking block` (#4297). Superset of
|
|
608
|
-
* {@link officialEndpoint}.
|
|
609
|
-
*/
|
|
610
|
-
signingEndpoint: boolean;
|
|
611
614
|
};
|
|
612
615
|
/**
|
|
613
616
|
* Compatibility settings for the devin-agent (Codeium Cascade) API. Cascade
|
|
@@ -686,6 +689,8 @@ export interface Model<TApi extends Api = Api> {
|
|
|
686
689
|
supportsTools?: boolean;
|
|
687
690
|
/** Whether this model accepts the GA OpenAI Responses `{ type: "computer" }` native tool. */
|
|
688
691
|
supportsComputerUse?: boolean;
|
|
692
|
+
/** Verbatim explicit computer-use support from the spec; undefined when `buildModel` inferred the runtime value. */
|
|
693
|
+
supportsComputerUseConfig?: boolean;
|
|
689
694
|
/** GitLab Duo Workflow root namespace selected during catalog discovery. */
|
|
690
695
|
gitlabDuoWorkflowRootNamespaceId?: string;
|
|
691
696
|
/** Cursor `max_mode` request flag returned by `GetUsableModels` for premium models that require max mode. */
|
|
@@ -770,7 +775,7 @@ export interface Model<TApi extends Api = Api> {
|
|
|
770
775
|
* vocabulary of `buildModel`. Identical to `Model` except `compat` carries the
|
|
771
776
|
* sparse override shape and nothing is resolved yet.
|
|
772
777
|
*/
|
|
773
|
-
export interface ModelSpec<TApi extends Api = Api> extends Omit<Model<TApi>, "compat" | "compatConfig"> {
|
|
778
|
+
export interface ModelSpec<TApi extends Api = Api> extends Omit<Model<TApi>, "compat" | "compatConfig" | "supportsComputerUseConfig"> {
|
|
774
779
|
/** Sparse compatibility overrides; resolved into `Model.compat` by `buildModel`. */
|
|
775
780
|
compat?: CompatConfigOf<TApi>;
|
|
776
781
|
}
|
|
@@ -1,6 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* International (Singapore) Token Plan endpoint. Default region; keys issued by
|
|
3
|
+
* the international product authenticate only here.
|
|
4
|
+
*/
|
|
5
|
+
export declare const ALIBABA_TOKEN_PLAN_BASE_URL = "https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1";
|
|
6
|
+
/**
|
|
7
|
+
* China (Beijing) Token Plan endpoint (百炼 Token Plan). Keys are region-locked:
|
|
8
|
+
* a Beijing-issued key is rejected by the international endpoint with
|
|
9
|
+
* `invalid_api_key`, and vice versa (#6682).
|
|
10
|
+
*/
|
|
11
|
+
export declare const ALIBABA_TOKEN_PLAN_CN_BASE_URL = "https://token-plan.cn-beijing.maas.aliyuncs.com/compatible-mode/v1";
|
|
1
12
|
export interface AlibabaTokenPlanCredential {
|
|
2
13
|
token: string;
|
|
3
14
|
cookie?: string;
|
|
15
|
+
/**
|
|
16
|
+
* Region base URL the key authenticates against. Absent means the default
|
|
17
|
+
* international endpoint ({@link ALIBABA_TOKEN_PLAN_BASE_URL}).
|
|
18
|
+
*/
|
|
19
|
+
baseUrl?: string;
|
|
4
20
|
}
|
|
5
21
|
export declare function parseAlibabaTokenPlanCredential(value: string): AlibabaTokenPlanCredential | null;
|
|
6
|
-
export declare function serializeAlibabaTokenPlanCredential(token: string, cookie: string): string;
|
|
22
|
+
export declare function serializeAlibabaTokenPlanCredential(token: string, cookie: string, baseUrl?: string): string;
|
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.1.
|
|
4
|
+
"version": "17.1.5",
|
|
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.1.
|
|
37
|
+
"@oh-my-pi/pi-utils": "17.1.5",
|
|
38
38
|
"arktype": "2.2.3",
|
|
39
39
|
"zod": "^4"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
|
-
"@oh-my-pi/pi-ai": "17.1.
|
|
42
|
+
"@oh-my-pi/pi-ai": "17.1.5",
|
|
43
43
|
"@types/bun": "^1.3.14"
|
|
44
44
|
},
|
|
45
45
|
"engines": {
|
package/src/build.ts
CHANGED
|
@@ -20,28 +20,53 @@ import { cleanModelName } from "./utils";
|
|
|
20
20
|
|
|
21
21
|
const OPENAI_GA_COMPUTER_MODEL_RE = /^gpt-5\.(?:[4-9]|[1-9]\d)(?:[.-]|$)/i;
|
|
22
22
|
|
|
23
|
-
function
|
|
24
|
-
if (spec.
|
|
25
|
-
|
|
26
|
-
spec.
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
23
|
+
function isDirectOpenAIResponsesEndpoint(spec: ModelSpec<Api>): boolean {
|
|
24
|
+
if (spec.api === "openai-responses") {
|
|
25
|
+
if (spec.provider !== "openai") return false;
|
|
26
|
+
if (!spec.baseUrl) return true;
|
|
27
|
+
try {
|
|
28
|
+
const url = new URL(spec.baseUrl);
|
|
29
|
+
return url.protocol === "https:" && url.hostname === "api.openai.com";
|
|
30
|
+
} catch {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
if (spec.api !== "azure-openai-responses" || (spec.provider !== "azure" && spec.provider !== "azure-openai")) {
|
|
30
35
|
return false;
|
|
31
36
|
}
|
|
32
|
-
if (spec.
|
|
37
|
+
if (!spec.baseUrl) return true;
|
|
38
|
+
try {
|
|
39
|
+
const url = new URL(spec.baseUrl);
|
|
40
|
+
return (
|
|
41
|
+
url.protocol === "https:" &&
|
|
42
|
+
(url.hostname.endsWith(".openai.azure.com") || url.hostname === "models.inference.ai.azure.com")
|
|
43
|
+
);
|
|
44
|
+
} catch {
|
|
33
45
|
return false;
|
|
34
46
|
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function explicitComputerUseConfig(spec: ModelSpec<Api>): boolean | undefined {
|
|
50
|
+
return "supportsComputerUseConfig" in spec
|
|
51
|
+
? (spec as Model<Api>).supportsComputerUseConfig
|
|
52
|
+
: spec.supportsComputerUse;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function supportsOpenAIGAComputerUse(spec: ModelSpec<Api>, explicitSupport: boolean | undefined): boolean {
|
|
56
|
+
if (explicitSupport !== undefined) return explicitSupport;
|
|
57
|
+
if (!isDirectOpenAIResponsesEndpoint(spec)) return false;
|
|
35
58
|
return OPENAI_GA_COMPUTER_MODEL_RE.test(spec.requestModelId ?? spec.id);
|
|
36
59
|
}
|
|
37
60
|
|
|
38
61
|
export function buildModel<TApi extends Api>(spec: ModelSpec<TApi>): Model<TApi> {
|
|
39
62
|
const compat = buildCompat(spec) as CompatOf<TApi>;
|
|
63
|
+
const supportsComputerUseConfig = explicitComputerUseConfig(spec);
|
|
40
64
|
return {
|
|
41
65
|
...spec,
|
|
42
66
|
name: cleanModelName(spec.name),
|
|
43
67
|
thinking: resolveModelThinking(spec, compat),
|
|
44
|
-
supportsComputerUse: supportsOpenAIGAComputerUse(spec),
|
|
68
|
+
supportsComputerUse: supportsOpenAIGAComputerUse(spec, supportsComputerUseConfig),
|
|
69
|
+
supportsComputerUseConfig,
|
|
45
70
|
compat,
|
|
46
71
|
compatConfig: spec.compat,
|
|
47
72
|
} as Model<TApi>;
|
package/src/compat/anthropic.ts
CHANGED
|
@@ -4,10 +4,11 @@
|
|
|
4
4
|
* defaults come from provider ids, strict host checks, and model-id
|
|
5
5
|
* classification, with explicit spec overrides assigned on top.
|
|
6
6
|
*/
|
|
7
|
-
import { modelMatchesHost } from "../hosts";
|
|
7
|
+
import { hostMatchesUrl, modelMatchesHost } from "../hosts";
|
|
8
8
|
import {
|
|
9
9
|
hasOpus47ApiRestrictions,
|
|
10
10
|
isAnthropicFableOrMythosModel,
|
|
11
|
+
isKimiK3ModelId,
|
|
11
12
|
supportsMidConversationSystemMessages,
|
|
12
13
|
} from "../identity/family";
|
|
13
14
|
import type { ModelSpec, ResolvedAnthropicCompat } from "../types";
|
|
@@ -28,12 +29,25 @@ export function isOfficialAnthropicApiUrl(baseUrl?: string): boolean {
|
|
|
28
29
|
return lower === OFFICIAL_ANTHROPIC_URL || lower.startsWith(`${OFFICIAL_ANTHROPIC_URL}/`);
|
|
29
30
|
}
|
|
30
31
|
|
|
31
|
-
/**
|
|
32
|
+
/**
|
|
33
|
+
* Kimi models whose native endpoints (api.kimi.com, api.moonshot.ai) keep
|
|
34
|
+
* thinking enabled server-side no matter what the request carries: they
|
|
35
|
+
* reject disabled thinking (#3852) AND reject forced tool_choice
|
|
36
|
+
* (`tool_choice 'specified' is incompatible with thinking enabled`), so compat
|
|
37
|
+
* must keep thinking on and downgrade forced choices to `auto`. Covers the
|
|
38
|
+
* K2.7 Code family — public id, its Highspeed variant, and the kimi-code
|
|
39
|
+
* `kimi-for-coding[-highspeed]` aliases — plus K3 (`kimi-k3`, bare `k3` on
|
|
40
|
+
* kimi-code). Broader than the `compat/openai.ts` mirror: the OpenAI surface
|
|
41
|
+
* has its own per-dialect policies (K3 forced-choice guard, explicit disable
|
|
42
|
+
* shapes), while this surface has no working way to force a tool.
|
|
43
|
+
* Native-only host gating is the caller's responsibility.
|
|
44
|
+
*/
|
|
32
45
|
const KIMI_K27_CODE_MODEL_PATTERN = /(?:^|\/)kimi[-._]?k2(?:[._-]?|p)7[-._]?code(?:[-._]?highspeed)?$/i;
|
|
33
46
|
|
|
34
|
-
function
|
|
47
|
+
function matchesKimiMandatoryThinkingModel(spec: ModelSpec<"anthropic-messages">): boolean {
|
|
35
48
|
if (KIMI_K27_CODE_MODEL_PATTERN.test(spec.id)) return true;
|
|
36
|
-
|
|
49
|
+
if (spec.id === "kimi-for-coding" || spec.id === "kimi-for-coding-highspeed") return true;
|
|
50
|
+
return isKimiK3ModelId(spec.id) || spec.id === "k3";
|
|
37
51
|
}
|
|
38
52
|
|
|
39
53
|
const CLOUDFLARE_ANTHROPIC_GATEWAY_URL_MARKER = /gateway\.ai\.cloudflare\.com\/.+\/anthropic(?:\/|$)/i;
|
|
@@ -82,6 +96,23 @@ function isAzureAnthropicRoute(baseUrl?: string): boolean {
|
|
|
82
96
|
return baseUrl !== undefined && AZURE_ANTHROPIC_URL_MARKER.test(baseUrl);
|
|
83
97
|
}
|
|
84
98
|
|
|
99
|
+
/**
|
|
100
|
+
* Known non-official URLs that enforce Anthropic thinking signatures on replay.
|
|
101
|
+
*
|
|
102
|
+
* Runtime routing calls this with the effective URL because a model's resolved
|
|
103
|
+
* compat can be stale after Foundry or a provider base-URL override reroutes it.
|
|
104
|
+
*/
|
|
105
|
+
export function isAnthropicSigningProxyUrl(baseUrl?: string): boolean {
|
|
106
|
+
return (
|
|
107
|
+
hostMatchesUrl(baseUrl, "githubCopilot") ||
|
|
108
|
+
hostMatchesUrl(baseUrl, "zenmux") ||
|
|
109
|
+
isCloudflareAnthropicGateway(baseUrl) ||
|
|
110
|
+
isVertexAnthropicRoute(baseUrl) ||
|
|
111
|
+
isBedrockAnthropicRoute(baseUrl) ||
|
|
112
|
+
isAzureAnthropicRoute(baseUrl)
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
|
|
85
116
|
/** Build the resolved anthropic-messages compat record for a model spec. */
|
|
86
117
|
export function buildAnthropicCompat(spec: ModelSpec<"anthropic-messages">): ResolvedAnthropicCompat {
|
|
87
118
|
const baseUrl = spec.baseUrl;
|
|
@@ -98,12 +129,9 @@ export function buildAnthropicCompat(spec: ModelSpec<"anthropic-messages">): Res
|
|
|
98
129
|
// signature-enforcing Anthropic — same failure class as GitHub Copilot #2851
|
|
99
130
|
// (issue #4192).
|
|
100
131
|
const isZenmux = modelMatchesHost(spec, "zenmux");
|
|
101
|
-
const requiresThinkingEnabled = modelMatchesHost(spec, "moonshotNative") &&
|
|
102
|
-
const isVertex = isVertexAnthropicRoute(baseUrl);
|
|
103
|
-
const isBedrock = isBedrockAnthropicRoute(baseUrl);
|
|
132
|
+
const requiresThinkingEnabled = modelMatchesHost(spec, "moonshotNative") && matchesKimiMandatoryThinkingModel(spec);
|
|
104
133
|
const isAzure = isAzureAnthropicRoute(baseUrl);
|
|
105
|
-
const signingEndpoint =
|
|
106
|
-
official || isCopilot || isZenmux || isCloudflareAnthropicGateway(baseUrl) || isVertex || isBedrock || isAzure;
|
|
134
|
+
const signingEndpoint = official || isCopilot || isZenmux || isAnthropicSigningProxyUrl(baseUrl);
|
|
107
135
|
const compat: ResolvedAnthropicCompat = {
|
|
108
136
|
officialEndpoint: official,
|
|
109
137
|
signingEndpoint,
|
package/src/compat/bedrock.ts
CHANGED
|
@@ -42,6 +42,15 @@ const EXPLICIT_CHECKPOINTS_4096_1H: ResolvedBedrockCompat = {
|
|
|
42
42
|
promptCacheMaximumCheckpoints: 4,
|
|
43
43
|
};
|
|
44
44
|
|
|
45
|
+
// AWS モデルカード: 512 トークン、最大 4 個のキャッシュチェックポイント、5 分と 1 時間の TTL。
|
|
46
|
+
// https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-anthropic-claude-opus-5.html
|
|
47
|
+
const EXPLICIT_CHECKPOINTS_512_1H: ResolvedBedrockCompat = {
|
|
48
|
+
promptCacheMode: "explicit",
|
|
49
|
+
supportsLongPromptCacheRetention: true,
|
|
50
|
+
promptCacheMinimumTokens: 512,
|
|
51
|
+
promptCacheMaximumCheckpoints: 4,
|
|
52
|
+
};
|
|
53
|
+
|
|
45
54
|
/**
|
|
46
55
|
* Explicit Nova cache points complement Bedrock's automatic prefix caching:
|
|
47
56
|
* AWS recommends them for consistent cache hits and input-cost savings. Keep
|
|
@@ -82,6 +91,9 @@ function detectedBedrockCompat(modelId: string): ResolvedBedrockCompat {
|
|
|
82
91
|
) {
|
|
83
92
|
return EXPLICIT_CHECKPOINTS_4096_1H;
|
|
84
93
|
}
|
|
94
|
+
if (id.includes("anthropic.claude-opus-5")) {
|
|
95
|
+
return EXPLICIT_CHECKPOINTS_512_1H;
|
|
96
|
+
}
|
|
85
97
|
if (id.includes("anthropic.claude-opus-4-6")) {
|
|
86
98
|
return EXPLICIT_CHECKPOINTS_4096_5M;
|
|
87
99
|
}
|
package/src/identity/family.ts
CHANGED
|
@@ -261,6 +261,19 @@ export const modelFamilyToken = memo((modelId: string): string => {
|
|
|
261
261
|
return "";
|
|
262
262
|
});
|
|
263
263
|
|
|
264
|
+
/**
|
|
265
|
+
* True for Claude generations that support extended thinking: Sonnet/Opus 3.7+,
|
|
266
|
+
* every 4.x/5+ Opus/Sonnet, and the Fable/Mythos generation. Pre-thinking
|
|
267
|
+
* models (Claude 3.5 and older) are excluded so no thinking effort dial is
|
|
268
|
+
* fabricated for a model that rejects thinking parameters. Classifier-based, so
|
|
269
|
+
* dotted and dashed version forms both match; ids the classifier does not parse
|
|
270
|
+
* (e.g. Haiku, bare dated ids) return false.
|
|
271
|
+
*/
|
|
272
|
+
export const anthropicModelSupportsThinking = memo((modelId: string): boolean => {
|
|
273
|
+
const parsed = parseAnthropicModel(bareModelId(modelId));
|
|
274
|
+
return parsed !== null && semverGte(parsed.version, "3.7");
|
|
275
|
+
});
|
|
276
|
+
|
|
264
277
|
/**
|
|
265
278
|
* Adaptive thinking `display` is supported starting with Claude Opus 4.7+,
|
|
266
279
|
* Sonnet 5+, and the Claude Fable/Mythos 5 generation. Older adaptive-thinking
|
package/src/model-cache.ts
CHANGED
|
@@ -9,9 +9,11 @@ import type { Api, Model, ModelSpec } from "./types";
|
|
|
9
9
|
// Rows persist ModelSpec JSON (sparse `compat`, never the resolved record);
|
|
10
10
|
// the model manager rebuilds via `buildModel` on load. Request headers are
|
|
11
11
|
// intentionally omitted: arbitrary provider-defined header names can carry
|
|
12
|
-
// credentials.
|
|
13
|
-
//
|
|
14
|
-
//
|
|
12
|
+
// credentials. v12 invalidates Kimi Code rows carrying the blanket
|
|
13
|
+
// maxTokens: 32000 that predate per-family output caps (k3/k3-256k -> 131072,
|
|
14
|
+
// kimi-for-coding[-highspeed] -> 32768, #6711); v11 invalidates rows that may
|
|
15
|
+
// persist derived computer-use
|
|
16
|
+
// headers and records which model ids lost headers or cannot be rebuilt.
|
|
15
17
|
// v9 invalidated Kimi Code rows predating live effort and protocol metadata;
|
|
16
18
|
// v8 invalidated Codex discovery rows predating provider-native V2 compaction
|
|
17
19
|
// metadata; v7 invalidated rows predating the Antigravity Gemini budget-mode
|
|
@@ -20,7 +22,7 @@ import type { Api, Model, ModelSpec } from "./types";
|
|
|
20
22
|
// retired unknown-limit sentinels (222222/8888); v5 invalidated rows predating
|
|
21
23
|
// effort-tier variant collapsing (raw `-low`/`-high`/`-thinking` member ids);
|
|
22
24
|
// v4 dropped the pre-efforts ThinkingConfig shape.
|
|
23
|
-
const CACHE_SCHEMA_VERSION =
|
|
25
|
+
const CACHE_SCHEMA_VERSION = 12;
|
|
24
26
|
const HEADER_RESTORE_VERSION = 1;
|
|
25
27
|
|
|
26
28
|
interface CacheRow {
|
|
@@ -204,8 +206,8 @@ function hasModelHeaders(model: Model<Api>): boolean {
|
|
|
204
206
|
* headers and reject/refetch dynamic-only cached models that need live headers.
|
|
205
207
|
*/
|
|
206
208
|
function toCachedModelSpec<TApi extends Api>(model: Model<TApi>): ModelSpec<TApi> {
|
|
207
|
-
const { headers: _headers, compatConfig, ...rest } = model;
|
|
208
|
-
return { ...rest, compat: compatConfig };
|
|
209
|
+
const { headers: _headers, compatConfig, supportsComputerUseConfig, ...rest } = model;
|
|
210
|
+
return { ...rest, supportsComputerUse: supportsComputerUseConfig, compat: compatConfig };
|
|
209
211
|
}
|
|
210
212
|
|
|
211
213
|
/** Whether two in-memory header records are byte-for-byte equivalent. */
|
|
@@ -228,6 +230,7 @@ export function writeModelCache<TApi extends Api>(
|
|
|
228
230
|
staticFingerprint: string,
|
|
229
231
|
dbPath?: string,
|
|
230
232
|
staticHeaderSources: readonly Model<TApi>[] = [],
|
|
233
|
+
restorableHeaderFallback?: Record<string, string>,
|
|
231
234
|
): void {
|
|
232
235
|
try {
|
|
233
236
|
withModelCacheDb(dbPath, db => {
|
|
@@ -244,7 +247,14 @@ export function writeModelCache<TApi extends Api>(
|
|
|
244
247
|
// unrestorable and dropped on the next offline read (#6037, #6284).
|
|
245
248
|
const staticHeaderSource =
|
|
246
249
|
staticById.get(model.id) ?? (model.requestModelId ? staticById.get(model.requestModelId) : undefined);
|
|
247
|
-
|
|
250
|
+
// A model with no static source is still restorable when its live
|
|
251
|
+
// headers equal the provider's trusted constant (a compile-time,
|
|
252
|
+
// non-credential value the reader can reattach by value). This keeps
|
|
253
|
+
// reference-less Copilot models (e.g. claude-opus-5) alive offline.
|
|
254
|
+
const matchesStatic = staticHeaderSource
|
|
255
|
+
? headersEqual(model.headers, staticHeaderSource.headers)
|
|
256
|
+
: headersEqual(model.headers, restorableHeaderFallback);
|
|
257
|
+
if (!matchesStatic) {
|
|
248
258
|
unrestorableHeaderModelIds.push(model.id);
|
|
249
259
|
}
|
|
250
260
|
}
|