@oh-my-pi/pi-catalog 18.15.0 → 18.16.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.
- package/CHANGELOG.md +19 -0
- package/dist/types/compat/auth-ids.d.ts +2 -2
- package/dist/types/compat/catalog-policy.d.ts +6 -4
- package/dist/types/compat/factory-droid.d.ts +7 -1
- package/dist/types/compat/provider-ids.d.ts +1 -1
- package/dist/types/compat/resolve.d.ts +1 -1
- package/dist/types/compat/revision.d.ts +2 -0
- package/dist/types/compat/server-side-fallback.d.ts +6 -4
- package/dist/types/compat/types.d.ts +3 -1
- package/dist/types/discovery/openai-compatible.d.ts +6 -0
- package/dist/types/models.d.ts +16 -0
- package/dist/types/pricing.d.ts +66 -6
- package/dist/types/provider-models/openai-compat.d.ts +18 -0
- package/dist/types/types.d.ts +8 -3
- package/package.json +4 -4
- package/src/compat/auth-ids.ts +3 -0
- package/src/compat/axes.ts +5 -2
- package/src/compat/catalog-policy.ts +14 -10
- package/src/compat/collapse.ts +1 -1
- package/src/compat/factory-droid.ts +9 -1
- package/src/compat/provider-ids.ts +1 -0
- package/src/compat/resolve.ts +4 -2
- package/src/compat/revision.ts +10 -0
- package/src/compat/rules/README.md +2 -0
- package/src/compat/rules/auth/_order.kdl +1 -1
- package/src/compat/rules/auth/mistral.kdl +4 -0
- package/src/compat/rules/auth/scaleway.kdl +17 -0
- package/src/compat/rules/providers/factory-droid.kdl +219 -13
- package/src/compat/rules/providers/scaleway.kdl +179 -0
- package/src/compat/rules/runtime/behavior.kdl +13 -0
- package/src/compat/rules.json +1 -1
- package/src/compat/server-side-fallback.ts +7 -5
- package/src/compat/types.ts +3 -1
- package/src/discovery/factory-droid.ts +35 -11
- package/src/discovery/openai-compatible.ts +1 -1
- package/src/model-cache.ts +27 -54
- package/src/model-catalog-format.ts +5 -8
- package/src/model-manager.ts +27 -121
- package/src/model-tokenizer.ts +13 -12
- package/src/models.json +1 -1
- package/src/models.ts +32 -1
- package/src/pricing.ts +59 -94
- package/src/provider-models/descriptors.ts +2 -0
- package/src/provider-models/openai-compat.ts +54 -39
- package/src/types.ts +8 -2
- package/src/wire/factory-droid.ts +4 -3
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,25 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [18.16.0] - 2026-11-02
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- `/login mistral` now offers the Mistral Vibe browser sign-in beside `MISTRAL_API_KEY` ([#13875](https://github.com/can1357/oh-my-pi/pull/13875) by [@Gauthier-Huguenin](https://github.com/Gauthier-Huguenin), continued in [#17672](https://github.com/can1357/oh-my-pi/pull/17672) by [@abilliontokens](https://github.com/abilliontokens))
|
|
10
|
+
- Added `revisionAtLeast` to `compat/revision` and exported `withOpenAICompatibleDiscoveryTimeout` from `discovery/openai-compatible`
|
|
11
|
+
- Added the Factory Droid models droid 1.1.0 serves: GPT-6.1 Terra, GPT-6 Sol and Luna Fast Mode, GPT-6 Astra and GPT-6.1 Sol Ultra Fast (after the organization's high-cost opt-in), Nemotron 3.5 Super, Muse Spark Open, Muse Spark 1.2 and Beam 1.0, each listed once the account may use it ([#17766](https://github.com/can1357/oh-my-pi/pull/17766) by [@will-bogusz](https://github.com/will-bogusz))
|
|
12
|
+
- Added Factory Droid's dated credit rates: the credit rate follows Factory's promotions, discounted from a promotion's start and back to the base rate at its end, using the same dated rate cards as dollar prices (currently GPT-5.6 Sol and Gemini 3.7/3.8 Flash) ([#16309](https://github.com/can1357/oh-my-pi/pull/16309) by [@will-bogusz](https://github.com/will-bogusz))
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
|
|
16
|
+
- Factory Droid lists Opus 5.5 Fast, GPT-6.1 Sol and MiniMax M3 without the feature flags Factory retired, and no longer routes GPT-5.5 to Snowflake ([#17766](https://github.com/can1357/oh-my-pi/pull/17766) by [@will-bogusz](https://github.com/will-bogusz))
|
|
17
|
+
- Factory Droid Opus 5.5 now falls back to Opus 5 when Anthropic refuses a request, as droid does, while your account can use Opus 5 ([#17772](https://github.com/can1357/oh-my-pi/pull/17772) by [@will-bogusz](https://github.com/will-bogusz))
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
|
|
21
|
+
- GPT-3.5 and GPT-4 (Turbo) resolve the `cl100k` tokenizer, so their context estimates count in cl100k_base rather than the o200k_base default for models without one ([#17607](https://github.com/can1357/oh-my-pi/pull/17607) by [@will-bogusz](https://github.com/will-bogusz))
|
|
22
|
+
- Added Scaleway Generative APIs as a built-in provider with 12 serverless chat models (glm-5.3 default), API-key login, and live model discovery ([#16687](https://github.com/can1357/oh-my-pi/pull/16687) by [@reda-maizate](https://github.com/reda-maizate))
|
|
23
|
+
|
|
5
24
|
## [18.15.0] - 2026-11-01
|
|
6
25
|
|
|
7
26
|
### Added
|
|
@@ -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" | "inception" | "kagi" | "kilo" | "kimi-code" | "litellm" | "llama.cpp" | "lm-studio" | "local" | "meta" | "minimax" | "minimax-code" | "minimax-code-cn" | "mistral" | "modular" | "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";
|
|
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" | "inception" | "kagi" | "kilo" | "kimi-code" | "litellm" | "llama.cpp" | "lm-studio" | "local" | "meta" | "minimax" | "minimax-code" | "minimax-code-cn" | "mistral" | "modular" | "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" | "scaleway" | "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" | "inception" | "kagi" | "kilo" | "kimi-code" | "litellm" | "llama.cpp" | "lm-studio" | "meta" | "minimax-code" | "minimax-code-cn" | "modular" | "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";
|
|
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" | "inception" | "kagi" | "kilo" | "kimi-code" | "litellm" | "llama.cpp" | "lm-studio" | "meta" | "minimax-code" | "minimax-code-cn" | "mistral" | "modular" | "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" | "scaleway" | "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,7 +1,9 @@
|
|
|
1
1
|
import type { CatalogPolicyInputs } from "../types.js";
|
|
2
|
+
import { type ResolveRoute } from "./resolve.js";
|
|
2
3
|
/**
|
|
3
|
-
* The `catalog` axis bag authored in the KDL cascade rules for a model
|
|
4
|
-
* memoized per distinct model. Frozen: the bag
|
|
5
|
-
* must stay read-only — extract the value you
|
|
4
|
+
* The `catalog` axis bag authored in the KDL cascade rules for a model (on one
|
|
5
|
+
* upstream when `route` names it), memoized per distinct model. Frozen: the bag
|
|
6
|
+
* is shared across callers, so it must stay read-only — extract the value you
|
|
7
|
+
* need rather than handing it on.
|
|
6
8
|
*/
|
|
7
|
-
export declare function resolveCatalogPolicy(model: CatalogPolicyInputs): Readonly<Record<string, unknown>>;
|
|
9
|
+
export declare function resolveCatalogPolicy(model: CatalogPolicyInputs, route?: ResolveRoute): Readonly<Record<string, unknown>>;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type DatedCostCard } from "../pricing.js";
|
|
1
2
|
import type { Api, ModelSpec } from "../types.js";
|
|
2
3
|
import { type FactoryDroidRegion, type FactoryDroidWire } from "../wire/factory-droid.js";
|
|
3
4
|
/** Native token limits for one inference region. */
|
|
@@ -5,11 +6,16 @@ export interface FactoryDroidLimits {
|
|
|
5
6
|
contextWindow: number | null;
|
|
6
7
|
maxTokens: number | null;
|
|
7
8
|
}
|
|
8
|
-
/**
|
|
9
|
+
/**
|
|
10
|
+
* Standard Credits rates: `input` is the per-token weight; `output` and
|
|
11
|
+
* `cacheRead` multiply it. `effectiveRates` are dated cards (ascending) that
|
|
12
|
+
* overwrite those legs from their date, as on `cost-patch`.
|
|
13
|
+
*/
|
|
9
14
|
export interface FactoryDroidCreditRates {
|
|
10
15
|
input: number;
|
|
11
16
|
output?: number;
|
|
12
17
|
cacheRead?: number;
|
|
18
|
+
effectiveRates?: readonly DatedCostCard[];
|
|
13
19
|
}
|
|
14
20
|
/** Account gates on one registry model (the `entitlement` axis). */
|
|
15
21
|
export interface FactoryDroidEntitlement {
|
|
@@ -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" | "inception" | "kilo" | "kimi-code" | "litellm" | "lm-studio" | "local" | "meta" | "minimax" | "minimax-code" | "minimax-code-cn" | "mistral" | "modular" | "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";
|
|
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" | "inception" | "kilo" | "kimi-code" | "litellm" | "lm-studio" | "local" | "meta" | "minimax" | "minimax-code" | "minimax-code-cn" | "mistral" | "modular" | "moonshot" | "muse-code" | "nanogpt" | "novita" | "nvidia" | "ollama" | "ollama-cloud" | "openai" | "openai-codex" | "opencode-go" | "opencode-zen" | "openrouter" | "qianfan" | "qwen-portal" | "sakana" | "sambanova" | "scaleway" | "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";
|
|
@@ -21,7 +21,7 @@ export declare function resolveDiscoveryApi(spec: ModelSpec<Api>, providerType:
|
|
|
21
21
|
* {@link resolveModelPolicy} without resolving the compat and thinking
|
|
22
22
|
* policies, which dominate its cost on catalog-wide scans.
|
|
23
23
|
*/
|
|
24
|
-
export declare function resolveCatalogAxes(spec: CatalogPolicyInputs): Record<string, unknown>;
|
|
24
|
+
export declare function resolveCatalogAxes(spec: CatalogPolicyInputs, route?: ResolveRoute): Record<string, unknown>;
|
|
25
25
|
/**
|
|
26
26
|
* Resolves the full policy surface for one model spec: structured identity,
|
|
27
27
|
* complete compat record, thinking metadata, and catalog-data corrections.
|
|
@@ -22,6 +22,8 @@ export declare function parseRevision(value: string): Revision | undefined;
|
|
|
22
22
|
export declare function parseRevisionPrefix(value: string): Revision | undefined;
|
|
23
23
|
/** Lexicographic triple comparison: negative, zero, or positive. */
|
|
24
24
|
export declare function compareRevision(a: Revision, b: Revision): number;
|
|
25
|
+
/** Whether a revision string parses and is at or above `floor`; `undefined` or unparsable is never. */
|
|
26
|
+
export declare function revisionAtLeast(revision: string | undefined, floor: string): boolean;
|
|
25
27
|
/** Renders a revision as its canonical `major.minor.patch` string. */
|
|
26
28
|
export declare function formatRevision(revision: Revision): string;
|
|
27
29
|
/** Comparison operator inside a `revision ">=2.5 <4"` cascade constraint. */
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { CatalogPolicyInputs } from "../types.js";
|
|
2
|
+
import type { ResolveRoute } from "./resolve.js";
|
|
2
3
|
/**
|
|
3
4
|
* Ordered model ids for Anthropic's server-side refusal `fallbacks` chain,
|
|
4
|
-
* authored on the `server-side-fallback-models` catalog axis
|
|
5
|
-
*
|
|
5
|
+
* authored on the `server-side-fallback-models` catalog axis, on `route`'s
|
|
6
|
+
* upstream when it names one. Empty when no rule assigns a chain (the model,
|
|
7
|
+
* host or upstream does not support the feature).
|
|
6
8
|
*/
|
|
7
|
-
export declare function serverSideFallbackModels(model:
|
|
9
|
+
export declare function serverSideFallbackModels(model: CatalogPolicyInputs, route?: ResolveRoute): readonly string[];
|
|
@@ -714,6 +714,8 @@ export interface ResolvedAxes {
|
|
|
714
714
|
export interface RequestPolicy {
|
|
715
715
|
completionsReasoningMode?: "none" | "effort" | "opt-in" | "forced-on";
|
|
716
716
|
completionsReasoningHistory?: "omit" | "preserved" | "interleaved";
|
|
717
|
+
/** Send `allow_missing_thinking` with the effort so tool-call turns may arrive without reasoning. */
|
|
718
|
+
completionsAllowMissingThinking?: boolean;
|
|
717
719
|
anthropicThinking?: "adaptive" | "adaptive-summarized" | "budget-interleaved" | "budget-effort";
|
|
718
720
|
/** Advertise `fine-grained-tool-streaming-2025-05-14` on requests that carry tools. */
|
|
719
721
|
anthropicToolStreamingBeta?: boolean;
|
|
@@ -721,7 +723,7 @@ export interface RequestPolicy {
|
|
|
721
723
|
openaiPlatformHeader?: string;
|
|
722
724
|
responsesCacheRetention?: boolean;
|
|
723
725
|
responsesVerbosity?: "low";
|
|
724
|
-
responsesServiceTier?: "priority";
|
|
726
|
+
responsesServiceTier?: "priority" | "ultrafast";
|
|
725
727
|
responsesParallelToolCalls?: boolean;
|
|
726
728
|
responsesSafetyIdentifier?: boolean;
|
|
727
729
|
/** Default `tool_choice` to `auto` when the request carries tools and the caller picks none. */
|
|
@@ -10,6 +10,12 @@ import type { Api, FetchImpl, ModelSpec, Provider } from "../types.js";
|
|
|
10
10
|
* (issue #8315). 10s matches the coding-agent's remote-discovery budget.
|
|
11
11
|
*/
|
|
12
12
|
export declare const DEFAULT_OPENAI_COMPATIBLE_DISCOVERY_TIMEOUT_MS = 10000;
|
|
13
|
+
/**
|
|
14
|
+
* Uses a cancellable timer rather than the native abort-timeout helper so
|
|
15
|
+
* successful fast discovery requests do not leave armed timeout signals for
|
|
16
|
+
* concurrent GC to trip over later.
|
|
17
|
+
*/
|
|
18
|
+
export declare function withOpenAICompatibleDiscoveryTimeout<T>(timeoutMs: number, run: (signal: AbortSignal) => Promise<T>): Promise<T>;
|
|
13
19
|
/**
|
|
14
20
|
* Minimal OpenAI-style model entry shape consumed by discovery.
|
|
15
21
|
*
|
package/dist/types/models.d.ts
CHANGED
|
@@ -11,6 +11,22 @@ export declare function getBundledProviders(): readonly KnownProvider[];
|
|
|
11
11
|
export declare function getBundledModels(provider: GeneratedProvider): Model<Api>[];
|
|
12
12
|
/** Resolve display semantics without mistaking an absent rate card for a free model. */
|
|
13
13
|
export declare function getModelPricingStatus(model: Pick<Model<Api>, "cost" | "pricingStatus">): ModelPricingStatus;
|
|
14
|
+
/** Factory Droid credit rate in force at a time, and the next dated change to it. */
|
|
15
|
+
export interface FactoryDroidCreditRate {
|
|
16
|
+
rate: number;
|
|
17
|
+
/** The earliest later `credit-rates` card that changes `rate`: the rate it sets and when (Unix ms). */
|
|
18
|
+
next?: {
|
|
19
|
+
rate: number;
|
|
20
|
+
at: number;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Factory Droid Standard Credits rate at `timestamp` (Unix ms, default now):
|
|
25
|
+
* the `input` of the `credit-rates` card due then (the latest at or before
|
|
26
|
+
* it, as for cost cards), else the base rate. Undefined for models without a
|
|
27
|
+
* credit rate.
|
|
28
|
+
*/
|
|
29
|
+
export declare function getFactoryDroidCreditRate(model: Pick<Model, "factoryDroidCredits" | "factoryDroidCreditCards">, timestamp?: number): FactoryDroidCreditRate | undefined;
|
|
14
30
|
/**
|
|
15
31
|
* The base token card in force at `timestamp` (Unix ms, default now): the
|
|
16
32
|
* latest due `timeBased` effective rate, else the row's own legs. Price
|
package/dist/types/pricing.d.ts
CHANGED
|
@@ -1,24 +1,84 @@
|
|
|
1
1
|
import type { EffectiveTokenCost, ModelCost, TimeBasedCost, TokenCost } from "./types.js";
|
|
2
|
+
/** Serialized time-based pricing schedule, as cached model rows carry it. */
|
|
3
|
+
export declare const TimeBasedCostShape: import("@oh-my-pi/omptype").FluentType<{
|
|
4
|
+
effectiveRates?: ({
|
|
5
|
+
cacheRead: number;
|
|
6
|
+
cacheWrite: number;
|
|
7
|
+
input: number;
|
|
8
|
+
output: number;
|
|
9
|
+
} & {
|
|
10
|
+
effectiveFrom: number;
|
|
11
|
+
longContext?: ({
|
|
12
|
+
cacheRead: number;
|
|
13
|
+
cacheWrite: number;
|
|
14
|
+
input: number;
|
|
15
|
+
output: number;
|
|
16
|
+
} & {
|
|
17
|
+
inputThreshold: number;
|
|
18
|
+
inputThresholdInclusive?: boolean | undefined;
|
|
19
|
+
}) | undefined;
|
|
20
|
+
})[] | undefined;
|
|
21
|
+
offPeakMultiplier: number;
|
|
22
|
+
peakWindows: {
|
|
23
|
+
endMinute: number;
|
|
24
|
+
startMinute: number;
|
|
25
|
+
weekdays: number[];
|
|
26
|
+
}[];
|
|
27
|
+
}, {
|
|
28
|
+
effectiveRates?: ({
|
|
29
|
+
cacheRead: number;
|
|
30
|
+
cacheWrite: number;
|
|
31
|
+
input: number;
|
|
32
|
+
output: number;
|
|
33
|
+
} & {
|
|
34
|
+
effectiveFrom: number;
|
|
35
|
+
longContext?: ({
|
|
36
|
+
cacheRead: number;
|
|
37
|
+
cacheWrite: number;
|
|
38
|
+
input: number;
|
|
39
|
+
output: number;
|
|
40
|
+
} & {
|
|
41
|
+
inputThreshold: number;
|
|
42
|
+
inputThresholdInclusive?: boolean | undefined;
|
|
43
|
+
}) | undefined;
|
|
44
|
+
})[] | undefined;
|
|
45
|
+
offPeakMultiplier: number;
|
|
46
|
+
peakWindows: {
|
|
47
|
+
endMinute: number;
|
|
48
|
+
startMinute: number;
|
|
49
|
+
weekdays: number[];
|
|
50
|
+
}[];
|
|
51
|
+
}>;
|
|
2
52
|
/** Validate serialized schedules before admitting cached model rows. */
|
|
3
53
|
export declare function isTimeBasedCost(value: unknown): value is TimeBasedCost;
|
|
4
54
|
/** KDL object children are named records; normalize them once while constructing the model. */
|
|
5
55
|
export declare function materializeTimeBasedCost(value: unknown): TimeBasedCost;
|
|
56
|
+
/**
|
|
57
|
+
* The dated card due at `timestamp`: the one with the latest `effectiveFrom`
|
|
58
|
+
* at or before it, in any order, or undefined before the first card.
|
|
59
|
+
*/
|
|
60
|
+
export declare function dueDatedCard<T extends {
|
|
61
|
+
effectiveFrom: number;
|
|
62
|
+
}>(cards: readonly T[], timestamp: number): T | undefined;
|
|
6
63
|
/**
|
|
7
64
|
* The latest `effectiveRates` card of a materialized schedule due at
|
|
8
65
|
* `timestamp`, or undefined before the first one (or without a schedule).
|
|
9
66
|
*/
|
|
10
67
|
export declare function dueEffectiveRate(schedule: TimeBasedCost | undefined, timestamp: number): EffectiveTokenCost | undefined;
|
|
11
|
-
/**
|
|
68
|
+
/**
|
|
69
|
+
* A dated `effective-rates` card of a `cost-patch`, `cost-fallback` or
|
|
70
|
+
* `credit-rates` rule: the legs it overwrites from `effectiveFrom` (Unix ms).
|
|
71
|
+
*/
|
|
12
72
|
export interface DatedCostCard extends Partial<TokenCost> {
|
|
13
73
|
effectiveFrom: number;
|
|
14
74
|
}
|
|
15
75
|
/**
|
|
16
|
-
* Parse the compiled `effective-rates` block of a `cost-patch
|
|
17
|
-
* `cost-fallback` payload into dated cards,
|
|
18
|
-
* malformed card, so a typo in a rule fails the
|
|
19
|
-
* leaving the old price in force.
|
|
76
|
+
* Parse the compiled `effective-rates` block of a `cost-patch`,
|
|
77
|
+
* `cost-fallback` or Factory Droid `credit-rates` payload into dated cards,
|
|
78
|
+
* ascending by date. Throws on a malformed card, so a typo in a rule fails the
|
|
79
|
+
* build instead of silently leaving the old price in force.
|
|
20
80
|
*/
|
|
21
|
-
export declare function parseDatedCostCards(costAxis: unknown, axis: "cost-patch" | "cost-fallback"): DatedCostCard[];
|
|
81
|
+
export declare function parseDatedCostCards(costAxis: unknown, axis: "cost-patch" | "cost-fallback" | "credit-rates"): DatedCostCard[];
|
|
22
82
|
/**
|
|
23
83
|
* Earliest instant a rate card can name (the `Date` range minimum). A leading
|
|
24
84
|
* card from here holds the price in force before the first dated change.
|
|
@@ -740,3 +740,21 @@ export declare function singularityApiDevModelManagerOptions(config?: Singularit
|
|
|
740
740
|
* reviewed KDL rules own the wire shape, limits patch, and effort ladder.
|
|
741
741
|
*/
|
|
742
742
|
export declare function singularityApiTechModelManagerOptions(config?: SingularityApiModelManagerConfig): ModelManagerOptions<Api>;
|
|
743
|
+
/**
|
|
744
|
+
* Scaleway discovery configuration: the API key plus optional base-URL and
|
|
745
|
+
* fetch overrides. Consumed by {@link scalewayModelManagerOptions}, and
|
|
746
|
+
* exported for extensions and tests that construct the manager directly.
|
|
747
|
+
*/
|
|
748
|
+
export interface ScalewayModelManagerConfig {
|
|
749
|
+
apiKey?: string;
|
|
750
|
+
baseUrl?: string;
|
|
751
|
+
fetch?: FetchImpl;
|
|
752
|
+
}
|
|
753
|
+
/**
|
|
754
|
+
* Scaleway model manager: plain OpenAI-compatible chat completions at
|
|
755
|
+
* `api.scaleway.ai/v1` (fr-par serverless Generative APIs). `/v1/models`
|
|
756
|
+
* returns bare ids, so the authored seed in `providers/scaleway.kdl` is the
|
|
757
|
+
* reference each live row merges names, modalities, prices, and limits from;
|
|
758
|
+
* a successful fetch is the complete roster and prunes retired rows.
|
|
759
|
+
*/
|
|
760
|
+
export declare function scalewayModelManagerOptions(config?: ScalewayModelManagerConfig): ModelManagerOptions<"openai-completions">;
|
package/dist/types/types.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ModelIdentity } from "./compat/types.js";
|
|
2
2
|
import type { Effort } from "./effort.js";
|
|
3
|
+
import type { DatedCostCard } from "./pricing.js";
|
|
3
4
|
export type { FetchImpl } from "@oh-my-pi/pi-utils";
|
|
4
5
|
export type { KnownProvider } from "./provider-models/descriptors.js";
|
|
5
6
|
export type KnownApi = "openai-completions" | "openai-responses" | "openrouter" | "openai-codex-responses" | "azure-openai-responses" | "anthropic-messages" | "bedrock-converse-stream" | "google-generative-ai" | "google-gemini-cli" | "google-vertex" | "ollama-chat" | "cursor-agent" | "factory-droid-agent" | "gitlab-duo-agent" | "devin-agent" | "apple-foundation-models";
|
|
@@ -1198,12 +1199,12 @@ export interface ModelCost extends TokenCost {
|
|
|
1198
1199
|
/**
|
|
1199
1200
|
* Exact local content tokenizer family for a model.
|
|
1200
1201
|
*
|
|
1201
|
-
* Absent means no first-party local tokenizer is known and consumers
|
|
1202
|
-
*
|
|
1202
|
+
* Absent means no first-party local tokenizer is known and consumers count
|
|
1203
|
+
* in o200k_base. The values name tokenizer
|
|
1203
1204
|
* generations rather than providers: DeepSeek V3 through V4 share
|
|
1204
1205
|
* `"deepseek-v3"`; Kimi K2 through K3 share `"kimi-k2"`.
|
|
1205
1206
|
*/
|
|
1206
|
-
export type ModelTokenizer = "claude-v3" | "claude-v47" | "claude-v5" | "claude-v5-sonnet" | "qwen3" | "deepseek-v3" | "kimi-k2" | "glm5" | "o200k";
|
|
1207
|
+
export type ModelTokenizer = "claude-v3" | "claude-v47" | "claude-v5" | "claude-v5-sonnet" | "qwen3" | "deepseek-v3" | "kimi-k2" | "glm5" | "cl100k" | "o200k";
|
|
1207
1208
|
/** One account's discovered entitlements on a model; see {@link Model.accountAccess}. */
|
|
1208
1209
|
export interface ModelAccountAccess {
|
|
1209
1210
|
/**
|
|
@@ -1393,8 +1394,12 @@ export interface Model<TApi extends Api = Api> {
|
|
|
1393
1394
|
factoryDroidApiProviders?: string[];
|
|
1394
1395
|
/** Factory Droid: `configured_order` when live routing chose the rotation; absent means registry order. */
|
|
1395
1396
|
factoryDroidRoutingSource?: "configured_order";
|
|
1397
|
+
/** Factory Droid: server-side refusal fallbacks the account can run; absent when none are named. */
|
|
1398
|
+
factoryDroidRefusalFallbacks?: string[];
|
|
1396
1399
|
/** Factory Droid Standard Credits base rate (relative per-token weight, not dollars). */
|
|
1397
1400
|
factoryDroidCredits?: number;
|
|
1401
|
+
/** Factory Droid dated `credit-rates` cards, ascending; `getFactoryDroidCreditRate` picks the one due now. */
|
|
1402
|
+
factoryDroidCreditCards?: readonly DatedCostCard[];
|
|
1398
1403
|
/** Canonical organization resolved during Factory discovery. */
|
|
1399
1404
|
factoryDroidOrgId?: string;
|
|
1400
1405
|
cost: ModelCost;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oh-my-pi/pi-catalog",
|
|
3
|
-
"version": "18.
|
|
3
|
+
"version": "18.16.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",
|
|
@@ -88,12 +88,12 @@
|
|
|
88
88
|
"gen:proto": "bun scripts/generate-protocols.ts"
|
|
89
89
|
},
|
|
90
90
|
"dependencies": {
|
|
91
|
-
"@oh-my-pi/omptype": "18.
|
|
92
|
-
"@oh-my-pi/pi-utils": "18.
|
|
91
|
+
"@oh-my-pi/omptype": "18.16.0",
|
|
92
|
+
"@oh-my-pi/pi-utils": "18.16.0"
|
|
93
93
|
},
|
|
94
94
|
"devDependencies": {
|
|
95
95
|
"@bgotink/kdl": "0.4.0",
|
|
96
|
-
"@oh-my-pi/pi-ai": "18.
|
|
96
|
+
"@oh-my-pi/pi-ai": "18.16.0",
|
|
97
97
|
"@types/bun": "^1.3.14"
|
|
98
98
|
},
|
|
99
99
|
"engines": {
|
package/src/compat/auth-ids.ts
CHANGED
|
@@ -73,6 +73,7 @@ export type AuthProviderId =
|
|
|
73
73
|
| "qwen-portal"
|
|
74
74
|
| "sakana"
|
|
75
75
|
| "sambanova"
|
|
76
|
+
| "scaleway"
|
|
76
77
|
| "siliconflow"
|
|
77
78
|
| "siliconflow-cn"
|
|
78
79
|
| "singularityapi-dev"
|
|
@@ -144,6 +145,7 @@ export type LoginProviderId =
|
|
|
144
145
|
| "meta"
|
|
145
146
|
| "minimax-code"
|
|
146
147
|
| "minimax-code-cn"
|
|
148
|
+
| "mistral"
|
|
147
149
|
| "modular"
|
|
148
150
|
| "moonshot"
|
|
149
151
|
| "muse-code"
|
|
@@ -163,6 +165,7 @@ export type LoginProviderId =
|
|
|
163
165
|
| "qwen-portal"
|
|
164
166
|
| "sakana"
|
|
165
167
|
| "sambanova"
|
|
168
|
+
| "scaleway"
|
|
166
169
|
| "siliconflow"
|
|
167
170
|
| "siliconflow-cn"
|
|
168
171
|
| "singularityapi-dev"
|
package/src/compat/axes.ts
CHANGED
|
@@ -109,6 +109,7 @@ export const AXES: Readonly<Record<string, AxisDef>> = {
|
|
|
109
109
|
"preserved",
|
|
110
110
|
"interleaved",
|
|
111
111
|
]),
|
|
112
|
+
"completions-allow-missing-thinking": wire("completionsAllowMissingThinking", ["request"]),
|
|
112
113
|
"anthropic-thinking": wire("anthropicThinking", ["request"], "scalar", [
|
|
113
114
|
"adaptive",
|
|
114
115
|
"adaptive-summarized",
|
|
@@ -119,7 +120,7 @@ export const AXES: Readonly<Record<string, AxisDef>> = {
|
|
|
119
120
|
"openai-platform-header": wire("openaiPlatformHeader", ["request"]),
|
|
120
121
|
"responses-cache-retention": wire("responsesCacheRetention", ["request"]),
|
|
121
122
|
"responses-verbosity": wire("responsesVerbosity", ["request"], "scalar", ["low"]),
|
|
122
|
-
"responses-service-tier": wire("responsesServiceTier", ["request"], "scalar", ["priority"]),
|
|
123
|
+
"responses-service-tier": wire("responsesServiceTier", ["request"], "scalar", ["priority", "ultrafast"]),
|
|
123
124
|
"responses-parallel-tool-calls": wire("responsesParallelToolCalls", ["request"]),
|
|
124
125
|
"responses-safety-identifier": wire("responsesSafetyIdentifier", ["request"]),
|
|
125
126
|
"responses-tool-choice-auto": wire("responsesToolChoiceAuto", ["request"]),
|
|
@@ -672,7 +673,9 @@ export const AXES: Readonly<Record<string, AxisDef>> = {
|
|
|
672
673
|
"region-limits-eu": { key: "regionLimitsEu", set: "catalog", shape: "object" },
|
|
673
674
|
/**
|
|
674
675
|
* Subscription credit rates: `input` is the per-token credit weight shown
|
|
675
|
-
* as the model's multiplier; `output` and `cache-read` multiply it.
|
|
676
|
+
* as the model's multiplier; `output` and `cache-read` multiply it. An
|
|
677
|
+
* `effective-rates` block of dated cards overwrites them from each card's
|
|
678
|
+
* `effective-from`, as on `cost-patch`.
|
|
676
679
|
*/
|
|
677
680
|
"credit-rates": { key: "creditRates", set: "catalog", shape: "object" },
|
|
678
681
|
/**
|
|
@@ -1,29 +1,33 @@
|
|
|
1
1
|
import { withoutUnverifiedThinking } from "../model-thinking";
|
|
2
2
|
import type { CatalogPolicyInputs } from "../types";
|
|
3
|
-
import { resolveCatalogAxes } from "./resolve";
|
|
3
|
+
import { type ResolveRoute, resolveCatalogAxes } from "./resolve";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Rule-owned catalog-axis policy by model. Resolve once per process rather
|
|
7
7
|
* than walking the static policy cascade on every accessor call. The key
|
|
8
8
|
* mirrors the cascade target inputs — `providerType ?? provider`, api, the
|
|
9
9
|
* logical id plus `requestModelId` (the wire id `policyModelId` resolves
|
|
10
|
-
* unknown logical ids under), and the
|
|
11
|
-
* class/family/revision facts are a pure function of
|
|
12
|
-
* one entry per distinct model.
|
|
10
|
+
* unknown logical ids under), the reasoning flag, and the route's upstream;
|
|
11
|
+
* identity-derived class/family/revision facts are a pure function of
|
|
12
|
+
* provider + id. Bounded: one entry per distinct model and upstream.
|
|
13
13
|
*/
|
|
14
14
|
const catalogPolicyCache = new Map<string, Readonly<Record<string, unknown>>>();
|
|
15
15
|
const CATALOG_POLICY_CACHE_MAX = 8192;
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
|
-
* The `catalog` axis bag authored in the KDL cascade rules for a model
|
|
19
|
-
* memoized per distinct model. Frozen: the bag
|
|
20
|
-
* must stay read-only — extract the value you
|
|
18
|
+
* The `catalog` axis bag authored in the KDL cascade rules for a model (on one
|
|
19
|
+
* upstream when `route` names it), memoized per distinct model. Frozen: the bag
|
|
20
|
+
* is shared across callers, so it must stay read-only — extract the value you
|
|
21
|
+
* need rather than handing it on.
|
|
21
22
|
*/
|
|
22
|
-
export function resolveCatalogPolicy(
|
|
23
|
-
|
|
23
|
+
export function resolveCatalogPolicy(
|
|
24
|
+
model: CatalogPolicyInputs,
|
|
25
|
+
route?: ResolveRoute,
|
|
26
|
+
): Readonly<Record<string, unknown>> {
|
|
27
|
+
const key = `${model.provider}\u0000${model.providerType ?? ""}\u0000${model.api}\u0000${model.id}\u0000${model.requestModelId ?? ""}\u0000${model.reasoning ? 1 : 0}\u0000${route?.upstream ?? ""}`;
|
|
24
28
|
const cached = catalogPolicyCache.get(key);
|
|
25
29
|
if (cached !== undefined) return cached;
|
|
26
|
-
const policy = Object.freeze({ ...resolveCatalogAxes(withoutUnverifiedThinking(model)) });
|
|
30
|
+
const policy = Object.freeze({ ...resolveCatalogAxes(withoutUnverifiedThinking(model), route) });
|
|
27
31
|
if (catalogPolicyCache.size >= CATALOG_POLICY_CACHE_MAX) catalogPolicyCache.clear();
|
|
28
32
|
catalogPolicyCache.set(key, policy);
|
|
29
33
|
return policy;
|
package/src/compat/collapse.ts
CHANGED
|
@@ -240,7 +240,7 @@ function compiledTemplate(compiled: CompiledVariantFamily): VariantFamilyTemplat
|
|
|
240
240
|
const alternatives = [family.id, ...family.members, ...(family.extraAliases ?? [])].map(id =>
|
|
241
241
|
id
|
|
242
242
|
.split(REVISION_PLACEHOLDER)
|
|
243
|
-
.map(part =>
|
|
243
|
+
.map(part => RegExp.escape(part))
|
|
244
244
|
.join(REVISION_CAPTURE),
|
|
245
245
|
);
|
|
246
246
|
const template: VariantFamilyTemplate = {
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* to bind a model to the account serving it.
|
|
7
7
|
*/
|
|
8
8
|
import { isRecord, once } from "@oh-my-pi/pi-utils";
|
|
9
|
+
import { type DatedCostCard, parseDatedCostCards } from "../pricing";
|
|
9
10
|
import type { Api, ModelSpec } from "../types";
|
|
10
11
|
import { FACTORY_DROID_WIRES, type FactoryDroidRegion, type FactoryDroidWire } from "../wire/factory-droid";
|
|
11
12
|
import { apiRouteFor } from "./behavior";
|
|
@@ -20,11 +21,16 @@ export interface FactoryDroidLimits {
|
|
|
20
21
|
maxTokens: number | null;
|
|
21
22
|
}
|
|
22
23
|
|
|
23
|
-
/**
|
|
24
|
+
/**
|
|
25
|
+
* Standard Credits rates: `input` is the per-token weight; `output` and
|
|
26
|
+
* `cacheRead` multiply it. `effectiveRates` are dated cards (ascending) that
|
|
27
|
+
* overwrite those legs from their date, as on `cost-patch`.
|
|
28
|
+
*/
|
|
24
29
|
export interface FactoryDroidCreditRates {
|
|
25
30
|
input: number;
|
|
26
31
|
output?: number;
|
|
27
32
|
cacheRead?: number;
|
|
33
|
+
effectiveRates?: readonly DatedCostCard[];
|
|
28
34
|
}
|
|
29
35
|
|
|
30
36
|
/** Account gates on one registry model (the `entitlement` axis). */
|
|
@@ -95,6 +101,7 @@ function readPolicy(spec: ModelSpec<"factory-droid-agent">): FactoryDroidModelPo
|
|
|
95
101
|
const euMaxTokens = numberField(euLimits, "maxTokens");
|
|
96
102
|
const creditOutput = numberField(credits, "output");
|
|
97
103
|
const creditCacheRead = numberField(credits, "cacheRead");
|
|
104
|
+
const creditCards = parseDatedCostCards(credits, "credit-rates");
|
|
98
105
|
const featureFlag = stringField(entitlement, "featureFlag");
|
|
99
106
|
const deprecationFlag = stringField(entitlement, "deprecationFlag");
|
|
100
107
|
const baseVariant = stringField(entitlement, "baseVariant");
|
|
@@ -116,6 +123,7 @@ function readPolicy(spec: ModelSpec<"factory-droid-agent">): FactoryDroidModelPo
|
|
|
116
123
|
input: creditInput,
|
|
117
124
|
...(creditOutput !== undefined && { output: creditOutput }),
|
|
118
125
|
...(creditCacheRead !== undefined && { cacheRead: creditCacheRead }),
|
|
126
|
+
...(creditCards.length > 0 && { effectiveRates: creditCards }),
|
|
119
127
|
},
|
|
120
128
|
}),
|
|
121
129
|
...(priceProvider !== undefined && {
|
package/src/compat/resolve.ts
CHANGED
|
@@ -1337,8 +1337,10 @@ export function resolveDiscoveryApi(spec: ModelSpec<Api>, providerType: string):
|
|
|
1337
1337
|
* {@link resolveModelPolicy} without resolving the compat and thinking
|
|
1338
1338
|
* policies, which dominate its cost on catalog-wide scans.
|
|
1339
1339
|
*/
|
|
1340
|
-
export function resolveCatalogAxes(spec: CatalogPolicyInputs): Record<string, unknown> {
|
|
1341
|
-
|
|
1340
|
+
export function resolveCatalogAxes(spec: CatalogPolicyInputs, route?: ResolveRoute): Record<string, unknown> {
|
|
1341
|
+
const target = buildResolveTarget(spec, resolveIdentity(spec));
|
|
1342
|
+
target.upstream = route?.upstream;
|
|
1343
|
+
return resolveCascade(target).catalog;
|
|
1342
1344
|
}
|
|
1343
1345
|
|
|
1344
1346
|
/**
|
package/src/compat/revision.ts
CHANGED
|
@@ -76,6 +76,16 @@ export function compareRevision(a: Revision, b: Revision): number {
|
|
|
76
76
|
return a[0] - b[0] || a[1] - b[1] || a[2] - b[2];
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
+
/** Whether a revision string parses and is at or above `floor`; `undefined` or unparsable is never. */
|
|
80
|
+
export function revisionAtLeast(revision: string | undefined, floor: string): boolean {
|
|
81
|
+
if (revision === undefined) return false;
|
|
82
|
+
const parsedRevision = parseRevision(revision);
|
|
83
|
+
const parsedFloor = parseRevision(floor);
|
|
84
|
+
return (
|
|
85
|
+
parsedRevision !== undefined && parsedFloor !== undefined && compareRevision(parsedRevision, parsedFloor) >= 0
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
|
|
79
89
|
/** Renders a revision as its canonical `major.minor.patch` string. */
|
|
80
90
|
export function formatRevision(revision: Revision): string {
|
|
81
91
|
return `${revision[0]}.${revision[1]}.${revision[2]}`;
|
|
@@ -258,6 +258,8 @@ Opted in: Muse Code `muse-spark-1.3`, `muse-spark-1.3-contributor`, and the four
|
|
|
258
258
|
|
|
259
259
|
The routed-subscription registry axes (`upstream-rotation`, `region-upstreams-global|us|eu`, `region-limits-eu`, `credit-rates`, `list-price-from`, `routing-family`, `policy-aliases`, `entitlement`, `default-reasoning-off`) describe a gateway whose proxy fans one model out to several upstreams. They are read through `src/compat/factory-droid.ts` by Factory Droid discovery and its request provider, not materialized by `buildModel`. A provider-wide `region-upstreams-*` rule is the upstream serving table and a model rule replaces it for that region. `list-price-from "<provider>" ["<id>"]` shows a bundled row's list price beside the subscription's own billing; unlike seed values it is resolved at runtime and degrades to the seed's zero cost when the row is gone. See [`providers/factory-droid.kdl`](providers/factory-droid.kdl), whose wire and billing pool per model are `api-routes` and `quota-tiers` rules in `runtime/behavior.kdl`.
|
|
260
260
|
|
|
261
|
+
`credit-rates` accepts the same optional `effective-rates` block of named dated cards as `cost-patch`, parsed by the same code: each card has a distinct ISO UTC `effective-from` and any of `input`/`output`/`cache-read`, and the latest card at or before the moment the model browser renders overwrites the legs it names. A vendor credit promotion is two cards, the discounted rate from its start and the base rate again from its end, so a card left behind after the end changes nothing. See [`providers/factory-droid.kdl`](providers/factory-droid.kdl).
|
|
262
|
+
|
|
261
263
|
### Time-based pricing
|
|
262
264
|
|
|
263
265
|
The catalog object axis `time-based-cost` materializes into `ModelCost.timeBased`. It uses **named child objects**, not KDL arrays or repeated anonymous windows. For example, inside a matching provider/model scope:
|
|
@@ -10,4 +10,4 @@ login-order "openai-codex" "anthropic" "zai" "zai-coding-plan" "kimi-code" "open
|
|
|
10
10
|
"qianfan" "venice" "siliconflow" "siliconflow-cn" "synthetic" "nanogpt" "wafer-serverless" "coreweave" \
|
|
11
11
|
"vercel-ai-gateway" "cloudflare-ai-gateway" "cloudflare-workers-ai" "snowflake" "litellm" "kilo" "zenmux" "opencode-zen" "opencode-go" \
|
|
12
12
|
"yolo-auto" "tavily" "kagi" "exa" "parallel" "typesafe" "ollama" "ollama-cloud" "lm-studio" "llama.cpp" "vllm" \
|
|
13
|
-
"gmi-cloud" "stencil" "singularityapi-dev" "singularityapi-tech" "coralbricks" "modular"
|
|
13
|
+
"gmi-cloud" "stencil" "singularityapi-dev" "singularityapi-tech" "coralbricks" "modular" "mistral" "scaleway"
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
// Browser sign-in mirrors the Mistral Vibe CLI and mints a regular API key billed
|
|
2
|
+
// against the plan's Vibe Code quota; the hook returns it as a plain API key.
|
|
1
3
|
auth "mistral" {
|
|
2
4
|
name "Mistral"
|
|
5
|
+
env "MISTRAL_API_KEY"
|
|
6
|
+
login "custom" hook="mistral-vibe-sign-in"
|
|
3
7
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
auth "scaleway" {
|
|
2
|
+
name "Scaleway"
|
|
3
|
+
login "api-key" {
|
|
4
|
+
auth-url "https://console.scaleway.com/iam/api-keys"
|
|
5
|
+
instructions "Create an API key on the Scaleway console and paste its Secret Key (SCW_SECRET_KEY)"
|
|
6
|
+
// Scaleway secret keys are UUID-shaped opaque strings with no `sk-`
|
|
7
|
+
// prefix, so the placeholder must not teach a credential shape the
|
|
8
|
+
// console never issues.
|
|
9
|
+
prompt "Paste your Scaleway secret key" placeholder="..."
|
|
10
|
+
normalize "strip-bearer"
|
|
11
|
+
// `/v1/models` is key-protected: it answers 401 without a bearer key
|
|
12
|
+
// and 403 to a rejected one (verified 2026-10-21), so login validates
|
|
13
|
+
// the pasted key there. The probe bills nothing, unlike a
|
|
14
|
+
// chat-completions round-trip against a prepaid balance.
|
|
15
|
+
validate "models-endpoint" label="Scaleway" url="https://api.scaleway.ai/v1/models"
|
|
16
|
+
}
|
|
17
|
+
}
|