@linxiraos/pi-catalog 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +88 -6
- package/package.json +98 -99
- package/src/build.ts +3 -3
- package/src/compat/openai.ts +0 -6
- package/src/discovery/antigravity.ts +5 -1
- package/src/discovery/openai-compatible.ts +21 -4
- package/src/identity/classify.ts +32 -9
- package/src/identity/family.ts +18 -4
- package/src/model-manager.ts +13 -2
- package/src/model-thinking.ts +15 -7
- package/src/models.json +7999 -2099
- package/src/models.ts +28 -14
- package/src/provider-models/bundled-references.ts +4 -2
- package/src/provider-models/openai-compat.ts +379 -301
- package/src/types.ts +24 -16
- package/src/variant-collapse.ts +34 -17
- package/src/wire/gemini-headers.ts +79 -18
- package/dist/types/build.d.ts +0 -14
- package/dist/types/compat/anthropic.d.ts +0 -18
- package/dist/types/compat/apply.d.ts +0 -7
- package/dist/types/compat/bedrock.d.ts +0 -3
- package/dist/types/compat/devin.d.ts +0 -9
- package/dist/types/compat/openai.d.ts +0 -26
- package/dist/types/discovery/antigravity.d.ts +0 -70
- package/dist/types/discovery/codex.d.ts +0 -36
- package/dist/types/discovery/cursor-gen/agent_pb.d.ts +0 -16769
- package/dist/types/discovery/cursor.d.ts +0 -23
- package/dist/types/discovery/devin-gen/buf/validate/validate_pb.d.ts +0 -1715
- package/dist/types/discovery/devin-gen/exa/analytics_pb/analytics_pb.d.ts +0 -693
- package/dist/types/discovery/devin-gen/exa/api_server_pb/api_server_pb.d.ts +0 -9158
- package/dist/types/discovery/devin-gen/exa/auth_pb/auth_pb.d.ts +0 -52
- package/dist/types/discovery/devin-gen/exa/auto_cascade_common_pb/auto_cascade_common_pb.d.ts +0 -276
- package/dist/types/discovery/devin-gen/exa/bug_checker_pb/bug_checker_pb.d.ts +0 -78
- package/dist/types/discovery/devin-gen/exa/cascade_plugins_pb/cascade_plugins_pb.d.ts +0 -999
- package/dist/types/discovery/devin-gen/exa/chat_pb/chat_pb.d.ts +0 -1704
- package/dist/types/discovery/devin-gen/exa/code_edit/code_edit_pb/code_edit_pb.d.ts +0 -656
- package/dist/types/discovery/devin-gen/exa/codeium_common_pb/codeium_common_pb.d.ts +0 -15014
- package/dist/types/discovery/devin-gen/exa/context_module_pb/context_module_pb.d.ts +0 -607
- package/dist/types/discovery/devin-gen/exa/cortex_pb/cortex_pb.d.ts +0 -11830
- package/dist/types/discovery/devin-gen/exa/diff_action_pb/diff_action_pb.d.ts +0 -248
- package/dist/types/discovery/devin-gen/exa/index_pb/index_pb.d.ts +0 -1747
- package/dist/types/discovery/devin-gen/exa/knowledge_base_pb/knowledge_base_pb.d.ts +0 -509
- package/dist/types/discovery/devin-gen/exa/language_server_pb/language_server_pb.d.ts +0 -9048
- package/dist/types/discovery/devin-gen/exa/opensearch_clients_pb/opensearch_clients_pb.d.ts +0 -1760
- package/dist/types/discovery/devin-gen/exa/prompt_pb/prompt_pb.d.ts +0 -286
- package/dist/types/discovery/devin-gen/exa/reactive_component_pb/reactive_component_pb.d.ts +0 -405
- package/dist/types/discovery/devin-gen/exa/trust_pb/trust_pb.d.ts +0 -582
- package/dist/types/discovery/devin.d.ts +0 -24
- package/dist/types/discovery/gemini.d.ts +0 -25
- package/dist/types/discovery/gitlab-duo-workflow.d.ts +0 -30
- package/dist/types/discovery/index.d.ts +0 -5
- package/dist/types/discovery/openai-compatible.d.ts +0 -74
- package/dist/types/effort.d.ts +0 -10
- package/dist/types/fireworks-model-id.d.ts +0 -23
- package/dist/types/hosts.d.ts +0 -164
- package/dist/types/identity/bundled.d.ts +0 -3
- package/dist/types/identity/classify.d.ts +0 -69
- package/dist/types/identity/dialect.d.ts +0 -3
- package/dist/types/identity/family.d.ts +0 -181
- package/dist/types/identity/id.d.ts +0 -12
- package/dist/types/identity/index.d.ts +0 -8
- package/dist/types/identity/markers.d.ts +0 -4
- package/dist/types/identity/priority.d.ts +0 -1
- package/dist/types/identity/reference.d.ts +0 -29
- package/dist/types/index.d.ts +0 -17
- package/dist/types/model-cache.d.ts +0 -23
- package/dist/types/model-manager.d.ts +0 -77
- package/dist/types/model-thinking.d.ts +0 -66
- package/dist/types/models.d.ts +0 -12
- package/dist/types/provider-models/bundled-references.d.ts +0 -13
- package/dist/types/provider-models/cache-provider-id.d.ts +0 -9
- package/dist/types/provider-models/descriptor-types.d.ts +0 -76
- package/dist/types/provider-models/descriptors.d.ts +0 -504
- package/dist/types/provider-models/google.d.ts +0 -27
- package/dist/types/provider-models/index.d.ts +0 -7
- package/dist/types/provider-models/ollama.d.ts +0 -22
- package/dist/types/provider-models/openai-compat.d.ts +0 -599
- package/dist/types/provider-models/special.d.ts +0 -53
- package/dist/types/types.d.ts +0 -811
- package/dist/types/utils.d.ts +0 -20
- package/dist/types/variant-collapse.d.ts +0 -133
- package/dist/types/wire/alibaba-token-plan.d.ts +0 -22
- package/dist/types/wire/codex.d.ts +0 -42
- package/dist/types/wire/coreweave.d.ts +0 -12
- package/dist/types/wire/gemini-headers.d.ts +0 -37
- package/dist/types/wire/github-copilot.d.ts +0 -39
package/CHANGELOG.md
CHANGED
|
@@ -2,9 +2,81 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [1.0.2] - 2026-08-15
|
|
6
|
+
|
|
7
|
+
## [17.3.2] - 2026-08-13
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- Added support for the `deepseek-v4-pro:preview` model
|
|
12
|
+
- Added support for the `gemini-3.7-flash` model
|
|
13
|
+
- Added dynamic Antigravity client-version discovery from the official update manifest (darwin/arm64 channel), so version-gated models appear without a code change; `PI_AI_ANTIGRAVITY_VERSION` remains available as an override.
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- Fixed Antigravity discovery missing Gemini 3.7 Flash: Cloud Code Assist gates newer models on the client version in the `User-Agent`, and the pinned `antigravity/hub/2.1.4` was too old. The user-agent now matches the captured 2.8.0 client format (`antigravity/hub/2.8.0 (aidev_client; os_type=darwin; arch=arm64; cl=963137146)`); os_type/arch stay pinned to the darwin/arm64 reference client. Overridable via `PI_AI_ANTIGRAVITY_VERSION` / `PI_AI_ANTIGRAVITY_CL` / `PI_AI_ANTIGRAVITY_OS` / `PI_AI_ANTIGRAVITY_ARCH`.
|
|
18
|
+
|
|
19
|
+
### Removed
|
|
20
|
+
|
|
21
|
+
- Removed `ANTIGRAVITY_SYSTEM_INSTRUCTION` from `wire/gemini-headers`; the Antigravity transport and web search no longer inject a fake identity prompt.
|
|
22
|
+
|
|
23
|
+
## [17.3.1] - 2026-08-13
|
|
24
|
+
|
|
25
|
+
### Added
|
|
26
|
+
|
|
27
|
+
- Added dynamic Antigravity and Gemini CLI discovery support for Gemini 3.7 Flash, with low/medium/high thinking-level routing.
|
|
28
|
+
|
|
5
29
|
### Changed
|
|
6
30
|
|
|
7
|
-
-
|
|
31
|
+
- Updated model metadata, context windows, pricing, and configurations in the catalog
|
|
32
|
+
|
|
33
|
+
## [17.3.0] - 2026-08-13
|
|
34
|
+
|
|
35
|
+
### Breaking Changes
|
|
36
|
+
|
|
37
|
+
- Removed `OpenAICompat.enableGeminiThinkingLoopGuard`; thinking-loop eligibility is derived solely from the `model.id` family.
|
|
38
|
+
|
|
39
|
+
### Added
|
|
40
|
+
|
|
41
|
+
- Added first-party OpenAI Daybreak Blue, Daybreak Red, and GPT-5.6 Cyber models with full support for their documented API pricing (including long-context rates above 272K input), token limits, tools, and reasoning effort controls (off/low/medium/high/xhigh/max).
|
|
42
|
+
- Added calculateUncachedInputCost() to calculate prompt pricing against active context-length tiers without prompt caching.
|
|
43
|
+
|
|
44
|
+
### Fixed
|
|
45
|
+
|
|
46
|
+
- Fixed Anthropic cache-write pricing to correctly honor mixed 5-minute and 1-hour TTL usage instead of incorrectly charging all writes at the 5-minute rate.
|
|
47
|
+
- Fixed Ollama Cloud DeepSeek V4 Flash and older reasoners to correctly apply the DeepSeek effort contract (e.g., low/high/max) instead of the generic effort ladder.
|
|
48
|
+
- Added a default request timeout to OpenAI-compatible model discovery to prevent stalled provider endpoints from hanging startup indefinitely.
|
|
49
|
+
- Fixed Anthropic cache-write pricing to honor mixed 5-minute and 1-hour TTL usage instead of charging every write at the 5-minute rate.
|
|
50
|
+
- Fixed Ollama Cloud DeepSeek V4 Flash (including dated/preview tags like `deepseek-v4-flash:0731`) exposing the generic `minimal`/`low`/`medium`/`high`/`xhigh` effort ladder without `max`; the `ollama-chat` transport now applies the DeepSeek effort contract (Flash → `low`/`high`/`max`, older reasoners → `high`/`max`), matching the direct API and every other host ([#8334](https://github.com/can1357/oh-my-pi/issues/8334)).
|
|
51
|
+
- Exposed the `low` reasoning-effort tier for DeepSeek V4 Pro on the direct API and faithful aggregator routes, matching DeepSeek's updated API contract advertising `reasoning_effort` `low`/`high`/`max` for both V4 SKUs; OpenRouter's non-Flash route still exposes only `high`, and the older V3.x/R1 reasoners remain `high`/`max` ([#8405](https://github.com/can1357/oh-my-pi/issues/8405)).
|
|
52
|
+
- Bounded OpenAI-compatible model discovery with a default request timeout so a stalled provider `/models` endpoint can no longer hang startup indefinitely in `resolveModelDiscoveryFallback` ([#8315](https://github.com/can1357/oh-my-pi/issues/8315)).
|
|
53
|
+
- Fixed Codex-discovered `gpt-daybreak-*` aliases being treated as unknown models, restoring the GPT-5.6 `low`/`medium`/`high`/`xhigh`/`max` effort ladder and its 372K fallback only when the Codex registry omits `context_window`.
|
|
54
|
+
- Fixed first-party OpenAI GPT-5.6 aliases to preserve wire-level `off` through generated pro aliases and to price requests above 272K input at each SKU's documented long-context rates.
|
|
55
|
+
|
|
56
|
+
## [17.2.15] - 2026-08-12
|
|
57
|
+
|
|
58
|
+
### Fixed
|
|
59
|
+
|
|
60
|
+
- Fixed classification of model IDs with large minor versions (e.g., `claude-opus-5-11`) or three-part versions, ensuring they no longer fall back to stale default configurations.
|
|
61
|
+
|
|
62
|
+
## [17.2.13] - 2026-08-11
|
|
63
|
+
|
|
64
|
+
### Changed
|
|
65
|
+
|
|
66
|
+
- Standardized catalog discovery User-Agent headers on `omp/<version>` via the shared `USER_AGENT` utility.
|
|
67
|
+
|
|
68
|
+
### Fixed
|
|
69
|
+
|
|
70
|
+
- Marked `meta/muse-spark-1.2` and `muse-spark-1.2-contributor` as image-capable (`input: ["text", "image"]`) with the same Responses reasoning, thinking, and cost metadata as `muse-spark-1.1` (contributor uses its discounted 0.1/0.2 pricing), so `omp models` no longer lists them as text-only.
|
|
71
|
+
- Fixed GLM-5.2 thinking levels across Baseten, CoreWeave, HuggingFace, and other uppercase-ID resellers, which were getting the generic `xhigh` effort ladder instead of the GLM-5.2-specific tiers. Also added Baseten `zai-org/GLM-5.2-Fast` and Fireworks `glm-5.2-fast` as reasoning models ([#8200](https://github.com/can1357/oh-my-pi/pull/8200) by [@jcfrancisco](https://github.com/jcfrancisco)).
|
|
72
|
+
|
|
73
|
+
## [17.2.12] - 2026-08-08
|
|
74
|
+
|
|
75
|
+
### Fixed
|
|
76
|
+
|
|
77
|
+
- Fixed dynamically discovered `alibaba-token-plan/qwen3.8-max` metadata so thinking controls and image input are available ([#8019](https://github.com/can1357/oh-my-pi/issues/8019)).
|
|
78
|
+
- Routed `opencode-go/deepseek-v4-flash` through the OpenAI responses API — the OpenCode Go gateway does not serve this model at `/zen/go/v1/chat/completions`, only at `/zen/go/v1/responses` (`deepseek-v4-pro` keeps chat completions).
|
|
79
|
+
|
|
8
80
|
## [17.2.11] - 2026-08-07
|
|
9
81
|
|
|
10
82
|
### Fixed
|
|
@@ -816,8 +888,8 @@
|
|
|
816
888
|
|
|
817
889
|
### Added
|
|
818
890
|
|
|
819
|
-
- Added the `ToolCallSyntax` union and `FALLBACK_TOOL_SYNTAX` constant to `@
|
|
820
|
-
- Added `preferredToolSyntax(modelId)` to `@
|
|
891
|
+
- Added the `ToolCallSyntax` union and `FALLBACK_TOOL_SYNTAX` constant to `@linxiraos/pi-catalog/identity` (re-exported from `@linxiraos/pi-ai/grammar`).
|
|
892
|
+
- Added `preferredToolSyntax(modelId)` to `@linxiraos/pi-catalog/identity`, resolving a model's native tool-call syntax affinity from its family token (Claude→`anthropic`, GLM→`glm`, Kimi→`kimi`, Qwen→`qwen3`, DeepSeek→`deepseek`, OpenAI/gpt-oss→`harmony`, else the `xml` fallback).
|
|
821
893
|
- Added `flux-1-schnell-fp8` to the Fireworks serverless model catalog
|
|
822
894
|
- Added `gpt-oss-20b` to the Fireworks model catalog
|
|
823
895
|
- Added `qwen3-embedding-8b` to the Fireworks model catalog
|
|
@@ -835,7 +907,7 @@
|
|
|
835
907
|
|
|
836
908
|
### Added
|
|
837
909
|
|
|
838
|
-
- Added `modelFamilyToken(modelId)` to `@
|
|
910
|
+
- Added `modelFamilyToken(modelId)` to `@linxiraos/pi-catalog/identity`: a coarse vendor-lineage token (`anthropic`/`openai`/`gemini`/`kimi`/…) for "are two models the same family?" comparisons, backed by `parseKnownModel` canonical-id normalization. Opaque and comparison-only; kind/variant collapsed onto the vendor token ([#2406](https://github.com/can1357/oh-my-pi/issues/2406))
|
|
839
911
|
|
|
840
912
|
### Changed
|
|
841
913
|
|
|
@@ -971,7 +1043,7 @@
|
|
|
971
1043
|
- `buildModel(spec)` (`build.ts`) is now the single Model constructor: it materializes the fully-resolved compat record and canonical thinking metadata exactly once (compat first, thinking derived from identity + resolved compat), so `Model.compat` is a required, complete `CompatOf<TApi>` (`ResolvedOpenAICompat`/`ResolvedOpenAIResponsesCompat`/`ResolvedAnthropicCompat`) and request-path code reads fields with zero URL parsing and zero per-request allocation. Sparse user/config overrides live on the new `ModelSpec<TApi>` input shape and survive on `Model.compatConfig` for introspection.
|
|
972
1044
|
- Added `ResolvedAnthropicCompat.supportsSamplingParams` (Opus 4.7+/Fable/Mythos reject `temperature`/`top_p`/`top_k` with a 400), baked at build time from model identity so the request path stops re-parsing model ids.
|
|
973
1045
|
- Compat detection gained model-time flags so handlers stop sniffing baseUrl: completions `supportsReasoningParams`, `alwaysSendMaxTokens`, `isOpenRouterHost`, `isVercelGatewayHost`, `streamIdleTimeoutMs`, and a precomputed `whenThinking` alternate view (OpenCode `reasoning_content` gating, #1071/#1484); responses `strictResponsesPairing`, `supportsLongPromptCacheRetention`, `supportsReasoningEffort`; anthropic `officialEndpoint`, `requiresToolResultId`, `replayUnsignedThinking`.
|
|
974
|
-
- New `@
|
|
1046
|
+
- New `@linxiraos/pi-catalog` package: the model catalog extracted from `@linxiraos/pi-ai`. Owns the bundled `models.json` and its generation pipeline (`scripts/generate-models.ts`), the core model data types (`Model`, `Api`, `ThinkingConfig`, `Effort`, `Usage`, compat interfaces), thinking metadata enrichment and generated policies (`model-thinking.ts`), the SQLite model cache and model manager, per-provider discovery factories (`provider-models/`), the discovery protocol clients (`discovery/`), and the new `CATALOG_PROVIDERS` table — the single source of truth for provider ids, default models, and discovery wiring (`KnownProvider`, `PROVIDER_DESCRIPTORS`, and `DEFAULT_MODEL_PER_PROVIDER` are derived from it).
|
|
975
1047
|
- New `identity/` module centralizing model-identity concerns that were previously duplicated across packages: family classification and version parsing (`identity/classify.ts`, extracted from pi-ai's `model-thinking` internals), canonical model equivalence with injected reference data (`identity/equivalence.ts`, from coding-agent's `model-equivalence`), proxy/reseller reference lookup (`identity/reference.ts`, from coding-agent's `model-registry`), bracket-affix and id-segment helpers (`identity/id.ts`), a single trailing-marker vocabulary with canonical vs reference flavors (`identity/markers.ts` — `search` stays reference-only so Perplexity's `sonar-pro-search` remains canonical-distinct), and provider priority ordering (`identity/priority.ts`).
|
|
976
1048
|
- Memoized bundled-reference accessors (`getBundledCanonicalReferenceData` / `getBundledModelReferenceIndex` in `identity/bundled.ts`): one lazy walk of the bundled catalog feeds both canonical equivalence and proxy-reference lookup, so consumers no longer hand-roll the glue.
|
|
977
1049
|
- `identity/selection.ts`: pure canonical-variant selection (`resolveCanonicalVariant`, `buildCanonicalModelOrder`, `CanonicalVariantPreferences`) extracted from the coding-agent registry — provider rank, then exact-id match, variant source, id length, and candidate order.
|
|
@@ -990,10 +1062,20 @@
|
|
|
990
1062
|
|
|
991
1063
|
- Fixed Anthropic official-endpoint detection to require strict HTTPS hostname matching so non-official or lookalike URLs are no longer treated as official Anthropic hosts
|
|
992
1064
|
- Fixed Ollama Cloud dynamic discovery so same-id matches from other providers no longer supply context-window or max-output-token limits for discovered models.
|
|
993
|
-
- Wired `@
|
|
1065
|
+
- Wired `@linxiraos/pi-catalog` into the release publish package list, tarball install smoke test, and root `bun generate-models` script.
|
|
994
1066
|
- Fixed `supportsAdaptiveThinkingDisplay` only matching dash-form version ids: dotted ids (`claude-opus-4.7`) now classify through `identity/classify` like every other anthropic predicate, so six bundled dotted Opus 4.7/4.8 entries (github-copilot, vercel-ai-gateway, zenmux) regain adaptive `display` support; bare dated ids (`claude-opus-4-20250514` = Opus 4.0) stay excluded.
|
|
995
1067
|
- Fixed the OpenRouter anthropic adaptive-effort map misclassifying bare dated Opus ids (`claude-opus-4-20250514` parsed as version 4.20 → wrongly adaptive); the map now derives from the shared classifier and the shared 4-/5-tier tables.
|
|
996
1068
|
|
|
997
1069
|
### Removed
|
|
998
1070
|
|
|
999
1071
|
- Removed the runtime enrichment layer: `enrichModelThinking` (and its non-enumerable memo-slot cache), `refreshModelThinking`, `modelOmitsReasoningEffort`, and the `model-thinking` re-exports of generator-only policies. Thinking metadata is resolved exactly once inside `buildModel`; runtime helpers (`getSupportedEfforts`, `clampThinkingLevelForModel`, `requireSupportedEffort`, the effort mappers) are pure field reads.
|
|
1072
|
+
|
|
1073
|
+
## [1.0.1] - 2026-08-14
|
|
1074
|
+
|
|
1075
|
+
- Reset the version to 1.0.0 and republished under the `@linxiraos/*` scope, breaking from the `@oh-my-pi` version lineage.
|
|
1076
|
+
|
|
1077
|
+
## [1.0.0] - 2026-08-13
|
|
1078
|
+
|
|
1079
|
+
### Changed
|
|
1080
|
+
|
|
1081
|
+
- Reset the version to 1.0.0 and republished under the `@linxiraos/*` scope, breaking from the `@oh-my-pi` version lineage.
|
package/package.json
CHANGED
|
@@ -1,101 +1,100 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
}
|
|
2
|
+
"type": "module",
|
|
3
|
+
"name": "@linxiraos/pi-catalog",
|
|
4
|
+
"version": "1.0.2",
|
|
5
|
+
"description": "Model catalog for omp: bundled model database, provider discovery descriptors, model identity, classification, and equivalence",
|
|
6
|
+
"homepage": "https://linxira-os.github.io",
|
|
7
|
+
"author": "Can Boluk",
|
|
8
|
+
"license": "MIT",
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/Linxira-OS/linxira-zeta.git",
|
|
12
|
+
"directory": "packages/catalog"
|
|
13
|
+
},
|
|
14
|
+
"bugs": {
|
|
15
|
+
"url": "https://github.com/can1357/oh-my-pi/issues"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"ai",
|
|
19
|
+
"llm",
|
|
20
|
+
"models",
|
|
21
|
+
"catalog",
|
|
22
|
+
"discovery"
|
|
23
|
+
],
|
|
24
|
+
"main": "./src/index.ts",
|
|
25
|
+
"types": "./src/index.ts",
|
|
26
|
+
"scripts": {
|
|
27
|
+
"check": "biome check . && bun run check:types",
|
|
28
|
+
"check:types": "tsgo -p tsconfig.json --noEmit",
|
|
29
|
+
"lint": "biome lint .",
|
|
30
|
+
"test": "bun test --parallel",
|
|
31
|
+
"fix": "biome check --write --unsafe .",
|
|
32
|
+
"fmt": "biome format --write .",
|
|
33
|
+
"gen:models": "bun scripts/generate-models.ts",
|
|
34
|
+
"gen:cursor-proto": "protoc --plugin=protoc-gen-es=../../node_modules/.bin/protoc-gen-es --es_out=src/discovery/cursor-gen --es_opt=target=ts -I ../ai/src/providers/cursor/proto ../ai/src/providers/cursor/proto/agent.proto"
|
|
35
|
+
},
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"@bufbuild/protobuf": "catalog:",
|
|
38
|
+
"@linxiraos/pi-omptype": "catalog:",
|
|
39
|
+
"@linxiraos/pi-utils": "catalog:"
|
|
40
|
+
},
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"@linxiraos/pi-ai": "catalog:",
|
|
43
|
+
"@types/bun": "catalog:"
|
|
44
|
+
},
|
|
45
|
+
"engines": {
|
|
46
|
+
"bun": ">=1.3.14"
|
|
47
|
+
},
|
|
48
|
+
"files": [
|
|
49
|
+
"src",
|
|
50
|
+
"README.md",
|
|
51
|
+
"CHANGELOG.md"
|
|
52
|
+
],
|
|
53
|
+
"exports": {
|
|
54
|
+
".": {
|
|
55
|
+
"types": "./src/index.ts",
|
|
56
|
+
"import": "./src/index.ts"
|
|
57
|
+
},
|
|
58
|
+
"./models.json": {
|
|
59
|
+
"types": "./src/models.json.d.ts",
|
|
60
|
+
"import": "./src/models.json"
|
|
61
|
+
},
|
|
62
|
+
"./provider-models": {
|
|
63
|
+
"types": "./src/provider-models/index.ts",
|
|
64
|
+
"import": "./src/provider-models/index.ts"
|
|
65
|
+
},
|
|
66
|
+
"./provider-models/*": {
|
|
67
|
+
"types": "./src/provider-models/*.ts",
|
|
68
|
+
"import": "./src/provider-models/*.ts"
|
|
69
|
+
},
|
|
70
|
+
"./discovery": {
|
|
71
|
+
"types": "./src/discovery/index.ts",
|
|
72
|
+
"import": "./src/discovery/index.ts"
|
|
73
|
+
},
|
|
74
|
+
"./discovery/*": {
|
|
75
|
+
"types": "./src/discovery/*.ts",
|
|
76
|
+
"import": "./src/discovery/*.ts"
|
|
77
|
+
},
|
|
78
|
+
"./identity": {
|
|
79
|
+
"types": "./src/identity/index.ts",
|
|
80
|
+
"import": "./src/identity/index.ts"
|
|
81
|
+
},
|
|
82
|
+
"./identity/*": {
|
|
83
|
+
"types": "./src/identity/*.ts",
|
|
84
|
+
"import": "./src/identity/*.ts"
|
|
85
|
+
},
|
|
86
|
+
"./wire/*": {
|
|
87
|
+
"types": "./src/wire/*.ts",
|
|
88
|
+
"import": "./src/wire/*.ts"
|
|
89
|
+
},
|
|
90
|
+
"./compat/*": {
|
|
91
|
+
"types": "./src/compat/*.ts",
|
|
92
|
+
"import": "./src/compat/*.ts"
|
|
93
|
+
},
|
|
94
|
+
"./*": {
|
|
95
|
+
"types": "./src/*.ts",
|
|
96
|
+
"import": "./src/*.ts"
|
|
97
|
+
},
|
|
98
|
+
"./*.js": "./src/*.ts"
|
|
99
|
+
}
|
|
101
100
|
}
|
package/src/build.ts
CHANGED
|
@@ -14,12 +14,11 @@ import { buildAnthropicCompat } from "./compat/anthropic";
|
|
|
14
14
|
import { buildBedrockCompat } from "./compat/bedrock";
|
|
15
15
|
import { buildDevinCompat } from "./compat/devin";
|
|
16
16
|
import { buildOpenAICompat, buildOpenAIResponsesCompat, buildOpenRouterCompat } from "./compat/openai";
|
|
17
|
+
import { bareModelId, parseOpenAIModel, semverGte } from "./identity/classify";
|
|
17
18
|
import { resolveModelThinking } from "./model-thinking";
|
|
18
19
|
import type { Api, CompatOf, Model, ModelSpec } from "./types";
|
|
19
20
|
import { cleanModelName } from "./utils";
|
|
20
21
|
|
|
21
|
-
const OPENAI_GA_COMPUTER_MODEL_RE = /^gpt-5\.(?:[4-9]|[1-9]\d)(?:[.-]|$)/i;
|
|
22
|
-
|
|
23
22
|
function isDirectOpenAIResponsesEndpoint(spec: ModelSpec<Api>): boolean {
|
|
24
23
|
if (spec.api === "openai-responses") {
|
|
25
24
|
if (spec.provider !== "openai") return false;
|
|
@@ -55,7 +54,8 @@ function explicitComputerUseConfig(spec: ModelSpec<Api>): boolean | undefined {
|
|
|
55
54
|
function supportsOpenAIGAComputerUse(spec: ModelSpec<Api>, explicitSupport: boolean | undefined): boolean {
|
|
56
55
|
if (explicitSupport !== undefined) return explicitSupport;
|
|
57
56
|
if (!isDirectOpenAIResponsesEndpoint(spec)) return false;
|
|
58
|
-
|
|
57
|
+
const parsed = parseOpenAIModel(bareModelId(spec.requestModelId ?? spec.id));
|
|
58
|
+
return parsed !== null && semverGte(parsed.version, "5.4");
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
export function buildModel<TApi extends Api>(spec: ModelSpec<TApi>): Model<TApi> {
|
package/src/compat/openai.ts
CHANGED
|
@@ -22,7 +22,6 @@ import {
|
|
|
22
22
|
isMimoModelIdOrName,
|
|
23
23
|
isOpenAISamplingRestrictedModelId,
|
|
24
24
|
isQwenModelId,
|
|
25
|
-
modelFamilyToken,
|
|
26
25
|
} from "../identity/family";
|
|
27
26
|
import type {
|
|
28
27
|
ModelSpec,
|
|
@@ -474,10 +473,6 @@ export function buildOpenAICompat(spec: ModelSpec<"openai-completions">): Resolv
|
|
|
474
473
|
supportsSamplingParams: !isOpenAISamplingRestrictedModelId(spec.id),
|
|
475
474
|
reasoningEffortMap: {},
|
|
476
475
|
supportsUsageInStreaming: !isCerebras,
|
|
477
|
-
// pi-ai's thinking-loop guard is gemini-only; default the flag from the
|
|
478
|
-
// family classifier so OpenAI-compat proxies serving Gemini are covered.
|
|
479
|
-
// An opaque alias can opt in via `compat.enableGeminiThinkingLoopGuard`.
|
|
480
|
-
enableGeminiThinkingLoopGuard: modelFamilyToken(spec.id) === "gemini",
|
|
481
476
|
// Kimi (including via OpenRouter and Fireworks router-form IDs such as
|
|
482
477
|
// `accounts/fireworks/routers/kimi-*`) calculates TPM rate limits based on
|
|
483
478
|
// max_tokens, not actual output. The official Kimi K2 model guidance
|
|
@@ -749,7 +744,6 @@ export function buildOpenAIResponsesCompat(spec: OpenAIResponsesSpecLike): Resol
|
|
|
749
744
|
// lands on Moonshot's MFJS validator.
|
|
750
745
|
toolSchemaFlavor: isKimiModel ? "moonshot-mfjs" : undefined,
|
|
751
746
|
alwaysSendMaxTokens: spec.id ? isKimiModelId(spec.id) : false,
|
|
752
|
-
enableGeminiThinkingLoopGuard: modelFamilyToken(spec.id ?? "") === "gemini",
|
|
753
747
|
supportsObfuscationOptOut: isOpenAIUrl || spec.provider === "openai",
|
|
754
748
|
stripDeepseekSpecialTokens:
|
|
755
749
|
Boolean(id) && isDeepseekModelIdOrName(id) && (spec.provider === "nvidia" || spec.provider === "deepseek"),
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
collapseEffortVariants,
|
|
7
7
|
type VariantCollapseTable,
|
|
8
8
|
} from "../variant-collapse";
|
|
9
|
-
import { getAntigravityUserAgent } from "../wire/gemini-headers";
|
|
9
|
+
import { ensureAntigravityVersion, getAntigravityUserAgent } from "../wire/gemini-headers";
|
|
10
10
|
|
|
11
11
|
export const ANTIGRAVITY_PRIMARY_ENDPOINT = "https://daily-cloudcode-pa.googleapis.com";
|
|
12
12
|
export const ANTIGRAVITY_SANDBOX_ENDPOINT = "https://daily-cloudcode-pa.sandbox.googleapis.com";
|
|
@@ -161,6 +161,10 @@ export interface FetchAntigravityDiscoveryModelsOptions {
|
|
|
161
161
|
export async function fetchAntigravityDiscoveryModels(
|
|
162
162
|
options: FetchAntigravityDiscoveryModelsOptions,
|
|
163
163
|
): Promise<ModelSpec<"google-gemini-cli">[] | null> {
|
|
164
|
+
if (options.userAgent === undefined) {
|
|
165
|
+
await ensureAntigravityVersion(options.fetcher ?? fetch, options.signal);
|
|
166
|
+
}
|
|
167
|
+
|
|
164
168
|
const fetcher = discoveryFetch(options.fetcher);
|
|
165
169
|
const endpoints = options.endpoint
|
|
166
170
|
? [trimTrailingSlashes(options.endpoint)]
|
|
@@ -4,6 +4,18 @@ import { discoveryFetch } from "../utils";
|
|
|
4
4
|
|
|
5
5
|
const MODELS_PATH = "/models";
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* Default hard deadline applied to an OpenAI-compatible `/models` probe when
|
|
9
|
+
* the caller supplies neither an `AbortSignal` nor an explicit `timeoutMs`.
|
|
10
|
+
*
|
|
11
|
+
* Built-in provider model managers (openrouter, xAI, DeepSeek, …) call
|
|
12
|
+
* {@link fetchOpenAICompatibleModels} with no timeout, so without this bound a
|
|
13
|
+
* stalled endpoint left the request pending forever and blocked startup's
|
|
14
|
+
* awaited `resolveModelDiscoveryFallback` discovery pass indefinitely
|
|
15
|
+
* (issue #8315). 10s matches the coding-agent's remote-discovery budget.
|
|
16
|
+
*/
|
|
17
|
+
export const DEFAULT_OPENAI_COMPATIBLE_DISCOVERY_TIMEOUT_MS = 10_000;
|
|
18
|
+
|
|
7
19
|
/**
|
|
8
20
|
* Uses a cancellable timer rather than the native abort-timeout helper so
|
|
9
21
|
* successful fast discovery requests do not leave armed timeout signals for
|
|
@@ -96,7 +108,11 @@ export interface FetchOpenAICompatibleModelsOptions<TApi extends Api> {
|
|
|
96
108
|
headers?: Record<string, string>;
|
|
97
109
|
/** Optional AbortSignal for request cancellation; caller owns its lifecycle. */
|
|
98
110
|
signal?: AbortSignal;
|
|
99
|
-
/**
|
|
111
|
+
/**
|
|
112
|
+
* Optional cancellable request timeout used when `signal` is omitted.
|
|
113
|
+
* Defaults to {@link DEFAULT_OPENAI_COMPATIBLE_DISCOVERY_TIMEOUT_MS} so a
|
|
114
|
+
* stalled endpoint can never hang discovery indefinitely.
|
|
115
|
+
*/
|
|
100
116
|
timeoutMs?: number;
|
|
101
117
|
/** Optional fetch implementation override for testing/custom runtimes. */
|
|
102
118
|
fetch?: FetchImpl;
|
|
@@ -164,9 +180,10 @@ export async function fetchOpenAICompatibleModels<TApi extends Api>(
|
|
|
164
180
|
const payload =
|
|
165
181
|
options.signal !== undefined
|
|
166
182
|
? await fetchPayload(options.signal)
|
|
167
|
-
:
|
|
168
|
-
|
|
169
|
-
|
|
183
|
+
: await withOpenAICompatibleDiscoveryTimeout(
|
|
184
|
+
options.timeoutMs ?? DEFAULT_OPENAI_COMPATIBLE_DISCOVERY_TIMEOUT_MS,
|
|
185
|
+
fetchPayload,
|
|
186
|
+
);
|
|
170
187
|
if (payload === null) {
|
|
171
188
|
return null;
|
|
172
189
|
}
|
package/src/identity/classify.ts
CHANGED
|
@@ -122,16 +122,31 @@ export const parseAnthropicModel = parser((modelId): AnthropicModel | null => {
|
|
|
122
122
|
return { family: "anthropic", kind: kind as AnthropicKind, version };
|
|
123
123
|
});
|
|
124
124
|
|
|
125
|
+
/**
|
|
126
|
+
* Rolling OpenAI aliases inherit wire capabilities from their current default
|
|
127
|
+
* snapshots. Keep this map aligned with the model docs when an alias advances.
|
|
128
|
+
*/
|
|
129
|
+
const OPENAI_ALIAS_VERSIONS: Readonly<Record<string, string>> = {
|
|
130
|
+
"daybreak-blue-latest": "5.6",
|
|
131
|
+
"gpt-daybreak-blue-latest": "5.6",
|
|
132
|
+
"daybreak-red-latest": "5.6",
|
|
133
|
+
"gpt-daybreak-red-latest": "5.6",
|
|
134
|
+
};
|
|
135
|
+
|
|
125
136
|
export const parseOpenAIModel = parser((modelId): OpenAIModel | null => {
|
|
126
|
-
const
|
|
127
|
-
|
|
137
|
+
const aliasVersion = OPENAI_ALIAS_VERSIONS[modelId];
|
|
138
|
+
const match = aliasVersion
|
|
139
|
+
? null
|
|
140
|
+
: /gpt-(\d+(?:\.\d+){0,2})(?:-(codex-spark|codex-mini|codex-max|codex|mini|max|nano))?\b/.exec(modelId);
|
|
141
|
+
const versionInput = aliasVersion ?? match?.[1];
|
|
142
|
+
if (!versionInput) {
|
|
128
143
|
return null;
|
|
129
144
|
}
|
|
130
|
-
const version = parseSemVer(
|
|
145
|
+
const version = parseSemVer(versionInput);
|
|
131
146
|
if (!version) {
|
|
132
147
|
return null;
|
|
133
148
|
}
|
|
134
|
-
return { family: "openai", variant: (match[2] as OpenAIVariant | undefined) ?? "base", version };
|
|
149
|
+
return { family: "openai", variant: (match?.[2] as OpenAIVariant | undefined) ?? "base", version };
|
|
135
150
|
});
|
|
136
151
|
|
|
137
152
|
/**
|
|
@@ -143,7 +158,7 @@ export const parseOpenAIModel = parser((modelId): OpenAIModel | null => {
|
|
|
143
158
|
* `parseKnownModel`.
|
|
144
159
|
*/
|
|
145
160
|
export const parseGlmModel = parser((modelId): GlmModel | null => {
|
|
146
|
-
const match = /glm-(\d{1,2}(?:\.\d+)?)(v)?(?:-(air|turbo|flashx|flash|preview))?\b
|
|
161
|
+
const match = /glm-(\d{1,2}(?:\.\d+)?)(v)?(?:-(air|turbo|flashx|flash|preview))?\b/i.exec(modelId);
|
|
147
162
|
if (!match) {
|
|
148
163
|
return null;
|
|
149
164
|
}
|
|
@@ -153,8 +168,8 @@ export const parseGlmModel = parser((modelId): GlmModel | null => {
|
|
|
153
168
|
}
|
|
154
169
|
return {
|
|
155
170
|
family: "glm",
|
|
156
|
-
variant: (match[3] as GlmVariant | undefined) ?? "base",
|
|
157
|
-
vision: match[2] === "v",
|
|
171
|
+
variant: (match[3]?.toLowerCase() as GlmVariant | undefined) ?? "base",
|
|
172
|
+
vision: match[2]?.toLowerCase() === "v",
|
|
158
173
|
version,
|
|
159
174
|
};
|
|
160
175
|
});
|
|
@@ -181,7 +196,9 @@ function createSemVer(major: number, minor: number, patch = 0): SemVer {
|
|
|
181
196
|
return { major, minor, patch };
|
|
182
197
|
}
|
|
183
198
|
|
|
184
|
-
//
|
|
199
|
+
// Fast path for the common 1–2 component versions; anything the table misses
|
|
200
|
+
// (large minors, 3-part versions) parses dynamically below so no future
|
|
201
|
+
// version ever classifies as unknown (the failure class #8256 fixed).
|
|
185
202
|
const precomputeTable: Record<string, SemVer> = {};
|
|
186
203
|
for (let major = 0; major <= 9; major++) {
|
|
187
204
|
for (let minor = 0; minor <= 10; minor++) {
|
|
@@ -192,8 +209,14 @@ for (let major = 0; major <= 9; major++) {
|
|
|
192
209
|
precomputeTable[`${major}`] = createSemVer(major, 0, 0);
|
|
193
210
|
}
|
|
194
211
|
|
|
212
|
+
const SEMVER_PATTERN = /^(\d{1,2})(?:[.-](\d{1,2}))?(?:[.-](\d{1,2}))?$/;
|
|
213
|
+
|
|
195
214
|
export function parseSemVer(version: string): SemVer | null {
|
|
196
|
-
|
|
215
|
+
const hit = precomputeTable[version];
|
|
216
|
+
if (hit) return hit;
|
|
217
|
+
const match = SEMVER_PATTERN.exec(version);
|
|
218
|
+
if (!match) return null;
|
|
219
|
+
return createSemVer(Number(match[1]), Number(match[2] ?? 0), Number(match[3] ?? 0));
|
|
197
220
|
}
|
|
198
221
|
|
|
199
222
|
export function semverGte(left: SemVer | string, right: SemVer | string): boolean {
|
package/src/identity/family.ts
CHANGED
|
@@ -85,9 +85,11 @@ export const isDeepseekModelIdOrName = memo((value: string): boolean => {
|
|
|
85
85
|
|
|
86
86
|
/**
|
|
87
87
|
* DeepSeek V4 Flash SKU in any host/namespace form (`deepseek-v4-flash`, dated
|
|
88
|
-
* `deepseek-v4-flash-0731`, `deepseek-ai/DeepSeek-V4-Flash`).
|
|
89
|
-
*
|
|
90
|
-
*
|
|
88
|
+
* `deepseek-v4-flash-0731`, `deepseek-ai/DeepSeek-V4-Flash`). Both V4 SKUs
|
|
89
|
+
* (Flash and Pro) accept the `low` reasoning_effort tier; this predicate keeps
|
|
90
|
+
* Flash distinguishable from Pro where a host quirk splits them (e.g.
|
|
91
|
+
* OpenRouter exposes `low` on Flash but only `high` on non-Flash V4).
|
|
92
|
+
* See https://api-docs.deepseek.com/api/create-chat-completion.
|
|
91
93
|
*/
|
|
92
94
|
export const isDeepseekV4FlashModelId = memo((modelId: string): boolean => {
|
|
93
95
|
return bareModelId(modelId).toLowerCase().includes("deepseek-v4-flash");
|
|
@@ -98,6 +100,16 @@ export const isMimoModelIdOrName = memo((value: string): boolean => {
|
|
|
98
100
|
return value.toLowerCase().includes("mimo");
|
|
99
101
|
});
|
|
100
102
|
|
|
103
|
+
/** Gemini family ids in any namespace form (`gemini-*`, `google/gemini-*`, `openrouter/google/gemini-…`). */
|
|
104
|
+
export const isGeminiModelId = memo((modelId: string): boolean => {
|
|
105
|
+
return /(^|\/)gemini[-.]?/i.test(modelId);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
/** Grok family ids across namespace and delimiter forms (`grok-*`, `cursor-grok-*`, `xai/grok-*`). */
|
|
109
|
+
export const isGrokModelId = memo((modelId: string): boolean => {
|
|
110
|
+
return /(?:^|[./_-])grok(?:[-.]|$)/i.test(modelId);
|
|
111
|
+
});
|
|
112
|
+
|
|
101
113
|
const GROK_EFFORT_CAPABLE_PREFIXES = ["grok-3-mini", "grok-4.20-multi-agent", "grok-4.3", "grok-4.5"] as const;
|
|
102
114
|
|
|
103
115
|
/**
|
|
@@ -259,12 +271,14 @@ export const modelFamilyToken = memo((modelId: string): string => {
|
|
|
259
271
|
const parsed = parseKnownModel(modelId);
|
|
260
272
|
if (parsed.family !== "unknown") return parsed.family;
|
|
261
273
|
if (isClaudeModelId(modelId) || isAnthropicNamespacedModelId(modelId)) return "anthropic";
|
|
274
|
+
if (isGeminiModelId(modelId)) return "gemini";
|
|
275
|
+
if (isGrokModelId(modelId)) return "grok";
|
|
276
|
+
if (isDeepseekModelIdOrName(modelId)) return "deepseek";
|
|
262
277
|
if (isOpenAIModelId(modelId)) return "openai";
|
|
263
278
|
if (isKimiModelId(modelId)) return "kimi";
|
|
264
279
|
if (isQwenModelId(modelId)) return "qwen";
|
|
265
280
|
if (isMinimaxM2FamilyModelId(modelId) || isMinimaxM3FamilyModelId(modelId)) return "minimax";
|
|
266
281
|
if (isOpenAIGptOssModelId(modelId)) return "gpt-oss";
|
|
267
|
-
if (isDeepseekModelIdOrName(modelId)) return "deepseek";
|
|
268
282
|
if (isMimoModelIdOrName(modelId)) return "mimo";
|
|
269
283
|
if (isGemmaModelId(modelId)) return "gemma";
|
|
270
284
|
if (parseGlmModel(bareModelId(modelId))) return "glm";
|
package/src/model-manager.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { buildModel } from "./build";
|
|
2
2
|
import { readModelCache, writeModelCache } from "./model-cache";
|
|
3
3
|
import { type GeneratedProvider, getBundledModels } from "./models";
|
|
4
|
-
import type { Api, Model, ModelSpec, Provider } from "./types";
|
|
4
|
+
import type { Api, Model, ModelCost, ModelSpec, Provider, TokenCost } from "./types";
|
|
5
5
|
import { isRecord } from "./utils";
|
|
6
6
|
import { collapseBuiltModelVariants } from "./variant-collapse";
|
|
7
7
|
|
|
@@ -510,6 +510,7 @@ function mergeDynamicModel<TApi extends Api>(existingModel: Model<TApi>, dynamic
|
|
|
510
510
|
const reasoning = dynamicReasoningAuthoritative
|
|
511
511
|
? dynamicModel.reasoning
|
|
512
512
|
: existingModel.reasoning || dynamicModel.reasoning;
|
|
513
|
+
const longContextCost = dynamicModel.cost.longContext ?? existingModel.cost.longContext;
|
|
513
514
|
// Re-build from spec stage: sparse compat comes from `compatConfig` (the
|
|
514
515
|
// verbatim override vocabulary), never the resolved `compat` record.
|
|
515
516
|
return buildModel({
|
|
@@ -523,6 +524,7 @@ function mergeDynamicModel<TApi extends Api>(existingModel: Model<TApi>, dynamic
|
|
|
523
524
|
output: preferDiscoveryCost(dynamicModel.cost.output, existingModel.cost.output),
|
|
524
525
|
cacheRead: preferDiscoveryCost(dynamicModel.cost.cacheRead, existingModel.cost.cacheRead),
|
|
525
526
|
cacheWrite: preferDiscoveryCost(dynamicModel.cost.cacheWrite, existingModel.cost.cacheWrite),
|
|
527
|
+
...(longContextCost ? { longContext: longContextCost } : {}),
|
|
526
528
|
},
|
|
527
529
|
contextWindow: preferDiscoveryLimit(dynamicModel.contextWindow, existingModel.contextWindow),
|
|
528
530
|
maxTokens: preferDiscoveryLimit(dynamicModel.maxTokens, existingModel.maxTokens),
|
|
@@ -640,7 +642,7 @@ function isModelInputArray(value: unknown): value is ("text" | "image")[] {
|
|
|
640
642
|
return true;
|
|
641
643
|
}
|
|
642
644
|
|
|
643
|
-
function
|
|
645
|
+
function isTokenCost(value: unknown): value is TokenCost {
|
|
644
646
|
if (!isRecord(value)) {
|
|
645
647
|
return false;
|
|
646
648
|
}
|
|
@@ -670,3 +672,12 @@ function isModelCost(value: unknown): value is Model<Api>["cost"] {
|
|
|
670
672
|
}
|
|
671
673
|
return true;
|
|
672
674
|
}
|
|
675
|
+
|
|
676
|
+
function isModelCost(value: unknown): value is ModelCost {
|
|
677
|
+
if (!isTokenCost(value)) return false;
|
|
678
|
+
const longContext = (value as TokenCost & { longContext?: unknown }).longContext;
|
|
679
|
+
if (longContext === undefined) return true;
|
|
680
|
+
if (!isTokenCost(longContext) || !isRecord(longContext)) return false;
|
|
681
|
+
const threshold = longContext.inputThreshold;
|
|
682
|
+
return typeof threshold === "number" && threshold > 0 && threshold < Infinity;
|
|
683
|
+
}
|