@linxiraos/pi-catalog 1.1.27 → 1.2.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 +4 -17
- package/dist/types/compat/auth-ids.d.ts +2 -2
- package/dist/types/compat/judgment-questions.d.ts +7 -0
- package/dist/types/compat/prompt-cache-lookback.d.ts +7 -0
- package/dist/types/compat/provider-ids.d.ts +1 -1
- package/dist/types/compat/reasoning-carry.d.ts +21 -0
- package/dist/types/compat/request-size.d.ts +7 -0
- package/dist/types/compat/taxonomy.d.ts +0 -10
- package/dist/types/compat/types.d.ts +4 -0
- package/dist/types/discovery/antigravity.d.ts +13 -3
- package/dist/types/model-manager.d.ts +16 -0
- package/dist/types/provider-models/account-access.d.ts +11 -0
- package/dist/types/provider-models/google.d.ts +23 -1
- package/dist/types/provider-models/openai-compat.d.ts +19 -1
- package/dist/types/provider-models/special.d.ts +2 -0
- package/dist/types/types.d.ts +106 -20
- package/dist/types/wire/cloudflare-ai-gateway.d.ts +0 -10
- package/dist/types/wire/cloudflare.d.ts +10 -0
- package/dist/types/wire/factory-droid.d.ts +1 -1
- package/package.json +4 -4
- package/src/build.ts +39 -35
- package/src/compat/auth-ids.ts +4 -0
- package/src/compat/axes.ts +37 -3
- package/src/compat/cascade.ts +6 -1
- package/src/compat/judgment-questions.ts +12 -0
- package/src/compat/prompt-cache-lookback.ts +23 -0
- package/src/compat/provider-ids.d.ts +1 -1
- package/src/compat/provider-ids.ts +2 -0
- package/src/compat/reasoning-carry.ts +64 -0
- package/src/compat/request-size.ts +25 -0
- package/src/compat/resolve.ts +16 -2
- package/src/compat/rules/README.md +12 -2
- package/src/compat/rules/auth/_order.kdl +2 -2
- package/src/compat/rules/auth/cloudflare-workers-ai.kdl +9 -0
- package/src/compat/rules/auth/coralbricks.kdl +13 -0
- package/src/compat/rules/auth/minimax-code-cn.kdl +1 -1
- package/src/compat/rules/auth/minimax-code.kdl +1 -1
- package/src/compat/rules/auth/openai-codex.kdl +1 -1
- package/src/compat/rules/auth/stencil.kdl +1 -1
- package/src/compat/rules/classes/anthropic.kdl +87 -1
- package/src/compat/rules/classes/deepseek.kdl +6 -0
- package/src/compat/rules/classes/glm.kdl +16 -7
- package/src/compat/rules/classes/kimi.kdl +8 -0
- package/src/compat/rules/classes/minimax.kdl +31 -1
- package/src/compat/rules/classes/openai.kdl +5 -3
- package/src/compat/rules/classes/qwen.kdl +2 -1
- package/src/compat/rules/providers/abliteration.kdl +7 -0
- package/src/compat/rules/providers/anthropic.kdl +13 -0
- package/src/compat/rules/providers/apple.kdl +1 -0
- package/src/compat/rules/providers/cloudflare-workers-ai.kdl +34 -0
- package/src/compat/rules/providers/coralbricks.kdl +62 -0
- package/src/compat/rules/providers/cursor.kdl +22 -0
- package/src/compat/rules/providers/devin.kdl +2 -2
- package/src/compat/rules/providers/factory-droid.kdl +77 -13
- package/src/compat/rules/providers/github-copilot.kdl +29 -0
- package/src/compat/rules/providers/groq.kdl +21 -0
- package/src/compat/rules/providers/judgment-questions.kdl +53 -0
- package/src/compat/rules/providers/litellm.kdl +4 -1
- package/src/compat/rules/providers/minimax-code-cn.kdl +10 -5
- package/src/compat/rules/providers/minimax-code.kdl +10 -5
- package/src/compat/rules/providers/mistral.kdl +25 -1
- package/src/compat/rules/providers/moonshot.kdl +11 -0
- package/src/compat/rules/providers/openai-codex.kdl +17 -13
- package/src/compat/rules/providers/openai.kdl +22 -0
- package/src/compat/rules/providers/opencode-zen.kdl +20 -0
- package/src/compat/rules/providers/openrouter.kdl +6 -0
- package/src/compat/rules/providers/zhipu-coding-plan.kdl +10 -0
- package/src/compat/rules/runtime/behavior.kdl +7 -4
- package/src/compat/rules/taxonomy/_collapse.kdl +18 -0
- package/src/compat/rules.json +1 -1
- package/src/compat/taxonomy.d.ts +0 -10
- package/src/compat/taxonomy.ts +3 -3
- package/src/compat/types.d.ts +4 -0
- package/src/compat/types.ts +4 -0
- package/src/discovery/antigravity.d.ts +13 -3
- package/src/discovery/antigravity.ts +33 -17
- package/src/discovery/cursor.ts +2 -1
- package/src/discovery/devin.ts +20 -2
- package/src/model-cache.ts +113 -47
- package/src/model-manager.d.ts +16 -0
- package/src/model-manager.ts +107 -41
- package/src/model-tokenizer.ts +3 -0
- package/src/models.json +1 -1
- package/src/provider-models/account-access.d.ts +11 -0
- package/src/provider-models/account-access.ts +30 -0
- package/src/provider-models/cache-provider-id.ts +3 -2
- package/src/provider-models/descriptors.ts +4 -0
- package/src/provider-models/google.d.ts +23 -1
- package/src/provider-models/google.ts +70 -14
- package/src/provider-models/openai-compat.d.ts +19 -1
- package/src/provider-models/openai-compat.ts +215 -31
- package/src/provider-models/special.d.ts +2 -0
- package/src/provider-models/special.ts +15 -18
- package/src/types.d.ts +106 -20
- package/src/types.ts +149 -17
- package/src/wire/cloudflare-ai-gateway.d.ts +0 -10
- package/src/wire/cloudflare-ai-gateway.ts +0 -36
- package/src/wire/cloudflare.ts +40 -0
- package/src/wire/factory-droid.d.ts +1 -1
- package/src/wire/factory-droid.ts +1 -1
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Api, ModelSpec } from "../types";
|
|
2
|
+
/**
|
|
3
|
+
* Merge per-account discovery catalogs into one list, deduped by model id.
|
|
4
|
+
*
|
|
5
|
+
* The first catalog to list an id supplies its spec; {@link ModelSpec.accountAccess}
|
|
6
|
+
* is merged from every catalog listing that id, so credential selection can
|
|
7
|
+
* route account-gated models to the accounts that serve them. Used by
|
|
8
|
+
* multi-account discovery for Codex and Antigravity, whose rosters differ per
|
|
9
|
+
* account.
|
|
10
|
+
*/
|
|
11
|
+
export declare function unionAccountCatalogs<TApi extends Api>(catalogs: readonly (readonly ModelSpec<TApi>[])[]): ModelSpec<TApi>[];
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { Api, ModelSpec } from "../types";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Merge per-account discovery catalogs into one list, deduped by model id.
|
|
5
|
+
*
|
|
6
|
+
* The first catalog to list an id supplies its spec; {@link ModelSpec.accountAccess}
|
|
7
|
+
* is merged from every catalog listing that id, so credential selection can
|
|
8
|
+
* route account-gated models to the accounts that serve them. Used by
|
|
9
|
+
* multi-account discovery for Codex and Antigravity, whose rosters differ per
|
|
10
|
+
* account.
|
|
11
|
+
*/
|
|
12
|
+
export function unionAccountCatalogs<TApi extends Api>(
|
|
13
|
+
catalogs: readonly (readonly ModelSpec<TApi>[])[],
|
|
14
|
+
): ModelSpec<TApi>[] {
|
|
15
|
+
const byId = new Map<string, ModelSpec<TApi>>();
|
|
16
|
+
for (const catalog of catalogs) {
|
|
17
|
+
for (const model of catalog) {
|
|
18
|
+
const existing = byId.get(model.id);
|
|
19
|
+
if (!existing) {
|
|
20
|
+
byId.set(model.id, model);
|
|
21
|
+
} else if (model.accountAccess) {
|
|
22
|
+
byId.set(model.id, {
|
|
23
|
+
...existing,
|
|
24
|
+
accountAccess: { ...existing.accountAccess, ...model.accountAccess },
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return [...byId.values()];
|
|
30
|
+
}
|
|
@@ -108,11 +108,12 @@ export function resolveModelCacheProviderId(providerId: string, options: ModelCa
|
|
|
108
108
|
// authoritative model rows.
|
|
109
109
|
// v3 invalidates zero-price rows written before rich lanes were priced
|
|
110
110
|
// from the KDL rate card; v4 keys the scope on the token's stable
|
|
111
|
-
// account subject instead of the rotating JWT
|
|
111
|
+
// account subject instead of the rotating JWT; v5 drops rows that still
|
|
112
|
+
// mark the account default `isProviderDefault`, which startup now honors.
|
|
112
113
|
const baseUrl = (options.baseUrl ?? CURSOR_DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
113
114
|
const apiKey = options.apiKey ?? "";
|
|
114
115
|
const scope = `${cursorCredentialSubject(apiKey) ?? apiKey}\u0000${baseUrl}`;
|
|
115
|
-
return `cursor:rich-models-
|
|
116
|
+
return `cursor:rich-models-v5:${Bun.hash(scope).toString(36)}`;
|
|
116
117
|
}
|
|
117
118
|
case "charm-hyper": {
|
|
118
119
|
// Discovery is authoritative for this gateway, so a warm cache is served
|
|
@@ -29,6 +29,7 @@ import {
|
|
|
29
29
|
clinePassModelManagerOptions,
|
|
30
30
|
cloudflareAiGatewayModelManagerOptions,
|
|
31
31
|
commandCodeModelManagerOptions,
|
|
32
|
+
coralbricksModelManagerOptions,
|
|
32
33
|
coreWeaveModelManagerOptions,
|
|
33
34
|
deepinfraModelManagerOptions,
|
|
34
35
|
deepseekModelManagerOptions,
|
|
@@ -78,6 +79,7 @@ import {
|
|
|
78
79
|
zhipuCodingPlanModelManagerOptions,
|
|
79
80
|
} from "./openai-compat";
|
|
80
81
|
import {
|
|
82
|
+
cloudflareWorkersAiModelManagerOptions,
|
|
81
83
|
cursorModelManagerOptions,
|
|
82
84
|
devinModelManagerOptions,
|
|
83
85
|
factoryDroidModelManagerOptions,
|
|
@@ -104,7 +106,9 @@ const MODEL_MANAGER_FACTORIES: Readonly<Partial<Record<KnownProvider, ModelManag
|
|
|
104
106
|
cerebras: config => cerebrasModelManagerOptions(config),
|
|
105
107
|
"charm-hyper": config => charmHyperModelManagerOptions(config),
|
|
106
108
|
"cloudflare-ai-gateway": config => cloudflareAiGatewayModelManagerOptions(config),
|
|
109
|
+
"cloudflare-workers-ai": () => cloudflareWorkersAiModelManagerOptions(),
|
|
107
110
|
commandcode: config => commandCodeModelManagerOptions(config),
|
|
111
|
+
coralbricks: config => coralbricksModelManagerOptions(config),
|
|
108
112
|
cursor: config => cursorModelManagerOptions(config),
|
|
109
113
|
deepinfra: config => deepinfraModelManagerOptions(config),
|
|
110
114
|
deepseek: config => deepseekModelManagerOptions(config),
|
|
@@ -11,8 +11,30 @@ export interface GoogleVertexModelManagerConfig {
|
|
|
11
11
|
signal?: AbortSignal;
|
|
12
12
|
fetch?: FetchImpl;
|
|
13
13
|
}
|
|
14
|
+
/** One Antigravity OAuth account whose `fetchAvailableModels` roster discovery reads. */
|
|
15
|
+
export interface GoogleAntigravityAccount {
|
|
16
|
+
/** OAuth access token used for `Authorization: Bearer ...`. */
|
|
17
|
+
accessToken: string;
|
|
18
|
+
/**
|
|
19
|
+
* Credential identity recorded in {@link ModelSpec.accountAccess} for every
|
|
20
|
+
* model this account serves (see `oauthAccountKey` in `@linxiraos/pi-ai`).
|
|
21
|
+
* When any account lacks one, no model records per-account access.
|
|
22
|
+
*/
|
|
23
|
+
accountKey?: string;
|
|
24
|
+
}
|
|
14
25
|
export interface GoogleAntigravityModelManagerConfig {
|
|
15
|
-
|
|
26
|
+
/**
|
|
27
|
+
* Resolves every configured Antigravity account at discovery time. Rosters
|
|
28
|
+
* are plan-scoped (Claude 5.5 is served to some accounts only) and
|
|
29
|
+
* authoritative, so each account's roster is fetched and the results are
|
|
30
|
+
* unioned; reading one account's roster would prune models only its
|
|
31
|
+
* siblings serve (#14924).
|
|
32
|
+
*
|
|
33
|
+
* Returns `null` to abort discovery (e.g. an account's credential failed to
|
|
34
|
+
* refresh), keeping the previous/bundled catalog instead of caching a
|
|
35
|
+
* partial one.
|
|
36
|
+
*/
|
|
37
|
+
resolveAccounts?: () => Promise<readonly GoogleAntigravityAccount[] | null>;
|
|
16
38
|
endpoint?: string;
|
|
17
39
|
fetch?: FetchImpl;
|
|
18
40
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { logger } from "@linxiraos/pi-utils";
|
|
1
2
|
import { reviewedCollapseTable } from "../compat/collapse";
|
|
2
3
|
import { classifyModel } from "../compat/taxonomy";
|
|
3
4
|
import { providerEntry } from "../compat/providers";
|
|
@@ -5,7 +6,8 @@ import { fetchAntigravityDiscoveryModels } from "../discovery/antigravity";
|
|
|
5
6
|
import { fetchGeminiModels } from "../discovery/gemini";
|
|
6
7
|
import { fetchGeminiCliQuotaModels } from "../discovery/gemini-cli";
|
|
7
8
|
import type { ModelManagerOptions } from "../model-manager";
|
|
8
|
-
import type { FetchImpl } from "../types";
|
|
9
|
+
import type { FetchImpl, ModelSpec } from "../types";
|
|
10
|
+
import { unionAccountCatalogs } from "./account-access";
|
|
9
11
|
|
|
10
12
|
export interface GoogleModelManagerConfig {
|
|
11
13
|
apiKey?: string;
|
|
@@ -20,8 +22,31 @@ export interface GoogleVertexModelManagerConfig {
|
|
|
20
22
|
fetch?: FetchImpl;
|
|
21
23
|
}
|
|
22
24
|
|
|
25
|
+
/** One Antigravity OAuth account whose `fetchAvailableModels` roster discovery reads. */
|
|
26
|
+
export interface GoogleAntigravityAccount {
|
|
27
|
+
/** OAuth access token used for `Authorization: Bearer ...`. */
|
|
28
|
+
accessToken: string;
|
|
29
|
+
/**
|
|
30
|
+
* Credential identity recorded in {@link ModelSpec.accountAccess} for every
|
|
31
|
+
* model this account serves (see `oauthAccountKey` in `@linxiraos/pi-ai`).
|
|
32
|
+
* When any account lacks one, no model records per-account access.
|
|
33
|
+
*/
|
|
34
|
+
accountKey?: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
23
37
|
export interface GoogleAntigravityModelManagerConfig {
|
|
24
|
-
|
|
38
|
+
/**
|
|
39
|
+
* Resolves every configured Antigravity account at discovery time. Rosters
|
|
40
|
+
* are plan-scoped (Claude 5.5 is served to some accounts only) and
|
|
41
|
+
* authoritative, so each account's roster is fetched and the results are
|
|
42
|
+
* unioned; reading one account's roster would prune models only its
|
|
43
|
+
* siblings serve (#14924).
|
|
44
|
+
*
|
|
45
|
+
* Returns `null` to abort discovery (e.g. an account's credential failed to
|
|
46
|
+
* refresh), keeping the previous/bundled catalog instead of caching a
|
|
47
|
+
* partial one.
|
|
48
|
+
*/
|
|
49
|
+
resolveAccounts?: () => Promise<readonly GoogleAntigravityAccount[] | null>;
|
|
25
50
|
endpoint?: string;
|
|
26
51
|
fetch?: FetchImpl;
|
|
27
52
|
}
|
|
@@ -71,18 +96,49 @@ export function googleVertexModelManagerOptions(_config?: GoogleVertexModelManag
|
|
|
71
96
|
export function googleAntigravityModelManagerOptions(
|
|
72
97
|
config?: GoogleAntigravityModelManagerConfig,
|
|
73
98
|
): ModelManagerOptions<"google-gemini-cli"> {
|
|
74
|
-
const
|
|
99
|
+
const resolveAccounts = config?.resolveAccounts;
|
|
75
100
|
return {
|
|
76
101
|
providerId: "google-antigravity",
|
|
77
102
|
dynamicModelsAuthoritative: providerEntry("google-antigravity")?.dynamicModelsAuthoritative === true,
|
|
78
|
-
...(
|
|
103
|
+
...(resolveAccounts
|
|
79
104
|
? {
|
|
80
|
-
fetchDynamicModels: () =>
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
105
|
+
fetchDynamicModels: async () => {
|
|
106
|
+
const accounts = await resolveAccounts();
|
|
107
|
+
if (!accounts || accounts.length === 0) return null;
|
|
108
|
+
const fetcher = toDiscoveryFetch(config?.fetch);
|
|
109
|
+
const rosters = await Promise.all(
|
|
110
|
+
accounts.map(async account => ({
|
|
111
|
+
accountKey: account.accountKey,
|
|
112
|
+
result: await fetchAntigravityDiscoveryModels({
|
|
113
|
+
token: account.accessToken,
|
|
114
|
+
endpoint: config?.endpoint,
|
|
115
|
+
fetcher,
|
|
116
|
+
}),
|
|
117
|
+
})),
|
|
118
|
+
);
|
|
119
|
+
const catalogs: { accountKey: string | undefined; models: ModelSpec<"google-gemini-cli">[] }[] = [];
|
|
120
|
+
for (const { accountKey, result } of rosters) {
|
|
121
|
+
// A transient failure would leave the union partial; keep the previous catalog.
|
|
122
|
+
if (!result) return null;
|
|
123
|
+
if (result.rejectedStatus !== undefined) {
|
|
124
|
+
logger.warn("Antigravity model discovery skipped an account whose credential was rejected", {
|
|
125
|
+
accountKey,
|
|
126
|
+
status: result.rejectedStatus,
|
|
127
|
+
});
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
catalogs.push({ accountKey, models: result.models });
|
|
131
|
+
}
|
|
132
|
+
if (catalogs.length === 0) return null;
|
|
133
|
+
const tagAccess = catalogs.every(catalog => catalog.accountKey !== undefined);
|
|
134
|
+
return unionAccountCatalogs(
|
|
135
|
+
catalogs.map(({ accountKey, models }) =>
|
|
136
|
+
tagAccess && accountKey !== undefined
|
|
137
|
+
? models.map(model => ({ ...model, accountAccess: { [accountKey]: {} } }))
|
|
138
|
+
: models,
|
|
139
|
+
),
|
|
140
|
+
);
|
|
141
|
+
},
|
|
86
142
|
}
|
|
87
143
|
: undefined),
|
|
88
144
|
};
|
|
@@ -103,19 +159,19 @@ export function googleGeminiCliModelManagerOptions(
|
|
|
103
159
|
if (collapseTable === undefined) {
|
|
104
160
|
throw new Error("missing reviewed collapse table for google-gemini-cli");
|
|
105
161
|
}
|
|
106
|
-
const
|
|
162
|
+
const result = await fetchAntigravityDiscoveryModels({
|
|
107
163
|
token,
|
|
108
164
|
fetcher,
|
|
109
|
-
collapseTable
|
|
165
|
+
collapseTable,
|
|
110
166
|
});
|
|
111
167
|
// Antigravity's fetchAvailableModels is unreachable for
|
|
112
168
|
// credentials without Antigravity entitlement (Code Assist
|
|
113
169
|
// Standard returns HTTP 403). Fall back to the account's own
|
|
114
170
|
// retrieveUserQuota list on Cloud Code Assist.
|
|
115
|
-
if (
|
|
171
|
+
if (result === null || result.rejectedStatus !== undefined) {
|
|
116
172
|
return fetchGeminiCliQuotaModels({ token, projectId: config?.projectId, endpoint, fetcher });
|
|
117
173
|
}
|
|
118
|
-
return models
|
|
174
|
+
return result.models
|
|
119
175
|
.filter(m => classifyModel("google-gemini-cli", m.id, { lenient: true }).class === "gemini")
|
|
120
176
|
.map(m => ({
|
|
121
177
|
...m,
|
|
@@ -69,7 +69,7 @@ export interface OpenAIModelManagerConfig {
|
|
|
69
69
|
baseUrl?: string;
|
|
70
70
|
fetch?: FetchImpl;
|
|
71
71
|
}
|
|
72
|
-
export declare function openaiModelManagerOptions(config?: OpenAIModelManagerConfig): ModelManagerOptions<
|
|
72
|
+
export declare function openaiModelManagerOptions(config?: OpenAIModelManagerConfig): ModelManagerOptions<Api>;
|
|
73
73
|
/**
|
|
74
74
|
* Re-derive the generated pro-reasoning aliases (`gpt-5.6-*-pro`) for the
|
|
75
75
|
* first-party `openai` gpt-5.6 rows. Each alias inherits the base row's
|
|
@@ -154,6 +154,19 @@ export interface DeepinfraModelManagerConfig {
|
|
|
154
154
|
* model availability at runtime is still gated on credentials by the registry.
|
|
155
155
|
*/
|
|
156
156
|
export declare function deepinfraModelManagerOptions(config?: DeepinfraModelManagerConfig): ModelManagerOptions<"openai-completions">;
|
|
157
|
+
export declare const CORALBRICKS_BASE_URL = "https://inference.coralbricks.ai/v1";
|
|
158
|
+
/** CoralBricks OpenAI-compatible discovery configuration. */
|
|
159
|
+
export interface CoralbricksModelManagerConfig {
|
|
160
|
+
apiKey?: string;
|
|
161
|
+
baseUrl?: string;
|
|
162
|
+
fetch?: FetchImpl;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Builds CoralBricks' model-discovery manager. `/v1/models` is key-protected
|
|
166
|
+
* (401 without a bearer key), so a keyless config serves only the bundled
|
|
167
|
+
* reviewed seed rows; with a key, live rows are authoritative over the bundle.
|
|
168
|
+
*/
|
|
169
|
+
export declare function coralbricksModelManagerOptions(config?: CoralbricksModelManagerConfig): ModelManagerOptions<"openai-completions">;
|
|
157
170
|
export interface XaiModelManagerConfig {
|
|
158
171
|
apiKey?: string;
|
|
159
172
|
baseUrl?: string;
|
|
@@ -339,6 +352,11 @@ export interface OpenRouterModelManagerConfig {
|
|
|
339
352
|
baseUrl?: string;
|
|
340
353
|
fetch?: FetchImpl;
|
|
341
354
|
}
|
|
355
|
+
/**
|
|
356
|
+
* OpenRouter's Decisions API lives at `/api/alpha`, a sibling of the `/api/v1`
|
|
357
|
+
* chat root; derive it so a custom gateway base URL keeps both aligned.
|
|
358
|
+
*/
|
|
359
|
+
export declare function openrouterDecisionsBaseUrl(chatBaseUrl: string): string;
|
|
342
360
|
export declare function openrouterModelManagerOptions(config?: OpenRouterModelManagerConfig): ModelManagerOptions<Api>;
|
|
343
361
|
export interface ZenMuxModelManagerConfig {
|
|
344
362
|
apiKey?: string;
|
|
@@ -1071,8 +1071,8 @@ export interface OpenAIModelManagerConfig {
|
|
|
1071
1071
|
fetch?: FetchImpl;
|
|
1072
1072
|
}
|
|
1073
1073
|
|
|
1074
|
-
export function openaiModelManagerOptions(config?: OpenAIModelManagerConfig): ModelManagerOptions<
|
|
1075
|
-
|
|
1074
|
+
export function openaiModelManagerOptions(config?: OpenAIModelManagerConfig): ModelManagerOptions<Api> {
|
|
1075
|
+
const options = createOpenAICompatibleModelManagerOptions({
|
|
1076
1076
|
api: "openai-responses",
|
|
1077
1077
|
providerId: "openai",
|
|
1078
1078
|
defaultBaseUrl: OPENAI_API_BASE_URL,
|
|
@@ -1081,6 +1081,30 @@ export function openaiModelManagerOptions(config?: OpenAIModelManagerConfig): Mo
|
|
|
1081
1081
|
filterModel: (_entry, model, references) => isLikelyOpenAIResponsesModelId(model.id, references),
|
|
1082
1082
|
mapModel: mapWithBundledReference,
|
|
1083
1083
|
});
|
|
1084
|
+
const fetchDiscovered = options.fetchDynamicModels;
|
|
1085
|
+
if (!fetchDiscovered) return options;
|
|
1086
|
+
return {
|
|
1087
|
+
...options,
|
|
1088
|
+
fetchDynamicModels: async () => appendUnbundledSeedModels("openai", await fetchDiscovered()),
|
|
1089
|
+
};
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
/**
|
|
1093
|
+
* Append the authored seed rows that neither discovery nor the bundled catalog
|
|
1094
|
+
* carries yet: a seed added since the last models.json regeneration (the
|
|
1095
|
+
* Decisions judge row) reaches runtime through a successful discovery. Seeds
|
|
1096
|
+
* already bundled keep resolving through the bundled slice, and the static
|
|
1097
|
+
* catalog (and with it the cache fingerprint) stays the bundled one.
|
|
1098
|
+
*/
|
|
1099
|
+
function appendUnbundledSeedModels(
|
|
1100
|
+
providerId: GeneratedProvider,
|
|
1101
|
+
discovered: readonly ModelSpec<Api>[] | null,
|
|
1102
|
+
): readonly ModelSpec<Api>[] | null {
|
|
1103
|
+
if (!discovered) return null;
|
|
1104
|
+
const known = new Set(getBundledModels(providerId).map(model => model.id));
|
|
1105
|
+
for (const model of discovered) known.add(model.id);
|
|
1106
|
+
const seeds = seedModels(providerId).filter(seed => !known.has(seed.id));
|
|
1107
|
+
return seeds.length === 0 ? discovered : [...discovered, ...seeds.map(seed => ({ ...seed }))];
|
|
1084
1108
|
}
|
|
1085
1109
|
|
|
1086
1110
|
/**
|
|
@@ -1618,10 +1642,171 @@ export function deepinfraModelManagerOptions(
|
|
|
1618
1642
|
return {
|
|
1619
1643
|
providerId: "deepinfra",
|
|
1620
1644
|
dynamicModelsAuthoritative: true,
|
|
1645
|
+
// `vision`/`vlm` tags are the whole truth for modality on this host.
|
|
1646
|
+
dynamicInputAuthoritative: true,
|
|
1621
1647
|
fetchDynamicModels: () => fetchDeepinfraModels({ baseUrl, apiKey, fetch: config?.fetch, references }),
|
|
1622
1648
|
};
|
|
1623
1649
|
}
|
|
1624
1650
|
|
|
1651
|
+
// ---------------------------------------------------------------------------
|
|
1652
|
+
// CoralBricks
|
|
1653
|
+
// ---------------------------------------------------------------------------
|
|
1654
|
+
|
|
1655
|
+
export const CORALBRICKS_BASE_URL = "https://inference.coralbricks.ai/v1";
|
|
1656
|
+
|
|
1657
|
+
/** CoralBricks OpenAI-compatible discovery configuration. */
|
|
1658
|
+
export interface CoralbricksModelManagerConfig {
|
|
1659
|
+
apiKey?: string;
|
|
1660
|
+
baseUrl?: string;
|
|
1661
|
+
fetch?: FetchImpl;
|
|
1662
|
+
}
|
|
1663
|
+
|
|
1664
|
+
/**
|
|
1665
|
+
* The CoralBricks-specific fields of a `GET /v1/models` row: per-million USD
|
|
1666
|
+
* pricing and the capability flags Coral documents as authoritative
|
|
1667
|
+
* (https://www.coralbricks.ai/docs.md).
|
|
1668
|
+
*/
|
|
1669
|
+
interface CoralbricksModelRecord extends OpenAICompatibleModelRecord {
|
|
1670
|
+
context_length?: unknown;
|
|
1671
|
+
pricing?: unknown;
|
|
1672
|
+
supports_chat?: unknown;
|
|
1673
|
+
supports_image_input?: unknown;
|
|
1674
|
+
supports_tools?: unknown;
|
|
1675
|
+
supports_reasoning?: unknown;
|
|
1676
|
+
reasoning?: unknown;
|
|
1677
|
+
}
|
|
1678
|
+
|
|
1679
|
+
/**
|
|
1680
|
+
* Read one live per-million price; a missing or negative field falls back to
|
|
1681
|
+
* the bundled reference. An explicit `0` is a real price: the manager keeps it
|
|
1682
|
+
* because `coralbricksModelManagerOptions` declares `dynamicCostAuthoritative`.
|
|
1683
|
+
*/
|
|
1684
|
+
function coralRate(value: unknown, fallback: number): number {
|
|
1685
|
+
const parsed = toNumber(value);
|
|
1686
|
+
return parsed !== undefined && parsed >= 0 ? parsed : fallback;
|
|
1687
|
+
}
|
|
1688
|
+
|
|
1689
|
+
/**
|
|
1690
|
+
* Map one CoralBricks catalog row to a chat model spec; non-chat rows
|
|
1691
|
+
* (`supports_chat: false`) are dropped. Pricing arrives in Coral's own
|
|
1692
|
+
* per-million field names; `cached_input_per_m` is $0 on every model and a
|
|
1693
|
+
* missing field falls back to the bundled reference.
|
|
1694
|
+
* Live reasoning capabilities and controls override the bundled fallback.
|
|
1695
|
+
* The endpoint publishes no output cap, so `maxTokens` keeps its reference
|
|
1696
|
+
* value rather than being invented from the context window.
|
|
1697
|
+
*/
|
|
1698
|
+
function mapCoralbricksModel(
|
|
1699
|
+
entry: CoralbricksModelRecord,
|
|
1700
|
+
defaults: ModelSpec<"openai-completions">,
|
|
1701
|
+
reference: ModelSpec<"openai-completions"> | undefined,
|
|
1702
|
+
): ModelSpec<"openai-completions"> | null {
|
|
1703
|
+
if (entry.supports_chat === false) {
|
|
1704
|
+
return null;
|
|
1705
|
+
}
|
|
1706
|
+
const pricing = isRecord(entry.pricing) ? entry.pricing : {};
|
|
1707
|
+
// A bundled reference may lend metadata, but its runner kind is not
|
|
1708
|
+
// evidence the chat roster advertised it.
|
|
1709
|
+
const { kind: _inheritedKind, ...chatReference } = reference ?? {};
|
|
1710
|
+
const hasReasoningFlag = typeof entry.supports_reasoning === "boolean";
|
|
1711
|
+
const reasoning = hasReasoningFlag
|
|
1712
|
+
? entry.supports_reasoning === true
|
|
1713
|
+
: (reference?.reasoning ?? defaults.reasoning);
|
|
1714
|
+
const controls = isRecord(entry.reasoning) ? entry.reasoning : undefined;
|
|
1715
|
+
const wireEfforts = controls?.supported_efforts;
|
|
1716
|
+
let thinking = reasoning ? reference?.thinking : undefined;
|
|
1717
|
+
let compat = reference?.compat;
|
|
1718
|
+
if (reasoning && Array.isArray(wireEfforts)) {
|
|
1719
|
+
const efforts = THINKING_EFFORTS.filter(effort => wireEfforts.includes(effort));
|
|
1720
|
+
// `none` is the server's off default, not an Effort (nor `minimal`).
|
|
1721
|
+
const defaultLevel = efforts.find(effort => effort === controls?.default_effort);
|
|
1722
|
+
thinking =
|
|
1723
|
+
efforts.length > 0
|
|
1724
|
+
? {
|
|
1725
|
+
mode: "effort",
|
|
1726
|
+
efforts,
|
|
1727
|
+
...(defaultLevel !== undefined && { defaultLevel }),
|
|
1728
|
+
...(typeof controls?.mandatory === "boolean" && { requiresEffort: controls.mandatory }),
|
|
1729
|
+
}
|
|
1730
|
+
: undefined;
|
|
1731
|
+
}
|
|
1732
|
+
if (hasReasoningFlag || Array.isArray(wireEfforts)) {
|
|
1733
|
+
// An explicit empty/unknown vocabulary must not regrow a guessed dial
|
|
1734
|
+
// from identity or KDL. Missing legacy metadata still uses the reference.
|
|
1735
|
+
compat = { ...compat, trustExplicitThinkingOnly: true };
|
|
1736
|
+
}
|
|
1737
|
+
if (reasoning && controls) {
|
|
1738
|
+
if (controls.mandatory === true || controls.disable === null) {
|
|
1739
|
+
compat = { ...compat, reasoningDisableMode: "lowest-effort" };
|
|
1740
|
+
} else if (isRecord(controls.disable) && controls.disable.reasoning_effort === "none") {
|
|
1741
|
+
compat = { ...compat, reasoningDisableMode: "none-effort" };
|
|
1742
|
+
}
|
|
1743
|
+
}
|
|
1744
|
+
const input: ("text" | "image")[] =
|
|
1745
|
+
entry.supports_image_input === true
|
|
1746
|
+
? ["text", "image"]
|
|
1747
|
+
: entry.supports_image_input === false
|
|
1748
|
+
? ["text"]
|
|
1749
|
+
: (reference?.input ?? defaults.input);
|
|
1750
|
+
return {
|
|
1751
|
+
...defaults,
|
|
1752
|
+
...chatReference,
|
|
1753
|
+
id: defaults.id,
|
|
1754
|
+
name: reference?.name ?? defaults.name,
|
|
1755
|
+
api: defaults.api,
|
|
1756
|
+
provider: defaults.provider,
|
|
1757
|
+
baseUrl: defaults.baseUrl,
|
|
1758
|
+
reasoning,
|
|
1759
|
+
thinking,
|
|
1760
|
+
compat,
|
|
1761
|
+
input,
|
|
1762
|
+
...(typeof entry.supports_tools === "boolean" ? { supportsTools: entry.supports_tools } : {}),
|
|
1763
|
+
cost: {
|
|
1764
|
+
input: coralRate(pricing.input_per_m, reference?.cost.input ?? 0),
|
|
1765
|
+
output: coralRate(pricing.output_per_m, reference?.cost.output ?? 0),
|
|
1766
|
+
cacheRead: coralRate(pricing.cached_input_per_m, reference?.cost.cacheRead ?? 0),
|
|
1767
|
+
cacheWrite: coralRate(pricing.cache_write_per_m, reference?.cost.cacheWrite ?? 0),
|
|
1768
|
+
},
|
|
1769
|
+
contextWindow: toPositiveNumber(entry.context_length, reference?.contextWindow ?? null),
|
|
1770
|
+
maxTokens: reference?.maxTokens ?? null,
|
|
1771
|
+
};
|
|
1772
|
+
}
|
|
1773
|
+
|
|
1774
|
+
/**
|
|
1775
|
+
* Builds CoralBricks' model-discovery manager. `/v1/models` is key-protected
|
|
1776
|
+
* (401 without a bearer key), so a keyless config serves only the bundled
|
|
1777
|
+
* reviewed seed rows; with a key, live rows are authoritative over the bundle.
|
|
1778
|
+
*/
|
|
1779
|
+
export function coralbricksModelManagerOptions(
|
|
1780
|
+
config?: CoralbricksModelManagerConfig,
|
|
1781
|
+
): ModelManagerOptions<"openai-completions"> {
|
|
1782
|
+
const apiKey = config?.apiKey;
|
|
1783
|
+
const baseUrl = config?.baseUrl ?? CORALBRICKS_BASE_URL;
|
|
1784
|
+
const references = createBundledReferenceMap<"openai-completions">("coralbricks");
|
|
1785
|
+
return {
|
|
1786
|
+
providerId: "coralbricks",
|
|
1787
|
+
dynamicModelsAuthoritative: true,
|
|
1788
|
+
dynamicReasoningAuthoritative: true,
|
|
1789
|
+
// `supports_image_input` is the row's whole truth for modality (Coral
|
|
1790
|
+
// answers unsupported content with `400 unsupported_content_type`).
|
|
1791
|
+
dynamicInputAuthoritative: true,
|
|
1792
|
+
// Coral's `pricing` block is the deployment tariff; an explicit live `0`
|
|
1793
|
+
// (a free model or no cache-write charge) must not revert to the bundle.
|
|
1794
|
+
dynamicCostAuthoritative: true,
|
|
1795
|
+
...(apiKey && {
|
|
1796
|
+
fetchDynamicModels: () =>
|
|
1797
|
+
fetchOpenAICompatibleModels({
|
|
1798
|
+
api: "openai-completions",
|
|
1799
|
+
provider: "coralbricks",
|
|
1800
|
+
baseUrl,
|
|
1801
|
+
apiKey,
|
|
1802
|
+
fetch: config?.fetch,
|
|
1803
|
+
mapModel: (entry, defaults) =>
|
|
1804
|
+
mapCoralbricksModel(entry as CoralbricksModelRecord, defaults, references.get(defaults.id)),
|
|
1805
|
+
}),
|
|
1806
|
+
}),
|
|
1807
|
+
};
|
|
1808
|
+
}
|
|
1809
|
+
|
|
1625
1810
|
// ---------------------------------------------------------------------------
|
|
1626
1811
|
// 6. xAI
|
|
1627
1812
|
// ---------------------------------------------------------------------------
|
|
@@ -3138,8 +3323,8 @@ function openCodeModelManagerOptions(
|
|
|
3138
3323
|
},
|
|
3139
3324
|
},
|
|
3140
3325
|
...(apiKey && {
|
|
3141
|
-
fetchDynamicModels: () =>
|
|
3142
|
-
fetchOpenAICompatibleModels<Api>({
|
|
3326
|
+
fetchDynamicModels: async () => {
|
|
3327
|
+
const discovered = await fetchOpenAICompatibleModels<Api>({
|
|
3143
3328
|
api: "openai-completions",
|
|
3144
3329
|
provider: providerId,
|
|
3145
3330
|
baseUrl: discoveryBaseUrl,
|
|
@@ -3197,7 +3382,12 @@ function openCodeModelManagerOptions(
|
|
|
3197
3382
|
};
|
|
3198
3383
|
},
|
|
3199
3384
|
fetch: config?.fetch,
|
|
3200
|
-
})
|
|
3385
|
+
});
|
|
3386
|
+
if (!discovered) return null;
|
|
3387
|
+
const seeds = seedModels(providerId).map(seed => ({ ...seed, baseUrl: basePath }));
|
|
3388
|
+
const seedIds = new Set(seeds.map(seed => seed.id));
|
|
3389
|
+
return [...discovered.filter(model => !seedIds.has(model.id)), ...seeds];
|
|
3390
|
+
},
|
|
3201
3391
|
}),
|
|
3202
3392
|
};
|
|
3203
3393
|
}
|
|
@@ -3290,8 +3480,9 @@ export interface OpenRouterModelManagerConfig {
|
|
|
3290
3480
|
* OpenRouter's Decisions API lives at `/api/alpha`, a sibling of the `/api/v1`
|
|
3291
3481
|
* chat root; derive it so a custom gateway base URL keeps both aligned.
|
|
3292
3482
|
*/
|
|
3293
|
-
function openrouterDecisionsBaseUrl(chatBaseUrl: string): string {
|
|
3294
|
-
|
|
3483
|
+
export function openrouterDecisionsBaseUrl(chatBaseUrl: string): string {
|
|
3484
|
+
const base = chatBaseUrl.replace(/\/+$/g, "");
|
|
3485
|
+
return base.endsWith("/v1") ? `${base.slice(0, -"/v1".length)}/alpha` : `${base}/alpha`;
|
|
3295
3486
|
}
|
|
3296
3487
|
|
|
3297
3488
|
function mapOpenRouterThinking(entry: OpenAICompatibleModelRecord): ThinkingConfig | undefined {
|
|
@@ -4264,6 +4455,7 @@ export function syntheticModelManagerOptions(
|
|
|
4264
4455
|
return {
|
|
4265
4456
|
providerId: "synthetic",
|
|
4266
4457
|
dynamicModelsAuthoritative: true,
|
|
4458
|
+
dynamicReasoningAuthoritative: true,
|
|
4267
4459
|
...(apiKey && {
|
|
4268
4460
|
fetchDynamicModels: () =>
|
|
4269
4461
|
fetchOpenAICompatibleModels({
|
|
@@ -6187,7 +6379,7 @@ function parseCopilotTokenPriceTier(value: unknown): CopilotTokenPriceTier | und
|
|
|
6187
6379
|
return undefined;
|
|
6188
6380
|
}
|
|
6189
6381
|
return {
|
|
6190
|
-
contextMax: toNumber(value.context_max),
|
|
6382
|
+
contextMax: toNumber(value.max_prompt_tokens) ?? toNumber(value.context_max),
|
|
6191
6383
|
inputPrice: toNumber(value.input_price),
|
|
6192
6384
|
outputPrice: toNumber(value.output_price),
|
|
6193
6385
|
cachePrice: toNumber(value.cache_price),
|
|
@@ -6303,6 +6495,9 @@ export function githubCopilotModelManagerOptions(config?: GithubCopilotModelMana
|
|
|
6303
6495
|
return {
|
|
6304
6496
|
providerId: "github-copilot",
|
|
6305
6497
|
cacheProviderId: resolveModelCacheProviderId("github-copilot", { apiKey: rawApiKey, baseUrl }),
|
|
6498
|
+
// Copilot discovery pre-applies the correct image fallback for omitted
|
|
6499
|
+
// `supports.vision`; the live row's modality is authoritative.
|
|
6500
|
+
dynamicInputAuthoritative: true,
|
|
6306
6501
|
dropCachedModelIdsOnStaticMismatch: COPILOT_CACHE_INVALIDATED_MODEL_IDS,
|
|
6307
6502
|
// COPILOT_API_HEADERS are compile-time wire identity constants, not
|
|
6308
6503
|
// credentials. The cache omits all request headers for
|
|
@@ -6373,12 +6568,12 @@ export function githubCopilotModelManagerOptions(config?: GithubCopilotModelMana
|
|
|
6373
6568
|
? ["text"]
|
|
6374
6569
|
: (reference?.input ?? defaults.input);
|
|
6375
6570
|
// With COPILOT_API_HEADERS the served window is the long-context
|
|
6376
|
-
// ceiling; the default tier
|
|
6377
|
-
//
|
|
6378
|
-
//
|
|
6379
|
-
//
|
|
6380
|
-
//
|
|
6381
|
-
//
|
|
6571
|
+
// ceiling; the default tier reports its prompt boundary in
|
|
6572
|
+
// token_prices.default.max_prompt_tokens (or legacy context_max).
|
|
6573
|
+
// Cap the base entry to the default tier — the long tier is
|
|
6574
|
+
// the opt-in `-1m` sibling below. On tiered legacy rows the
|
|
6575
|
+
// model-wide max_prompt_tokens may be tighter than the billed
|
|
6576
|
+
// default ceiling (#13912), so use the smaller bound.
|
|
6382
6577
|
const tokenPrices = extractCopilotTokenPrices(entry);
|
|
6383
6578
|
const billedDefaultMax = tokenPrices.defaultTier?.contextMax;
|
|
6384
6579
|
const tieredPromptBudget =
|
|
@@ -7132,23 +7327,12 @@ const MODELS_DEV_PROVIDER_DESCRIPTORS_CODING_PLANS: readonly ModelsDevProviderDe
|
|
|
7132
7327
|
allowsSyntheticReasoningContentForToolCalls: false,
|
|
7133
7328
|
},
|
|
7134
7329
|
}),
|
|
7135
|
-
// --- MiniMax
|
|
7136
|
-
|
|
7137
|
-
|
|
7138
|
-
|
|
7139
|
-
|
|
7140
|
-
|
|
7141
|
-
reasoningContentField: "reasoning_content",
|
|
7142
|
-
},
|
|
7143
|
-
}),
|
|
7144
|
-
openAiCompletionsDescriptor("minimax-cn-coding-plan", "minimax-code-cn", "https://api.minimaxi.com/v1", {
|
|
7145
|
-
compat: {
|
|
7146
|
-
supportsStore: false,
|
|
7147
|
-
supportsDeveloperRole: false,
|
|
7148
|
-
supportsReasoningEffort: false,
|
|
7149
|
-
reasoningContentField: "reasoning_content",
|
|
7150
|
-
},
|
|
7151
|
-
}),
|
|
7330
|
+
// --- MiniMax Token Plan ---
|
|
7331
|
+
// MiniMax documents its Anthropic-compatible API as the recommended
|
|
7332
|
+
// protocol: signed thinking blocks, `output_config.effort` depth control,
|
|
7333
|
+
// and prompt-cache usage reporting.
|
|
7334
|
+
anthropicMessagesDescriptor("minimax-coding-plan", "minimax-code", "https://api.minimax.io/anthropic"),
|
|
7335
|
+
anthropicMessagesDescriptor("minimax-cn-coding-plan", "minimax-code-cn", "https://api.minimaxi.com/anthropic"),
|
|
7152
7336
|
// --- Alibaba Coding Plan ---
|
|
7153
7337
|
openAiCompletionsDescriptor(
|
|
7154
7338
|
"alibaba-coding-plan",
|
|
@@ -65,6 +65,8 @@ export declare function devinModelManagerOptions(config?: DevinModelManagerConfi
|
|
|
65
65
|
export declare function factoryDroidModelManagerOptions(config?: FactoryDroidModelDiscoveryOptions): ModelManagerOptions<"factory-droid-agent">;
|
|
66
66
|
export declare function localModelManagerOptions(): ModelManagerOptions<"local-inference">;
|
|
67
67
|
export declare function webModelManagerOptions(): ModelManagerOptions<"web-search">;
|
|
68
|
+
/** Cloudflare Workers AI judges: the authored Clef rows; the account id resolves per request. */
|
|
69
|
+
export declare function cloudflareWorkersAiModelManagerOptions(): ModelManagerOptions<"workers-ai-systemone">;
|
|
68
70
|
/** Credentials and endpoint overrides for the TypeSafe catalog manager. */
|
|
69
71
|
export interface TypeSafeModelManagerConfig {
|
|
70
72
|
apiKey?: string;
|