@oh-my-pi/pi-catalog 18.10.3 → 18.11.0

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.
Files changed (43) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/README.md +4 -2
  3. package/dist/types/build.d.ts +3 -2
  4. package/dist/types/compat/auth-ids.d.ts +2 -2
  5. package/dist/types/compat/provider-ids.d.ts +1 -1
  6. package/dist/types/models.d.ts +16 -1
  7. package/dist/types/pricing.d.ts +37 -1
  8. package/dist/types/provider-models/openai-compat.d.ts +18 -0
  9. package/dist/types/types.d.ts +0 -2
  10. package/package.json +4 -4
  11. package/src/build.ts +138 -78
  12. package/src/compat/auth-ids.ts +2 -0
  13. package/src/compat/axes.ts +21 -0
  14. package/src/compat/provider-ids.ts +1 -0
  15. package/src/compat/resolve.ts +15 -2
  16. package/src/compat/rules/README.md +5 -1
  17. package/src/compat/rules/auth/_order.kdl +1 -1
  18. package/src/compat/rules/auth/sambanova.kdl +9 -0
  19. package/src/compat/rules/classes/anthropic.kdl +3 -0
  20. package/src/compat/rules/classes/gemini.kdl +28 -0
  21. package/src/compat/rules/classes/meta.kdl +4 -2
  22. package/src/compat/rules/classes/minimax.kdl +1 -1
  23. package/src/compat/rules/providers/cursor.kdl +20 -0
  24. package/src/compat/rules/providers/devin.kdl +110 -5
  25. package/src/compat/rules/providers/local.kdl +8 -1
  26. package/src/compat/rules/providers/muse-code.kdl +40 -0
  27. package/src/compat/rules/providers/opencode-go.kdl +31 -0
  28. package/src/compat/rules/providers/sambanova.kdl +60 -0
  29. package/src/compat/rules/runtime/behavior.kdl +4 -0
  30. package/src/compat/rules/taxonomy/_collapse.kdl +11 -0
  31. package/src/compat/rules/taxonomy/gemma.kdl +1 -0
  32. package/src/compat/rules/taxonomy/meta.kdl +4 -0
  33. package/src/compat/rules.json +1 -1
  34. package/src/discovery/codex.ts +0 -4
  35. package/src/discovery/cursor.ts +25 -11
  36. package/src/discovery/devin.ts +5 -3
  37. package/src/models.json +1 -1
  38. package/src/models.ts +26 -12
  39. package/src/pricing.ts +123 -12
  40. package/src/provider-models/cache-provider-id.ts +1 -1
  41. package/src/provider-models/descriptors.ts +2 -0
  42. package/src/provider-models/openai-compat.ts +169 -8
  43. package/src/types.ts +0 -2
package/CHANGELOG.md CHANGED
@@ -2,6 +2,51 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [18.11.0] - 2026-10-14
6
+
7
+ ### Added
8
+
9
+ - Added `local/nemotron-streaming-en-0.6b` for local streaming English dictation ([#15759](https://github.com/can1357/oh-my-pi/pull/15759) by [@daaximus](https://github.com/daaximus)).
10
+ - Added SambaNova Cloud (`sambanova`) as a provider: `/login sambanova` with an API key, and the live `/v1/models` roster with its prices, reasoning models, and Gemma 4 image input ([#14986](https://github.com/can1357/oh-my-pi/pull/14986) by [@rjuez00](https://github.com/rjuez00))
11
+
12
+ ### Changed
13
+
14
+ - OpenCode Go requests now go to the current `https://opencode.ai/inference/go/v1` endpoint instead of the legacy `/zen/go` one ([#15722](https://github.com/can1357/oh-my-pi/issues/15722))
15
+
16
+ ### Fixed
17
+
18
+ - Fixed OpenCode Go's `deepseek-flash` alias showing $0, unknown limits, and no reasoning instead of the V4.1 Flash rate card and capabilities ([#15357](https://github.com/can1357/oh-my-pi/issues/15357))
19
+ - Fixed OpenCode Go's Gemini 4 Argon Contributor failing with an unsupported protocol and thinking level; it now uses the native Gemini endpoint ([#15722](https://github.com/can1357/oh-my-pi/issues/15722))
20
+
21
+ ## [18.10.4] - 2026-10-13
22
+
23
+ ### Added
24
+
25
+ - Added Muse Code's subscription-only Muse Wonder Full and Wonder contributor tiers with the `max` effort, recording API-equivalent cost instead of $0: Wonder Full at Muse Wonder's $4 input / $0.20 cached / $20 output per 1M tokens, contributor tiers at the measured $1.60 / $0.08 / $8 ([#15522](https://github.com/can1357/oh-my-pi/pull/15522) by [@eggpeat](https://github.com/eggpeat))
26
+ - Added the `classifier-cot-extraction` rule axis, set for Claude, marking models whose classifier refuses earlier reasoning quoted back into their input ([#15436](https://github.com/can1357/oh-my-pi/pull/15436) by [@will-bogusz](https://github.com/will-bogusz))
27
+ - Added Gemini 4 Argon pricing on Cursor ($2 input / $0.10 cached / $10 output per 1M tokens) and Google Antigravity, which now uses Google's first-party rate instead of showing $0 ([#15620](https://github.com/can1357/oh-my-pi/pull/15620) by [@eggpeat](https://github.com/eggpeat))
28
+ - Added scheduled list-price changes to `cost-patch`, as `cost-fallback` already allowed: a dated `effective-rates` card sets the price from its date, and each request is priced at the card in force when it was made, even on models loaded before the change, on rebuilt models, and on Cursor lanes priced from a sibling's card. A malformed dated card now fails the model build instead of silently keeping the old price. `getEffectiveTokenCost` returns that card for price displays, and `hasRecurringTariff` tells these dated-only schedules apart from peak/off-peak tariffs, which alone show a status-line arrow ([#15620](https://github.com/can1357/oh-my-pi/pull/15620) by [@eggpeat](https://github.com/eggpeat))
29
+
30
+ ### Changed
31
+
32
+ - Devin models with a long-context price tier (GPT-5.4 through 5.6 standard lanes, GPT-6 through 6.1, Gemini Pro, and Grok 4.5 through 4.7) now default to a 272K or 200K context window, where that pricing starts; run `/extended-context on` to use the full window at long-context rates ([#14891](https://github.com/can1357/oh-my-pi/pull/14891) by [@eggpeat](https://github.com/eggpeat)).
33
+ - Devin SWE-2, SWE-1.7, and GLM-5.2 fallback pricing no longer charges cache writes at the input rate, since Devin publishes no cache-write rate for them ([#14891](https://github.com/can1357/oh-my-pi/pull/14891) by [@eggpeat](https://github.com/eggpeat)).
34
+ - Google Antigravity now lists Gemini 4 Argon once, with selectable low, medium, and high thinking levels, instead of a separate entry per thinking tier ([#15620](https://github.com/can1357/oh-my-pi/pull/15620) by [@eggpeat](https://github.com/eggpeat))
35
+ - Gemini 4 Argon on Google, Vertex, Google Antigravity, and Cursor moves to Google's $4 input / $0.20 cached / $20 output list price on 2026-12-01 (midnight US Pacific), including requests on models loaded before then, without a catalog update ([#15620](https://github.com/can1357/oh-my-pi/pull/15620) by [@eggpeat](https://github.com/eggpeat))
36
+ - Devin SWE-2's promotional fallback price now ends on 2027-01-01 for requests on models loaded before then too ([#15620](https://github.com/can1357/oh-my-pi/pull/15620) by [@eggpeat](https://github.com/eggpeat))
37
+
38
+ ### Fixed
39
+
40
+ - Fixed Devin Claude and GPT-5.6 through GPT-6 models pricing prompt-cache writes at $0; they now use Devin's published rate of 1.25x input, and Adaptive uses its input rate ([#14891](https://github.com/can1357/oh-my-pi/pull/14891) by [@eggpeat](https://github.com/eggpeat)).
41
+ - Fixed Devin GPT-5.4 through GPT-6.1, Gemini Pro, and Grok 4.5 through 4.7 models missing the long-context price tiers Devin publishes above 272K and 200K prompt tokens ([#14891](https://github.com/can1357/oh-my-pi/pull/14891) by [@eggpeat](https://github.com/eggpeat)).
42
+ - Fixed Devin Fusion pairings ignoring their lead model's rules, including its cache-write rate and long-context tier ([#14891](https://github.com/can1357/oh-my-pi/pull/14891) by [@eggpeat](https://github.com/eggpeat)).
43
+ - Fixed Kilo and NanoGPT models showing stale or missing prices (for example Gemini 4 Argon at $4/$20 instead of $2/$10, and many models at $0); prices now come from each gateway's live model list ([#15623](https://github.com/can1357/oh-my-pi/pull/15623) by [@eggpeat](https://github.com/eggpeat))
44
+ - Fixed NanoGPT models showing their raw id as the name and no context window (disabling auto-compaction) when the bundled catalog lacked them; names, context windows and output limits now come from NanoGPT's live model list ([#15623](https://github.com/can1357/oh-my-pi/pull/15623) by [@eggpeat](https://github.com/eggpeat))
45
+
46
+ ### Removed
47
+
48
+ - Removed the Codex `toolMode` model field (`code_mode_only`); Codex discovery no longer maps `tool_mode`
49
+
5
50
  ## [18.10.3] - 2026-10-12
6
51
 
7
52
  ### Breaking Changes
package/README.md CHANGED
@@ -47,12 +47,14 @@ The `models` subpath (also exported from the root) provides timestamp-aware pric
47
47
  | `calculateCost(model, usage, timestamp?)` | Updates and returns `usage.cost` using `model.cost`. |
48
48
  | `calculateUsageCost(cost, usage, timestamp?)` | Updates and returns `usage.cost` using a `ModelCost`. |
49
49
  | `calculateUncachedInputCost(cost, promptInputTokens, timestamp?)` | Returns the cost of a fully uncached prompt. |
50
- | `getTimeBasedPricingPeriod(cost, timestamp?)` | Returns `"peak"`, `"off-peak"`, or `undefined` without a schedule. |
50
+ | `getEffectiveTokenCost(cost, timestamp?)` | Returns the base card in force: the latest due effective rate, else the row's own legs. |
51
+ | `getTimeBasedPricingPeriod(cost, timestamp?)` | Returns `"peak"`, `"off-peak"`, or `undefined` without a recurring tariff. |
52
+ | `hasRecurringTariff(schedule)` | Whether a `TimeBasedCost` carries a peak/off-peak tariff rather than only dated rate cards. |
51
53
  | `getNextTimeBasedPricingTransition(cost, timestamp?)` | Returns the next actual peak/off-peak change strictly after the timestamp, or `undefined` if none exists. |
52
54
 
53
55
  Timestamps are Unix milliseconds; omitted timestamps use the current time for scheduled pricing. Flat token prices are unaffected. Pricing selects the latest applicable effective rate card, then its long-context tier, then the peak/off-peak multiplier. A transition query concerns the recurring tariff, not dated rate-card changes.
54
56
 
55
- `ModelCost.timeBased` is optional typed metadata (`TimeBasedCost`): `offPeakMultiplier`, `peakWindows` (UTC `weekdays`, Sunday = 0, and start-inclusive/end-exclusive `startMinute`/`endMinute`), and optional `effectiveRates`. Each effective rate is a complete `TokenCost` with an `effectiveFrom` Unix-millisecond timestamp and optional `longContext` tier, replacing the base card from that instant.
57
+ `ModelCost.timeBased` is optional typed metadata (`TimeBasedCost`): `offPeakMultiplier`, `peakWindows` (UTC `weekdays`, Sunday = 0, and start-inclusive/end-exclusive `startMinute`/`endMinute`), and optional `effectiveRates`. Each effective rate is a complete `TokenCost` with an `effectiveFrom` Unix-millisecond timestamp and optional `longContext` tier, replacing the base card from that instant. A schedule with no peak windows at a 1x off-peak multiplier has no recurring tariff, only dated rate cards: `cost-patch`/`cost-fallback` `effective-rates` compile to one, so a row built before a scheduled price change still prices later requests at the new card.
56
58
 
57
59
  Pass the request-start timestamp when estimating request usage, then preserve the resulting monetary amounts rather than repricing history at display time. OMP does this using the assistant message timestamp; it is an estimation convention, not a claim about server billing across boundaries. Prefer monetary costs reported by a provider when available.
58
60
 
@@ -9,8 +9,9 @@
9
9
  import { type Api, type Model, type ModelSpec } from "./types.js";
10
10
  /**
11
11
  * Applies reviewed catalog-data value corrections (`cost-patch`,
12
- * `cache-read-at-input-rate`, `limits-patch`, `long-context-cost`,
13
- * `context-window-floor`, `input-modalities`) onto an upstream-sourced spec. Applied by
12
+ * `cache-read-at-input-rate`, `cache-write-rate`, `limits-patch`,
13
+ * `long-context-cost`, `context-window-floor`, `input-modalities`) onto an
14
+ * upstream-sourced spec. Applied by
14
15
  * `buildModel` to every upstream-sourced spec; user-authored overrides are
15
16
  * recomposed after building by the override applicators, so explicit user
16
17
  * limits and pricing still win.
@@ -1,4 +1,4 @@
1
1
  /** Every provider with an `auth/<id>.kdl` policy. */
2
- export type AuthProviderId = "abliteration" | "aiand" | "aimlapi" | "alibaba-coding-plan" | "alibaba-token-plan" | "amazon-bedrock" | "anthropic" | "apple" | "azure" | "baseten" | "bedrock-mantle" | "cerebras" | "charm-hyper" | "cline-pass" | "cloudflare-ai-gateway" | "cloudflare-workers-ai" | "commandcode" | "coralbricks" | "coreweave" | "cursor" | "deepinfra" | "deepseek" | "devin" | "exa" | "factory-droid" | "firepass" | "fireworks" | "github-copilot" | "gitlab-duo" | "gitlab-duo-agent" | "gmi-cloud" | "google" | "google-antigravity" | "google-gemini-cli" | "google-vertex" | "groq" | "helmcode" | "huggingface" | "kagi" | "kilo" | "kimi-code" | "litellm" | "llama.cpp" | "lm-studio" | "local" | "meta" | "minimax" | "minimax-code" | "minimax-code-cn" | "mistral" | "moonshot" | "muse-code" | "nanogpt" | "novita" | "nvidia" | "ollama" | "ollama-cloud" | "openai" | "openai-codex" | "openai-codex-device" | "opencode-go" | "opencode-zen" | "openrouter" | "parallel" | "perplexity" | "qianfan" | "qwen-portal" | "sakana" | "siliconflow" | "siliconflow-cn" | "singularityapi-dev" | "singularityapi-tech" | "snowflake" | "stencil" | "stepfun" | "synthetic" | "tavily" | "together" | "typesafe" | "umans" | "venice" | "vercel-ai-gateway" | "vllm" | "wafer-serverless" | "web" | "xai" | "xai-oauth" | "xiaomi" | "xiaomi-token-plan-ams" | "xiaomi-token-plan-cn" | "xiaomi-token-plan-sgp" | "yolo-auto" | "zai" | "zai-coding-plan" | "zenmux" | "zhipu-coding-plan";
2
+ export type AuthProviderId = "abliteration" | "aiand" | "aimlapi" | "alibaba-coding-plan" | "alibaba-token-plan" | "amazon-bedrock" | "anthropic" | "apple" | "azure" | "baseten" | "bedrock-mantle" | "cerebras" | "charm-hyper" | "cline-pass" | "cloudflare-ai-gateway" | "cloudflare-workers-ai" | "commandcode" | "coralbricks" | "coreweave" | "cursor" | "deepinfra" | "deepseek" | "devin" | "exa" | "factory-droid" | "firepass" | "fireworks" | "github-copilot" | "gitlab-duo" | "gitlab-duo-agent" | "gmi-cloud" | "google" | "google-antigravity" | "google-gemini-cli" | "google-vertex" | "groq" | "helmcode" | "huggingface" | "kagi" | "kilo" | "kimi-code" | "litellm" | "llama.cpp" | "lm-studio" | "local" | "meta" | "minimax" | "minimax-code" | "minimax-code-cn" | "mistral" | "moonshot" | "muse-code" | "nanogpt" | "novita" | "nvidia" | "ollama" | "ollama-cloud" | "openai" | "openai-codex" | "openai-codex-device" | "opencode-go" | "opencode-zen" | "openrouter" | "parallel" | "perplexity" | "qianfan" | "qwen-portal" | "sakana" | "sambanova" | "siliconflow" | "siliconflow-cn" | "singularityapi-dev" | "singularityapi-tech" | "snowflake" | "stencil" | "stepfun" | "synthetic" | "tavily" | "together" | "typesafe" | "umans" | "venice" | "vercel-ai-gateway" | "vllm" | "wafer-serverless" | "web" | "xai" | "xai-oauth" | "xiaomi" | "xiaomi-token-plan-ams" | "xiaomi-token-plan-cn" | "xiaomi-token-plan-sgp" | "yolo-auto" | "zai" | "zai-coding-plan" | "zenmux" | "zhipu-coding-plan";
3
3
  /** Providers whose policy declares a `login` flow (the `/login` roster). */
4
- export type LoginProviderId = "abliteration" | "aiand" | "alibaba-coding-plan" | "alibaba-token-plan" | "anthropic" | "baseten" | "cerebras" | "charm-hyper" | "cline-pass" | "cloudflare-ai-gateway" | "cloudflare-workers-ai" | "commandcode" | "coralbricks" | "coreweave" | "cursor" | "deepinfra" | "deepseek" | "devin" | "exa" | "factory-droid" | "firepass" | "fireworks" | "github-copilot" | "gitlab-duo" | "gitlab-duo-agent" | "gmi-cloud" | "google-antigravity" | "google-gemini-cli" | "helmcode" | "huggingface" | "kagi" | "kilo" | "kimi-code" | "litellm" | "llama.cpp" | "lm-studio" | "meta" | "minimax-code" | "minimax-code-cn" | "moonshot" | "muse-code" | "nanogpt" | "novita" | "nvidia" | "ollama" | "ollama-cloud" | "openai-codex" | "openai-codex-device" | "opencode-go" | "opencode-zen" | "openrouter" | "parallel" | "perplexity" | "qianfan" | "qwen-portal" | "sakana" | "siliconflow" | "siliconflow-cn" | "singularityapi-dev" | "singularityapi-tech" | "snowflake" | "stencil" | "stepfun" | "synthetic" | "tavily" | "together" | "typesafe" | "umans" | "venice" | "vercel-ai-gateway" | "vllm" | "wafer-serverless" | "xai" | "xai-oauth" | "xiaomi" | "xiaomi-token-plan-ams" | "xiaomi-token-plan-cn" | "xiaomi-token-plan-sgp" | "yolo-auto" | "zai" | "zai-coding-plan" | "zenmux" | "zhipu-coding-plan";
4
+ export type LoginProviderId = "abliteration" | "aiand" | "alibaba-coding-plan" | "alibaba-token-plan" | "anthropic" | "baseten" | "cerebras" | "charm-hyper" | "cline-pass" | "cloudflare-ai-gateway" | "cloudflare-workers-ai" | "commandcode" | "coralbricks" | "coreweave" | "cursor" | "deepinfra" | "deepseek" | "devin" | "exa" | "factory-droid" | "firepass" | "fireworks" | "github-copilot" | "gitlab-duo" | "gitlab-duo-agent" | "gmi-cloud" | "google-antigravity" | "google-gemini-cli" | "helmcode" | "huggingface" | "kagi" | "kilo" | "kimi-code" | "litellm" | "llama.cpp" | "lm-studio" | "meta" | "minimax-code" | "minimax-code-cn" | "moonshot" | "muse-code" | "nanogpt" | "novita" | "nvidia" | "ollama" | "ollama-cloud" | "openai-codex" | "openai-codex-device" | "opencode-go" | "opencode-zen" | "openrouter" | "parallel" | "perplexity" | "qianfan" | "qwen-portal" | "sakana" | "sambanova" | "siliconflow" | "siliconflow-cn" | "singularityapi-dev" | "singularityapi-tech" | "snowflake" | "stencil" | "stepfun" | "synthetic" | "tavily" | "together" | "typesafe" | "umans" | "venice" | "vercel-ai-gateway" | "vllm" | "wafer-serverless" | "xai" | "xai-oauth" | "xiaomi" | "xiaomi-token-plan-ams" | "xiaomi-token-plan-cn" | "xiaomi-token-plan-sgp" | "yolo-auto" | "zai" | "zai-coding-plan" | "zenmux" | "zhipu-coding-plan";
@@ -1,2 +1,2 @@
1
1
  /** Every chat-model provider with a catalog entry (`default-model` in its provider rules). */
2
- export type KnownProvider = "abliteration" | "aiand" | "aimlapi" | "alibaba-coding-plan" | "alibaba-token-plan" | "amazon-bedrock" | "anthropic" | "apple" | "azure" | "baseten" | "bedrock-mantle" | "cerebras" | "charm-hyper" | "cline-pass" | "cloudflare-ai-gateway" | "cloudflare-workers-ai" | "commandcode" | "coralbricks" | "coreweave" | "cursor" | "deepinfra" | "deepseek" | "devin" | "factory-droid" | "firepass" | "fireworks" | "github-copilot" | "gitlab-duo" | "gitlab-duo-agent" | "gmi-cloud" | "google" | "google-antigravity" | "google-gemini-cli" | "google-vertex" | "groq" | "helmcode" | "huggingface" | "kilo" | "kimi-code" | "litellm" | "lm-studio" | "local" | "meta" | "minimax" | "minimax-code" | "minimax-code-cn" | "mistral" | "moonshot" | "muse-code" | "nanogpt" | "novita" | "nvidia" | "ollama" | "ollama-cloud" | "openai" | "openai-codex" | "opencode-go" | "opencode-zen" | "openrouter" | "qianfan" | "qwen-portal" | "sakana" | "siliconflow" | "siliconflow-cn" | "singularityapi-dev" | "singularityapi-tech" | "snowflake" | "stepfun" | "synthetic" | "together" | "typesafe" | "umans" | "venice" | "vercel-ai-gateway" | "vllm" | "wafer-serverless" | "web" | "xai" | "xai-oauth" | "xiaomi" | "xiaomi-token-plan-ams" | "xiaomi-token-plan-cn" | "xiaomi-token-plan-sgp" | "yolo-auto" | "zai" | "zenmux" | "zhipu-coding-plan";
2
+ export type KnownProvider = "abliteration" | "aiand" | "aimlapi" | "alibaba-coding-plan" | "alibaba-token-plan" | "amazon-bedrock" | "anthropic" | "apple" | "azure" | "baseten" | "bedrock-mantle" | "cerebras" | "charm-hyper" | "cline-pass" | "cloudflare-ai-gateway" | "cloudflare-workers-ai" | "commandcode" | "coralbricks" | "coreweave" | "cursor" | "deepinfra" | "deepseek" | "devin" | "factory-droid" | "firepass" | "fireworks" | "github-copilot" | "gitlab-duo" | "gitlab-duo-agent" | "gmi-cloud" | "google" | "google-antigravity" | "google-gemini-cli" | "google-vertex" | "groq" | "helmcode" | "huggingface" | "kilo" | "kimi-code" | "litellm" | "lm-studio" | "local" | "meta" | "minimax" | "minimax-code" | "minimax-code-cn" | "mistral" | "moonshot" | "muse-code" | "nanogpt" | "novita" | "nvidia" | "ollama" | "ollama-cloud" | "openai" | "openai-codex" | "opencode-go" | "opencode-zen" | "openrouter" | "qianfan" | "qwen-portal" | "sakana" | "sambanova" | "siliconflow" | "siliconflow-cn" | "singularityapi-dev" | "singularityapi-tech" | "snowflake" | "stepfun" | "synthetic" | "together" | "typesafe" | "umans" | "venice" | "vercel-ai-gateway" | "vllm" | "wafer-serverless" | "web" | "xai" | "xai-oauth" | "xiaomi" | "xiaomi-token-plan-ams" | "xiaomi-token-plan-cn" | "xiaomi-token-plan-sgp" | "yolo-auto" | "zai" | "zenmux" | "zhipu-coding-plan";
@@ -1,5 +1,5 @@
1
1
  import MODELS from "./models.json";
2
- import type { Api, KnownProvider, Model, ModelCost, ModelPricingStatus, Usage } from "./types.js";
2
+ import type { Api, KnownProvider, Model, ModelCost, ModelPricingStatus, TimeBasedCost, TokenCost, Usage } from "./types.js";
3
3
  export type GeneratedProvider = keyof typeof MODELS;
4
4
  /** Whether `provider` has bundled rows in models.json (e.g. a provider id authored in KDL). */
5
5
  export declare function isGeneratedProvider(provider: string): provider is GeneratedProvider;
@@ -8,6 +8,21 @@ export declare function getBundledProviders(): KnownProvider[];
8
8
  export declare function getBundledModels(provider: GeneratedProvider): Model<Api>[];
9
9
  /** Resolve display semantics without mistaking an absent rate card for a free model. */
10
10
  export declare function getModelPricingStatus(model: Pick<Model<Api>, "cost" | "pricingStatus">): ModelPricingStatus;
11
+ /**
12
+ * The base token card in force at `timestamp` (Unix ms, default now): the
13
+ * latest due `timeBased` effective rate, else the row's own legs. Price
14
+ * displays read this rather than the scalar legs, which keep the card current
15
+ * when the row was built while bundles, caches, and long sessions outlive it.
16
+ * Applies no long-context tier or peak/off-peak multiplier.
17
+ */
18
+ export declare function getEffectiveTokenCost(cost: ModelCost, timestamp?: number): TokenCost;
19
+ /**
20
+ * Whether a schedule carries a recurring peak/off-peak tariff. A schedule with
21
+ * no peak windows at a 1x off-peak multiplier only carries dated rate cards
22
+ * (`cost-patch`/`cost-fallback` `effective-rates`): its price changes on a
23
+ * date, never by time of day.
24
+ */
25
+ export declare function hasRecurringTariff(schedule: TimeBasedCost): boolean;
11
26
  /** Return the recurring UTC tariff period, independently of its monetary multiplier. */
12
27
  export declare function getTimeBasedPricingPeriod(cost: ModelCost, timestamp?: number): "peak" | "off-peak" | undefined;
13
28
  /** Return the next actual peak/off-peak change strictly after the Unix-ms timestamp. */
@@ -1,5 +1,41 @@
1
- import type { TimeBasedCost } from "./types.js";
1
+ import type { EffectiveTokenCost, ModelCost, TimeBasedCost, TokenCost } from "./types.js";
2
2
  /** Validate serialized schedules before admitting cached model rows. */
3
3
  export declare function isTimeBasedCost(value: unknown): value is TimeBasedCost;
4
4
  /** KDL object children are named records; normalize them once while constructing the model. */
5
5
  export declare function materializeTimeBasedCost(value: unknown): TimeBasedCost;
6
+ /**
7
+ * The latest `effectiveRates` card of a materialized schedule due at
8
+ * `timestamp`, or undefined before the first one (or without a schedule).
9
+ */
10
+ export declare function dueEffectiveRate(schedule: TimeBasedCost | undefined, timestamp: number): EffectiveTokenCost | undefined;
11
+ /** A `cost-patch`/`cost-fallback` dated card: the legs it overwrites from `effectiveFrom` (Unix ms). */
12
+ export interface DatedCostCard extends Partial<TokenCost> {
13
+ effectiveFrom: number;
14
+ }
15
+ /**
16
+ * Parse the compiled `effective-rates` block of a `cost-patch` or
17
+ * `cost-fallback` payload into dated cards, ascending by date. Throws on a
18
+ * malformed card, so a typo in a rule fails the build instead of silently
19
+ * leaving the old price in force.
20
+ */
21
+ export declare function parseDatedCostCards(costAxis: unknown, axis: "cost-patch" | "cost-fallback"): DatedCostCard[];
22
+ /**
23
+ * Earliest instant a rate card can name (the `Date` range minimum). A leading
24
+ * card from here holds the price in force before the first dated change.
25
+ */
26
+ export declare const EARLIEST_EFFECTIVE_FROM = -8640000000000000;
27
+ /** A schedule of dated rate cards alone: no peak windows, no off-peak discount. */
28
+ export declare const NO_RECURRING_TARIFF: TimeBasedCost;
29
+ /**
30
+ * A flat card plus its dated overrides as a request-time `ModelCost`: the
31
+ * card due at `now` in the scalar legs, and every period (a leading card for
32
+ * the base price, then each dated card over the previous one) in a
33
+ * `timeBased` schedule without a recurring tariff. The plain card when no
34
+ * dated card exists.
35
+ */
36
+ export declare function datedCost(base: TokenCost, cards: readonly DatedCostCard[], now?: number): ModelCost;
37
+ /**
38
+ * Overwrite `cost` legs with the latest dated card due at `now`. Leaves
39
+ * `cost` unchanged before the first card.
40
+ */
41
+ export declare function applyDueDatedCostCard(cost: TokenCost, cards: readonly DatedCostCard[], now?: number): void;
@@ -100,6 +100,19 @@ export interface CerebrasModelManagerConfig {
100
100
  fetch?: FetchImpl;
101
101
  }
102
102
  export declare function cerebrasModelManagerOptions(config?: CerebrasModelManagerConfig): ModelManagerOptions<"openai-completions">;
103
+ /** SambaNova discovery inputs: `apiKey` enables the live roster; `baseUrl` defaults to SambaNova Cloud. */
104
+ export interface SambaNovaModelManagerConfig {
105
+ apiKey?: string;
106
+ baseUrl?: string;
107
+ fetch?: FetchImpl;
108
+ }
109
+ /**
110
+ * SambaNova Cloud's model manager. `/v1/models` requires a key, so a keyless
111
+ * manager serves only the bundled seed; with a key the live roster replaces
112
+ * it. Rows carry pricing and limits but no capability metadata: reasoning,
113
+ * thinking wire format, and image input come from `providers/sambanova.kdl`.
114
+ */
115
+ export declare function sambanovaModelManagerOptions(config?: SambaNovaModelManagerConfig): ModelManagerOptions<"openai-completions">;
103
116
  export interface HuggingfaceModelManagerConfig {
104
117
  apiKey?: string;
105
118
  baseUrl?: string;
@@ -655,6 +668,11 @@ export interface ModelsDevProviderDescriptor {
655
668
  }
656
669
  /** Generic mapper that converts models.dev data using provider descriptors. */
657
670
  export declare function mapModelsDevToModels(data: Record<string, unknown>, descriptors: readonly ModelsDevProviderDescriptor[]): ModelSpec<Api>[];
671
+ /**
672
+ * OpenCode Go endpoint for one request API. The model generator re-bases
673
+ * previous-snapshot rows with it so retained rows follow endpoint moves.
674
+ */
675
+ export declare function openCodeGoBaseUrlForApi(api: Api): string;
658
676
  /** Resolves the transport and endpoint for one Z.AI model catalog entry. */
659
677
  export declare function resolveZaiApi(modelId: string): {
660
678
  api: "anthropic-messages" | "openai-completions";
@@ -1401,8 +1401,6 @@ export interface Model<TApi extends Api = Api> {
1401
1401
  preferWebsockets?: boolean;
1402
1402
  /** Codex Responses Lite transport: send the lite marker and carry instructions/tools as input items (mirrors codex-rs `use_responses_lite`). */
1403
1403
  useResponsesLite?: boolean;
1404
- /** Codex Code Mode restriction: model expects tools routed through a programmatic exec surface (mirrors codex-rs `tool_mode`). */
1405
- toolMode?: "code_mode_only";
1406
1404
  /**
1407
1405
  * Service-tier ids the provider advertises for this model (Codex discovery
1408
1406
  * `service_tiers[].id`, e.g. `priority`, `ultrafast`). Absent when the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oh-my-pi/pi-catalog",
3
- "version": "18.10.3",
3
+ "version": "18.11.0",
4
4
  "description": "Model catalog for omp: bundled model database, provider discovery descriptors, model identity, classification, and equivalence",
5
5
  "keywords": [
6
6
  "ai",
@@ -93,12 +93,12 @@
93
93
  "gen:proto": "bun scripts/generate-protocols.ts"
94
94
  },
95
95
  "dependencies": {
96
- "@oh-my-pi/omptype": "18.10.3",
97
- "@oh-my-pi/pi-utils": "18.10.3"
96
+ "@oh-my-pi/omptype": "18.11.0",
97
+ "@oh-my-pi/pi-utils": "18.11.0"
98
98
  },
99
99
  "devDependencies": {
100
100
  "@bgotink/kdl": "0.4.0",
101
- "@oh-my-pi/pi-ai": "18.10.3",
101
+ "@oh-my-pi/pi-ai": "18.11.0",
102
102
  "@types/bun": "^1.3.14"
103
103
  },
104
104
  "engines": {
package/src/build.ts CHANGED
@@ -10,8 +10,16 @@
10
10
  import { resolveDiscoveryApi, resolveModelPolicy } from "./compat/resolve";
11
11
  import type { ModelIdentity } from "./compat/types";
12
12
  import { resolveModelTokenizer } from "./model-tokenizer";
13
- import { materializeTimeBasedCost } from "./pricing";
14
- import { type Api, MODEL_KINDS, type Model, type ModelSpec } from "./types";
13
+ import {
14
+ applyDueDatedCostCard,
15
+ type DatedCostCard,
16
+ dueEffectiveRate,
17
+ EARLIEST_EFFECTIVE_FROM,
18
+ materializeTimeBasedCost,
19
+ NO_RECURRING_TARIFF,
20
+ parseDatedCostCards,
21
+ } from "./pricing";
22
+ import { type Api, type EffectiveTokenCost, MODEL_KINDS, type Model, type ModelCost, type ModelSpec } from "./types";
15
23
  import { cleanModelName } from "./utils";
16
24
 
17
25
  function numberField(source: object, key: string): number | undefined {
@@ -24,41 +32,6 @@ function objectPayload(value: unknown): object | undefined {
24
32
  return typeof value === "object" && value !== null && !Array.isArray(value) ? value : undefined;
25
33
  }
26
34
 
27
- /**
28
- * Overwrite seeded fallback rates with the latest dated card whose
29
- * `effectiveFrom` is already due. Leaves the seed unchanged when no card
30
- * has started, and never attaches a `timeBased` tariff.
31
- */
32
- function applyEffectiveFallbackRates(
33
- cost: { input: number; output: number; cacheRead: number; cacheWrite: number },
34
- effectiveRates: unknown,
35
- now = Date.now(),
36
- ): void {
37
- const rates = objectPayload(effectiveRates);
38
- if (rates === undefined) return;
39
- let latestFrom = Number.NEGATIVE_INFINITY;
40
- let latest: object | undefined;
41
- for (const entry of Object.values(rates)) {
42
- const payload = objectPayload(entry);
43
- if (payload === undefined) continue;
44
- const date = Reflect.get(payload, "effectiveFrom");
45
- if (typeof date !== "string") continue;
46
- const from = Date.parse(date);
47
- if (!Number.isFinite(from) || from > now || from < latestFrom) continue;
48
- latestFrom = from;
49
- latest = payload;
50
- }
51
- if (latest === undefined) return;
52
- const input = numberField(latest, "input");
53
- if (input !== undefined) cost.input = input;
54
- const output = numberField(latest, "output");
55
- if (output !== undefined) cost.output = output;
56
- const cacheRead = numberField(latest, "cacheRead");
57
- if (cacheRead !== undefined) cost.cacheRead = cacheRead;
58
- const cacheWrite = numberField(latest, "cacheWrite");
59
- if (cacheWrite !== undefined) cost.cacheWrite = cacheWrite;
60
- }
61
-
62
35
  /** Narrow a compiled `input-modalities` axis value to the model input union. */
63
36
  function isInputModalities(value: unknown): value is ("text" | "image")[] {
64
37
  return Array.isArray(value) && value.every(entry => entry === "text" || entry === "image");
@@ -164,52 +137,65 @@ function applyCatalogAssignments<TApi extends Api>(model: Model<TApi>, catalog:
164
137
  }
165
138
  }
166
139
 
140
+ /** The parsed dated cards of a row's `cost-patch` and `cost-fallback`. */
141
+ interface DatedCostCards {
142
+ patch: readonly DatedCostCard[];
143
+ fallback: readonly DatedCostCard[];
144
+ }
145
+
167
146
  /**
168
- * Applies reviewed catalog-data value corrections (`cost-patch`,
169
- * `cache-read-at-input-rate`, `limits-patch`, `long-context-cost`,
170
- * `context-window-floor`, `input-modalities`) onto an upstream-sourced spec. Applied by
171
- * `buildModel` to every upstream-sourced spec; user-authored overrides are
172
- * recomposed after building by the override applicators, so explicit user
173
- * limits and pricing still win.
147
+ * The row's corrected price card at `now`: `cost-patch`, then `cost-fallback`
148
+ * (each with its dated card due at `now`), then the long-context tier and
149
+ * cache-rate derivations, all from the upstream card.
174
150
  */
175
- export function applyCatalogCorrections(
176
- model: Pick<ModelSpec<Api>, "cost" | "contextWindow" | "maxTokens" | "input">,
151
+ function correctedCost(
152
+ upstream: ModelCost,
177
153
  catalog: Record<string, unknown>,
178
- ): void {
154
+ cards: DatedCostCards,
155
+ now: number,
156
+ ): ModelCost {
157
+ let cost: ModelCost = { ...upstream };
158
+ const upstreamCard =
159
+ upstream.timeBased?.effectiveRates?.[0]?.effectiveFrom === EARLIEST_EFFECTIVE_FROM
160
+ ? dueEffectiveRate(upstream.timeBased, now)
161
+ : undefined;
162
+ if (upstreamCard !== undefined) {
163
+ // A row rebuilt from an already built one (dynamic merges, overrides)
164
+ // carries the dated schedule compiled below. Its leading card covers all
165
+ // history, so the scalar legs only ever held the card due at the first
166
+ // build; start from the card due now instead.
167
+ const { effectiveFrom: _effectiveFrom, ...card } = upstreamCard;
168
+ const { longContext: _longContext, ...base } = cost;
169
+ cost = { ...base, ...card };
170
+ }
179
171
  const patch = objectPayload(catalog.costPatch);
180
172
  if (patch !== undefined) {
181
- model.cost = { ...model.cost };
182
173
  const input = numberField(patch, "input");
183
- if (input !== undefined) model.cost.input = input;
174
+ if (input !== undefined) cost.input = input;
184
175
  const output = numberField(patch, "output");
185
- if (output !== undefined) model.cost.output = output;
176
+ if (output !== undefined) cost.output = output;
186
177
  const cacheRead = numberField(patch, "cacheRead");
187
- if (cacheRead !== undefined) model.cost.cacheRead = cacheRead;
178
+ if (cacheRead !== undefined) cost.cacheRead = cacheRead;
188
179
  const cacheWrite = numberField(patch, "cacheWrite");
189
- if (cacheWrite !== undefined) model.cost.cacheWrite = cacheWrite;
180
+ if (cacheWrite !== undefined) cost.cacheWrite = cacheWrite;
181
+ applyDueDatedCostCard(cost, cards.patch, now);
190
182
  }
191
183
  const fallback = objectPayload(catalog.costFallback);
192
184
  if (fallback !== undefined) {
193
- const base = model.cost;
194
- const hasTokenPrice = base.input !== 0 || base.output !== 0 || base.cacheRead !== 0 || base.cacheWrite !== 0;
185
+ const hasTokenPrice = cost.input !== 0 || cost.output !== 0 || cost.cacheRead !== 0 || cost.cacheWrite !== 0;
195
186
  if (!hasTokenPrice) {
196
187
  // Upstream reported no token price (plan-included or promo-free
197
188
  // rows): seed the reviewed list price instead of overwriting real
198
189
  // discovery data the way `cost-patch` would.
199
- model.cost = { ...model.cost };
200
190
  const input = numberField(fallback, "input");
201
- if (input !== undefined) model.cost.input = input;
191
+ if (input !== undefined) cost.input = input;
202
192
  const output = numberField(fallback, "output");
203
- if (output !== undefined) model.cost.output = output;
193
+ if (output !== undefined) cost.output = output;
204
194
  const cacheRead = numberField(fallback, "cacheRead");
205
- if (cacheRead !== undefined) model.cost.cacheRead = cacheRead;
195
+ if (cacheRead !== undefined) cost.cacheRead = cacheRead;
206
196
  const cacheWrite = numberField(fallback, "cacheWrite");
207
- if (cacheWrite !== undefined) model.cost.cacheWrite = cacheWrite;
208
- // Dated fallback rates overwrite the seeded numbers when they have
209
- // already taken effect. They are not a recurring tariff: wrapping
210
- // them in `timeBased` with empty peak windows would report
211
- // permanent off-peak and never wake at the dated boundary.
212
- applyEffectiveFallbackRates(model.cost, Reflect.get(fallback, "effectiveRates"));
197
+ if (cacheWrite !== undefined) cost.cacheWrite = cacheWrite;
198
+ applyDueDatedCostCard(cost, cards.fallback, now);
213
199
  }
214
200
  }
215
201
  const longContext = objectPayload(catalog.longContext);
@@ -221,20 +207,19 @@ export function applyCatalogCorrections(
221
207
  const output = numberField(longContext, "output");
222
208
  const cacheRead = numberField(longContext, "cacheRead");
223
209
  const cacheWrite = numberField(longContext, "cacheWrite");
224
- const base = model.cost;
225
- const hasTokenPrice = base.input !== 0 || base.output !== 0 || base.cacheRead !== 0 || base.cacheWrite !== 0;
210
+ const hasTokenPrice = cost.input !== 0 || cost.output !== 0 || cost.cacheRead !== 0 || cost.cacheWrite !== 0;
226
211
  if (inputThreshold !== undefined && multiplier !== undefined && hasTokenPrice) {
227
212
  // Multiplier form: tier rates derive from the row's final base card,
228
213
  // after `cost-patch`/`cost-fallback` corrected the live list price.
229
- model.cost = {
230
- ...base,
214
+ cost = {
215
+ ...cost,
231
216
  longContext: {
232
217
  inputThreshold,
233
218
  ...(inclusive && { inputThresholdInclusive: true }),
234
- input: base.input * multiplier,
235
- output: base.output * multiplier,
236
- cacheRead: base.cacheRead * multiplier,
237
- cacheWrite: base.cacheWrite * multiplier,
219
+ input: cost.input * multiplier,
220
+ output: cost.output * (numberField(longContext, "outputMultiplier") ?? multiplier),
221
+ cacheRead: cost.cacheRead * multiplier,
222
+ cacheWrite: cost.cacheWrite * multiplier,
238
223
  },
239
224
  };
240
225
  } else if (
@@ -244,19 +229,94 @@ export function applyCatalogCorrections(
244
229
  cacheRead !== undefined &&
245
230
  cacheWrite !== undefined
246
231
  ) {
247
- model.cost = { ...model.cost, longContext: { inputThreshold, input, output, cacheRead, cacheWrite } };
232
+ cost = { ...cost, longContext: { inputThreshold, input, output, cacheRead, cacheWrite } };
248
233
  }
249
234
  }
250
235
  if (catalog.cacheReadAtInputRate === true) {
251
- const { longContext } = model.cost;
252
- model.cost = {
253
- ...model.cost,
254
- cacheRead: model.cost.input,
236
+ const { longContext } = cost;
237
+ cost = {
238
+ ...cost,
239
+ cacheRead: cost.input,
255
240
  ...(longContext && { longContext: { ...longContext, cacheRead: longContext.input } }),
256
241
  };
257
242
  }
258
- if (catalog.timeBased !== undefined) {
259
- model.cost = { ...model.cost, timeBased: materializeTimeBasedCost(catalog.timeBased) };
243
+ const cacheWriteRate = catalog.cacheWriteRate;
244
+ if (typeof cacheWriteRate === "number") {
245
+ const { longContext } = cost;
246
+ cost = {
247
+ ...cost,
248
+ cacheWrite: cost.input * cacheWriteRate,
249
+ ...(longContext && { longContext: { ...longContext, cacheWrite: longContext.input * cacheWriteRate } }),
250
+ };
251
+ }
252
+ return cost;
253
+ }
254
+
255
+ /**
256
+ * Every price period the dated `cost-patch`/`cost-fallback` cards define, for
257
+ * request-time selection (`calculateUsageCost`): a row built before a change
258
+ * still bills requests after it at the new price, and a leading card keeps
259
+ * earlier requests at the old one on rows built after it. Undefined when no
260
+ * card changes the price (no cards, or a fallback the upstream price outranks).
261
+ */
262
+ function scheduledRates(
263
+ upstream: ModelCost,
264
+ catalog: Record<string, unknown>,
265
+ cards: DatedCostCards,
266
+ ): EffectiveTokenCost[] | undefined {
267
+ const dates = [...cards.patch, ...cards.fallback].map(card => card.effectiveFrom).sort((a, b) => a - b);
268
+ if (dates.length === 0) return undefined;
269
+ const periods: EffectiveTokenCost[] = [];
270
+ let previousPrice: string | undefined;
271
+ for (const effectiveFrom of [EARLIEST_EFFECTIVE_FROM, ...dates]) {
272
+ const { input, output, cacheRead, cacheWrite, longContext } = correctedCost(
273
+ upstream,
274
+ catalog,
275
+ cards,
276
+ effectiveFrom,
277
+ );
278
+ const price = { input, output, cacheRead, cacheWrite, ...(longContext && { longContext }) };
279
+ const key = JSON.stringify(price);
280
+ if (key === previousPrice) continue;
281
+ previousPrice = key;
282
+ periods.push({ effectiveFrom, ...price });
283
+ }
284
+ return periods.length > 1 ? periods : undefined;
285
+ }
286
+
287
+ /**
288
+ * Applies reviewed catalog-data value corrections (`cost-patch`,
289
+ * `cache-read-at-input-rate`, `cache-write-rate`, `limits-patch`,
290
+ * `long-context-cost`, `context-window-floor`, `input-modalities`) onto an
291
+ * upstream-sourced spec. Applied by
292
+ * `buildModel` to every upstream-sourced spec; user-authored overrides are
293
+ * recomposed after building by the override applicators, so explicit user
294
+ * limits and pricing still win.
295
+ */
296
+ export function applyCatalogCorrections(
297
+ model: Pick<ModelSpec<Api>, "cost" | "contextWindow" | "maxTokens" | "input">,
298
+ catalog: Record<string, unknown>,
299
+ ): void {
300
+ const upstream = model.cost;
301
+ const cards: DatedCostCards = {
302
+ patch: parseDatedCostCards(catalog.costPatch, "cost-patch"),
303
+ fallback: parseDatedCostCards(catalog.costFallback, "cost-fallback"),
304
+ };
305
+ // The card due now is the row's displayed price; the dated schedule rides
306
+ // along so rows reused past a change (bundles, caches, long sessions) still
307
+ // bill each request at the card in force when it was made.
308
+ model.cost = correctedCost(upstream, catalog, cards, Date.now());
309
+ const recurring = catalog.timeBased === undefined ? undefined : materializeTimeBasedCost(catalog.timeBased);
310
+ const dated = scheduledRates(upstream, catalog, cards);
311
+ if (dated !== undefined) {
312
+ if (recurring?.effectiveRates !== undefined) {
313
+ throw new Error(
314
+ "Dated cost-patch/cost-fallback effective-rates cannot combine with time-based-cost effective-rates",
315
+ );
316
+ }
317
+ model.cost = { ...model.cost, timeBased: { ...(recurring ?? NO_RECURRING_TARIFF), effectiveRates: dated } };
318
+ } else if (recurring !== undefined) {
319
+ model.cost = { ...model.cost, timeBased: recurring };
260
320
  }
261
321
  const limitsPatch = objectPayload(catalog.limitsPatch);
262
322
  if (limitsPatch !== undefined) {
@@ -70,6 +70,7 @@ export type AuthProviderId =
70
70
  | "qianfan"
71
71
  | "qwen-portal"
72
72
  | "sakana"
73
+ | "sambanova"
73
74
  | "siliconflow"
74
75
  | "siliconflow-cn"
75
76
  | "singularityapi-dev"
@@ -157,6 +158,7 @@ export type LoginProviderId =
157
158
  | "qianfan"
158
159
  | "qwen-portal"
159
160
  | "sakana"
161
+ | "sambanova"
160
162
  | "siliconflow"
161
163
  | "siliconflow-cn"
162
164
  | "singularityapi-dev"
@@ -370,6 +370,19 @@ export const AXES: Readonly<Record<string, AxisDef>> = {
370
370
  shape: "scalar",
371
371
  values: [true, false],
372
372
  },
373
+ /**
374
+ * The model's input classifier refuses requests that reproduce assistant
375
+ * reasoning as quoted text (`reasoning_extraction`), so reasoning must not
376
+ * be replayed as text into its input: compaction archives, summarizer
377
+ * transcripts and demoted interrupted turns leave it out. Declared per class
378
+ * in `classes/*.kdl`.
379
+ */
380
+ "classifier-cot-extraction": {
381
+ key: "classifierCotExtraction",
382
+ set: "catalog",
383
+ shape: "scalar",
384
+ values: [true, false],
385
+ },
373
386
  "clamp-context-override": { key: "clampContextOverride", set: "catalog", shape: "scalar" },
374
387
  "context-promotion-target": { key: "contextPromotionTarget", set: "catalog", shape: "scalar" },
375
388
  "context-window-floor": { key: "contextWindowFloor", set: "catalog", shape: "scalar" },
@@ -391,6 +404,14 @@ export const AXES: Readonly<Record<string, AxisDef>> = {
391
404
  shape: "scalar",
392
405
  values: [true, false],
393
406
  },
407
+ /**
408
+ * The host bills cache writes at this multiple of the row's live `input`
409
+ * price, for hosts whose discovery reports cache-write tokens but no
410
+ * cache-write rate. It replaces any `cacheWrite` the row carries, including
411
+ * a `cost-patch`/`cost-fallback` value and an absolute long-context tier's
412
+ * `cacheWrite` (rebased on the tier's `input`).
413
+ */
414
+ "cache-write-rate": { key: "cacheWriteRate", set: "catalog", shape: "scalar" },
394
415
  /**
395
416
  * How the model line bills a replayed signed thinking block as input:
396
417
  * `output` bills the reasoning its signature carries, at the size the turn
@@ -64,6 +64,7 @@ export type KnownProvider =
64
64
  | "qianfan"
65
65
  | "qwen-portal"
66
66
  | "sakana"
67
+ | "sambanova"
67
68
  | "siliconflow"
68
69
  | "siliconflow-cn"
69
70
  | "singularityapi-dev"