@opengeni/config 0.10.0 → 0.10.1
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/dist/index.d.ts +66 -0
- package/dist/index.js +216 -10
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/index.ts +251 -15
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opengeni/config",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.1",
|
|
4
4
|
"description": "OpenGeni runtime configuration: settings resolution, deployment knobs, and config validation shared across the server packages.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
36
|
"@opengeni/codex": "^0.2.9",
|
|
37
|
-
"@opengeni/contracts": "^0.31.
|
|
37
|
+
"@opengeni/contracts": "^0.31.1",
|
|
38
38
|
"zod": "^4.2.1"
|
|
39
39
|
},
|
|
40
40
|
"engines": {
|
package/src/index.ts
CHANGED
|
@@ -355,6 +355,10 @@ const SettingsSchema = z.object({
|
|
|
355
355
|
openaiBaseUrl: z.string().optional(),
|
|
356
356
|
openaiModel: z.string().default("gpt-5.6-sol"),
|
|
357
357
|
openaiAllowedModels: z.string().default("gpt-5.6-sol,gpt-5.6-terra,gpt-5.6-luna"),
|
|
358
|
+
// OpenGeni-managed Vercel AI Gateway. When configured, the two reviewed
|
|
359
|
+
// Gateway models below are added to the managed-credit catalog. Workspace
|
|
360
|
+
// Gateway keys use the encrypted connection broker and never this secret.
|
|
361
|
+
vercelAiGatewayApiKey: z.string().optional(),
|
|
358
362
|
// Native composer voice input (browser MediaRecorder → API transcription).
|
|
359
363
|
// Provider credentials stay server-side; ClientConfig only projects availability
|
|
360
364
|
// and hard ceilings. Selection happens once before audio is sent — never retry
|
|
@@ -1178,6 +1182,9 @@ export const ModelCapabilitiesV1Schema = z
|
|
|
1178
1182
|
responsesWebSocket: CapabilityStateV1Schema,
|
|
1179
1183
|
realtimeAudio: CapabilityStateV1Schema,
|
|
1180
1184
|
}),
|
|
1185
|
+
promptCaching: CapabilityStateV1Schema.extend({
|
|
1186
|
+
mode: z.enum(["implicit", "automatic", "none"]),
|
|
1187
|
+
}).optional(),
|
|
1181
1188
|
latencyModes: z
|
|
1182
1189
|
.array(
|
|
1183
1190
|
z.object({
|
|
@@ -1282,7 +1289,12 @@ export type ModelProviderApi = z.infer<typeof ModelProviderApi>;
|
|
|
1282
1289
|
* "codex-subscription" providers authenticate per-request with a ChatGPT/Codex
|
|
1283
1290
|
* subscription token resolved at call time (no static key) — see @opengeni/codex.
|
|
1284
1291
|
*/
|
|
1285
|
-
export const RegistryProviderKind = z.enum([
|
|
1292
|
+
export const RegistryProviderKind = z.enum([
|
|
1293
|
+
"api-key",
|
|
1294
|
+
"codex-subscription",
|
|
1295
|
+
"vercel-gateway-managed",
|
|
1296
|
+
"vercel-gateway-workspace",
|
|
1297
|
+
]);
|
|
1286
1298
|
export type RegistryProviderKind = z.infer<typeof RegistryProviderKind>;
|
|
1287
1299
|
|
|
1288
1300
|
/** A single model exposed by a registry provider. */
|
|
@@ -1335,7 +1347,7 @@ const RegistryModelSchema = z
|
|
|
1335
1347
|
|
|
1336
1348
|
/** A non-built-in provider declared by the host via OPENGENI_MODEL_PROVIDERS_JSON. */
|
|
1337
1349
|
const RegistryProviderSchema = z.object({
|
|
1338
|
-
kind: RegistryProviderKind.default("api-key"),
|
|
1350
|
+
kind: RegistryProviderKind.default("api-key"),
|
|
1339
1351
|
id: z.string().min(1).regex(registryId), // stable provider id, e.g. "fireworks"
|
|
1340
1352
|
label: z.string().min(1).optional(),
|
|
1341
1353
|
api: ModelProviderApi.default("chat"),
|
|
@@ -1401,6 +1413,12 @@ export interface ConfiguredModel {
|
|
|
1401
1413
|
credentialSource: CredentialSourceV1;
|
|
1402
1414
|
billing: BillingAttributionV1;
|
|
1403
1415
|
capabilities: ModelCapabilitiesV1;
|
|
1416
|
+
requestPolicy?: {
|
|
1417
|
+
gateway: {
|
|
1418
|
+
only: [string];
|
|
1419
|
+
caching: "auto" | "none";
|
|
1420
|
+
};
|
|
1421
|
+
};
|
|
1404
1422
|
pricing?: ModelPricingScheduleV1 | undefined;
|
|
1405
1423
|
definitionVersion: string;
|
|
1406
1424
|
contextWindowTokens?: number | undefined;
|
|
@@ -1411,6 +1429,32 @@ export interface ConfiguredModel {
|
|
|
1411
1429
|
hostedWebSearch: boolean;
|
|
1412
1430
|
}
|
|
1413
1431
|
|
|
1432
|
+
export const VERCEL_AI_GATEWAY_BASE_URL = "https://ai-gateway.vercel.sh/v1" as const;
|
|
1433
|
+
export const OPENGENI_GATEWAY_PROVIDER_ID = "opengeni-gateway" as const;
|
|
1434
|
+
export const WORKSPACE_GATEWAY_PROVIDER_ID = "workspace-gateway" as const;
|
|
1435
|
+
export const WORKSPACE_GATEWAY_MODEL_ID_PREFIX = "workspace-gateway/" as const;
|
|
1436
|
+
export const VERCEL_AI_GATEWAY_CONNECTION_DOMAIN = "ai-gateway.vercel.sh" as const;
|
|
1437
|
+
export const VERCEL_AI_GATEWAY_CONNECTION_ROLE = "vercel_ai_gateway" as const;
|
|
1438
|
+
|
|
1439
|
+
export const OPENGENI_GATEWAY_MODELS = {
|
|
1440
|
+
deepseek: {
|
|
1441
|
+
productId: "deepseek-v4-flash-0731",
|
|
1442
|
+
workspaceProductId: `${WORKSPACE_GATEWAY_MODEL_ID_PREFIX}deepseek-v4-flash-0731`,
|
|
1443
|
+
upstreamModelId: "deepseek/deepseek-v4-flash-0731",
|
|
1444
|
+
label: "DeepSeek V4 Flash 0731",
|
|
1445
|
+
provider: "deepinfra",
|
|
1446
|
+
implicitCaching: true,
|
|
1447
|
+
},
|
|
1448
|
+
kimi: {
|
|
1449
|
+
productId: "kimi-k3-fast",
|
|
1450
|
+
workspaceProductId: `${WORKSPACE_GATEWAY_MODEL_ID_PREFIX}kimi-k3-fast`,
|
|
1451
|
+
upstreamModelId: "moonshotai/kimi-k3-fast",
|
|
1452
|
+
label: "Kimi K3 Fast",
|
|
1453
|
+
provider: "wafer",
|
|
1454
|
+
implicitCaching: true,
|
|
1455
|
+
},
|
|
1456
|
+
} as const;
|
|
1457
|
+
|
|
1414
1458
|
/**
|
|
1415
1459
|
* Built-in OpenGeni credit pricing schedules.
|
|
1416
1460
|
*
|
|
@@ -1485,6 +1529,27 @@ export const defaultModelPricing: Record<string, ModelPricingScheduleV1> = {
|
|
|
1485
1529
|
},
|
|
1486
1530
|
],
|
|
1487
1531
|
},
|
|
1532
|
+
// Vercel AI Gateway endpoint prices, provider-pinned in the runtime.
|
|
1533
|
+
// Snapshot: 2026-08-02. Both pinned routes returned discounted implicit
|
|
1534
|
+
// cache reads in live Gateway responses. Wafer/Kimi reported $0.45/M even
|
|
1535
|
+
// though the provider-discovery flag currently says otherwise; bill from
|
|
1536
|
+
// the response-backed rate, not that inconsistent boolean.
|
|
1537
|
+
[OPENGENI_GATEWAY_MODELS.deepseek.productId]: {
|
|
1538
|
+
default: {
|
|
1539
|
+
inputMicrosPerMillionTokens: 90_000,
|
|
1540
|
+
cachedInputMicrosPerMillionTokens: 18_000,
|
|
1541
|
+
outputMicrosPerMillionTokens: 180_000,
|
|
1542
|
+
marginBps: 2_500,
|
|
1543
|
+
},
|
|
1544
|
+
},
|
|
1545
|
+
[OPENGENI_GATEWAY_MODELS.kimi.productId]: {
|
|
1546
|
+
default: {
|
|
1547
|
+
inputMicrosPerMillionTokens: 4_500_000,
|
|
1548
|
+
cachedInputMicrosPerMillionTokens: 450_000,
|
|
1549
|
+
outputMicrosPerMillionTokens: 22_500_000,
|
|
1550
|
+
marginBps: 2_500,
|
|
1551
|
+
},
|
|
1552
|
+
},
|
|
1488
1553
|
// Fireworks AI / GLM 5.2 — the first shipped non-OpenAI registry model. A
|
|
1489
1554
|
// built-in default pricing entry makes managed billing work out of the box
|
|
1490
1555
|
// for hosts that expose this model via OPENGENI_MODEL_PROVIDERS_JSON without
|
|
@@ -1656,6 +1721,7 @@ export function getSettings(): Settings {
|
|
|
1656
1721
|
openaiBaseUrl: optional("OPENGENI_OPENAI_BASE_URL") ?? optional("OPENAI_BASE_URL"),
|
|
1657
1722
|
openaiModel: optional("OPENGENI_OPENAI_MODEL"),
|
|
1658
1723
|
openaiAllowedModels: optional("OPENGENI_OPENAI_ALLOWED_MODELS"),
|
|
1724
|
+
vercelAiGatewayApiKey: optional("OPENGENI_VERCEL_AI_GATEWAY_API_KEY"),
|
|
1659
1725
|
voiceInputMaxDurationSeconds: optional("OPENGENI_VOICE_INPUT_MAX_DURATION_SECONDS"),
|
|
1660
1726
|
voiceInputMaxSizeBytes: optional("OPENGENI_VOICE_INPUT_MAX_SIZE_BYTES"),
|
|
1661
1727
|
voiceInputProviderOrder: optional("OPENGENI_VOICE_INPUT_PROVIDER_ORDER"),
|
|
@@ -2202,6 +2268,128 @@ function legacyModelCapabilities(
|
|
|
2202
2268
|
});
|
|
2203
2269
|
}
|
|
2204
2270
|
|
|
2271
|
+
export function gatewayRequestPolicyForUpstreamModel(
|
|
2272
|
+
upstreamModelId: string,
|
|
2273
|
+
): ConfiguredModel["requestPolicy"] {
|
|
2274
|
+
const model = Object.values(OPENGENI_GATEWAY_MODELS).find(
|
|
2275
|
+
(candidate) => candidate.upstreamModelId === upstreamModelId,
|
|
2276
|
+
);
|
|
2277
|
+
if (!model) {
|
|
2278
|
+
return undefined;
|
|
2279
|
+
}
|
|
2280
|
+
return {
|
|
2281
|
+
gateway: {
|
|
2282
|
+
only: [model.provider],
|
|
2283
|
+
caching: model.implicitCaching ? "auto" : "none",
|
|
2284
|
+
},
|
|
2285
|
+
};
|
|
2286
|
+
}
|
|
2287
|
+
|
|
2288
|
+
function gatewayModelCapabilities(
|
|
2289
|
+
settings: Settings,
|
|
2290
|
+
input: { implicitCaching: boolean; vision: boolean },
|
|
2291
|
+
): ModelCapabilitiesV1 {
|
|
2292
|
+
const legacy = legacyModelCapabilities(settings, {
|
|
2293
|
+
reasoningEffort: true,
|
|
2294
|
+
hostedWebSearch: false,
|
|
2295
|
+
});
|
|
2296
|
+
return normalizeCapabilities({
|
|
2297
|
+
...legacy,
|
|
2298
|
+
functionCalling: { upstream: "supported", runnable: true },
|
|
2299
|
+
inputModalities: input.vision ? ["text", "image"] : ["text"],
|
|
2300
|
+
transports: {
|
|
2301
|
+
...legacy.transports,
|
|
2302
|
+
sse: { upstream: "supported", runnable: true },
|
|
2303
|
+
},
|
|
2304
|
+
promptCaching: input.implicitCaching
|
|
2305
|
+
? { upstream: "supported", runnable: true, mode: "implicit" }
|
|
2306
|
+
: { upstream: "unsupported", runnable: false, mode: "none" },
|
|
2307
|
+
// "Fast" is part of Kimi's product name, not OpenGeni's separately billed
|
|
2308
|
+
// latency mode. Both Gateway products expose only standard here.
|
|
2309
|
+
latencyModes: [{ id: "standard", upstream: "supported", runnable: true }],
|
|
2310
|
+
});
|
|
2311
|
+
}
|
|
2312
|
+
|
|
2313
|
+
function gatewayRegistryProvider(
|
|
2314
|
+
settings: Settings,
|
|
2315
|
+
input:
|
|
2316
|
+
| { kind: "vercel-gateway-managed"; apiKey: string }
|
|
2317
|
+
| { kind: "vercel-gateway-workspace"; apiKey?: string },
|
|
2318
|
+
): RegistryProvider {
|
|
2319
|
+
const workspace = input.kind === "vercel-gateway-workspace";
|
|
2320
|
+
const models = [OPENGENI_GATEWAY_MODELS.deepseek, OPENGENI_GATEWAY_MODELS.kimi].map((model) => ({
|
|
2321
|
+
id: workspace ? model.workspaceProductId : model.productId,
|
|
2322
|
+
upstreamModelId: model.upstreamModelId,
|
|
2323
|
+
label: model.label,
|
|
2324
|
+
capabilities: gatewayModelCapabilities(settings, {
|
|
2325
|
+
implicitCaching: model.implicitCaching,
|
|
2326
|
+
vision: model === OPENGENI_GATEWAY_MODELS.kimi,
|
|
2327
|
+
}),
|
|
2328
|
+
contextWindowTokens: 1_000_000,
|
|
2329
|
+
effectiveContextWindowTokens: 900_000,
|
|
2330
|
+
autoCompactTokenLimit: 850_000,
|
|
2331
|
+
toolOutputTruncationTokens: settings.modelToolOutputTruncationTokens,
|
|
2332
|
+
}));
|
|
2333
|
+
return {
|
|
2334
|
+
kind: input.kind,
|
|
2335
|
+
id: workspace ? WORKSPACE_GATEWAY_PROVIDER_ID : OPENGENI_GATEWAY_PROVIDER_ID,
|
|
2336
|
+
label: workspace ? "Your Gateway" : "OpenGeni",
|
|
2337
|
+
// Responses preserves vision, reasoning items, and provider-native usage.
|
|
2338
|
+
// Model-specific compatibility stays at the reviewed request fence rather
|
|
2339
|
+
// than downgrading the whole provider wire.
|
|
2340
|
+
api: "responses",
|
|
2341
|
+
baseUrl: VERCEL_AI_GATEWAY_BASE_URL,
|
|
2342
|
+
...(input.apiKey ? { apiKey: input.apiKey } : {}),
|
|
2343
|
+
models,
|
|
2344
|
+
};
|
|
2345
|
+
}
|
|
2346
|
+
|
|
2347
|
+
function configuredRegistryProviders(settings: Settings): RegistryProvider[] {
|
|
2348
|
+
const providers = parseModelProvidersJson(settings.modelProvidersJson);
|
|
2349
|
+
if (!settings.vercelAiGatewayApiKey) {
|
|
2350
|
+
return providers;
|
|
2351
|
+
}
|
|
2352
|
+
if (providers.some((provider) => provider.id === OPENGENI_GATEWAY_PROVIDER_ID)) {
|
|
2353
|
+
throw new Error(
|
|
2354
|
+
`${OPENGENI_GATEWAY_PROVIDER_ID} is reserved for OPENGENI_VERCEL_AI_GATEWAY_API_KEY`,
|
|
2355
|
+
);
|
|
2356
|
+
}
|
|
2357
|
+
return [
|
|
2358
|
+
...providers,
|
|
2359
|
+
gatewayRegistryProvider(settings, {
|
|
2360
|
+
kind: "vercel-gateway-managed",
|
|
2361
|
+
apiKey: settings.vercelAiGatewayApiKey,
|
|
2362
|
+
}),
|
|
2363
|
+
];
|
|
2364
|
+
}
|
|
2365
|
+
|
|
2366
|
+
/** Static catalog overlay; it contains no concrete workspace credential. */
|
|
2367
|
+
export function withWorkspaceGatewayCatalogProvider(settings: Settings): Settings {
|
|
2368
|
+
const providers = parseModelProvidersJson(settings.modelProvidersJson);
|
|
2369
|
+
if (providers.some((provider) => provider.id === WORKSPACE_GATEWAY_PROVIDER_ID)) {
|
|
2370
|
+
return settings;
|
|
2371
|
+
}
|
|
2372
|
+
return {
|
|
2373
|
+
...settings,
|
|
2374
|
+
modelProvidersJson: JSON.stringify([
|
|
2375
|
+
...providers,
|
|
2376
|
+
gatewayRegistryProvider(settings, { kind: "vercel-gateway-workspace" }),
|
|
2377
|
+
]),
|
|
2378
|
+
};
|
|
2379
|
+
}
|
|
2380
|
+
|
|
2381
|
+
/** Runtime overlay after the worker resolves the workspace's encrypted key. */
|
|
2382
|
+
export function withWorkspaceGatewayCredential(settings: Settings, apiKey: string): Settings {
|
|
2383
|
+
if (!apiKey.trim()) {
|
|
2384
|
+
throw new Error("workspace AI Gateway credential is empty");
|
|
2385
|
+
}
|
|
2386
|
+
const catalogSettings = withWorkspaceGatewayCatalogProvider(settings);
|
|
2387
|
+
const providers = parseModelProvidersJson(catalogSettings.modelProvidersJson).map((provider) =>
|
|
2388
|
+
provider.id === WORKSPACE_GATEWAY_PROVIDER_ID ? { ...provider, apiKey } : provider,
|
|
2389
|
+
);
|
|
2390
|
+
return { ...catalogSettings, modelProvidersJson: JSON.stringify(providers) };
|
|
2391
|
+
}
|
|
2392
|
+
|
|
2205
2393
|
/** OpenAI GPT-5.6 Fast mode is 2× Standard list rates (service_tier fast/priority). */
|
|
2206
2394
|
const GPT56_FAST_BILLING_MULTIPLIER_BPS = 20_000;
|
|
2207
2395
|
|
|
@@ -2256,6 +2444,17 @@ function builtinLatencyModesForModel(modelId: string): Array<{
|
|
|
2256
2444
|
return [{ id: "standard", upstream: "unknown", runnable: true }];
|
|
2257
2445
|
}
|
|
2258
2446
|
|
|
2447
|
+
function builtinPromptCachingForModel(
|
|
2448
|
+
modelId: string,
|
|
2449
|
+
): NonNullable<ModelCapabilitiesV1["promptCaching"]> | undefined {
|
|
2450
|
+
const slug = modelId.startsWith(CODEX_MODEL_ID_PREFIX)
|
|
2451
|
+
? modelId.slice(CODEX_MODEL_ID_PREFIX.length)
|
|
2452
|
+
: modelId;
|
|
2453
|
+
return slug.startsWith("gpt-5.6-")
|
|
2454
|
+
? { upstream: "supported", runnable: true, mode: "implicit" }
|
|
2455
|
+
: undefined;
|
|
2456
|
+
}
|
|
2457
|
+
|
|
2259
2458
|
/**
|
|
2260
2459
|
* Map OpenGeni latency mode to the provider `service_tier` wire value.
|
|
2261
2460
|
* Azure and Codex ChatGPT accept `priority`; OpenAI API accepts `fast` (alias of priority).
|
|
@@ -2312,15 +2511,23 @@ function assertLatencyModeRunnable(
|
|
|
2312
2511
|
}
|
|
2313
2512
|
|
|
2314
2513
|
function registryCredentialSource(provider: RegistryProvider): CredentialSourceV1 {
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2514
|
+
if (provider.kind === "codex-subscription") {
|
|
2515
|
+
return { kind: "connected_subscription", provider: "codex" };
|
|
2516
|
+
}
|
|
2517
|
+
if (provider.kind === "vercel-gateway-workspace") {
|
|
2518
|
+
return { kind: "workspace_connection", mechanism: "api_key" };
|
|
2519
|
+
}
|
|
2520
|
+
return { kind: "deployment", mechanism: "api_key" };
|
|
2318
2521
|
}
|
|
2319
2522
|
|
|
2320
2523
|
function registryBilling(provider: RegistryProvider): BillingAttributionV1 {
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2524
|
+
if (provider.kind === "codex-subscription") {
|
|
2525
|
+
return { upstreamPayer: "connected_subscription", metering: "external" };
|
|
2526
|
+
}
|
|
2527
|
+
if (provider.kind === "vercel-gateway-workspace") {
|
|
2528
|
+
return { upstreamPayer: "workspace", metering: "external" };
|
|
2529
|
+
}
|
|
2530
|
+
return { upstreamPayer: "deployment", metering: "opengeni_credits" };
|
|
2324
2531
|
}
|
|
2325
2532
|
|
|
2326
2533
|
function builtinCredentialSource(settings: Settings): CredentialSourceV1 {
|
|
@@ -2403,6 +2610,7 @@ function definitionVersionFor(
|
|
|
2403
2610
|
billing: model.billing,
|
|
2404
2611
|
executionLimits: model.executionLimits,
|
|
2405
2612
|
capabilities: model.capabilities,
|
|
2613
|
+
...(model.requestPolicy ? { requestPolicy: model.requestPolicy } : {}),
|
|
2406
2614
|
pricing: model.pricing ?? null,
|
|
2407
2615
|
});
|
|
2408
2616
|
return `sha256:${createHash("sha256")
|
|
@@ -2455,7 +2663,7 @@ export function configuredProviders(settings: Settings): ResolvedModelProvider[]
|
|
|
2455
2663
|
: undefined;
|
|
2456
2664
|
builtin.apiKey = settings.openaiApiKey;
|
|
2457
2665
|
}
|
|
2458
|
-
const registry =
|
|
2666
|
+
const registry = configuredRegistryProviders(settings).map(
|
|
2459
2667
|
(provider): ResolvedModelProvider => ({
|
|
2460
2668
|
id: provider.id,
|
|
2461
2669
|
label: provider.label ?? provider.id,
|
|
@@ -2498,6 +2706,11 @@ export function withCodexCatalogProvider(settings: Settings): Settings {
|
|
|
2498
2706
|
reasoningEffort: true,
|
|
2499
2707
|
hostedWebSearch: true,
|
|
2500
2708
|
}),
|
|
2709
|
+
...(builtinPromptCachingForModel(`${CODEX_MODEL_ID_PREFIX}${slug}`)
|
|
2710
|
+
? {
|
|
2711
|
+
promptCaching: builtinPromptCachingForModel(`${CODEX_MODEL_ID_PREFIX}${slug}`)!,
|
|
2712
|
+
}
|
|
2713
|
+
: {}),
|
|
2501
2714
|
latencyModes: builtinLatencyModesForModel(`${CODEX_MODEL_ID_PREFIX}${slug}`),
|
|
2502
2715
|
};
|
|
2503
2716
|
return {
|
|
@@ -2542,6 +2755,9 @@ export function policyProviderIdForModel(settings: Settings, modelId: string): s
|
|
|
2542
2755
|
if (canonicalModelId.startsWith(CODEX_MODEL_ID_PREFIX)) {
|
|
2543
2756
|
return CODEX_PROVIDER_ID;
|
|
2544
2757
|
}
|
|
2758
|
+
if (canonicalModelId.startsWith(WORKSPACE_GATEWAY_MODEL_ID_PREFIX)) {
|
|
2759
|
+
return WORKSPACE_GATEWAY_PROVIDER_ID;
|
|
2760
|
+
}
|
|
2545
2761
|
const configured = configuredModels(settings).find((model) => model.id === canonicalModelId);
|
|
2546
2762
|
return configured?.providerId ?? builtinProviderId(settings);
|
|
2547
2763
|
}
|
|
@@ -2571,9 +2787,14 @@ function finalizeConfiguredModel(
|
|
|
2571
2787
|
provider: ResolvedModelProvider,
|
|
2572
2788
|
input: Omit<ConfiguredModel, "schemaVersion" | "definitionVersion" | "executionLimits">,
|
|
2573
2789
|
): ConfiguredModel {
|
|
2790
|
+
const requestPolicy =
|
|
2791
|
+
provider.kind === "vercel-gateway-managed" || provider.kind === "vercel-gateway-workspace"
|
|
2792
|
+
? gatewayRequestPolicyForUpstreamModel(input.upstreamModelId)
|
|
2793
|
+
: undefined;
|
|
2574
2794
|
const modelWithoutVersion: Omit<ConfiguredModel, "definitionVersion"> = {
|
|
2575
2795
|
schemaVersion: 1,
|
|
2576
2796
|
...input,
|
|
2797
|
+
...(requestPolicy ? { requestPolicy } : {}),
|
|
2577
2798
|
executionLimits: resolvedExecutionLimits(settings, input),
|
|
2578
2799
|
};
|
|
2579
2800
|
return {
|
|
@@ -2645,7 +2866,7 @@ export function configuredModels(settings: Settings): ConfiguredModel[] {
|
|
|
2645
2866
|
// a codex/ id has NO codex provider injected (no active subscription) it then
|
|
2646
2867
|
// resolves to nothing and getModel fails loud with
|
|
2647
2868
|
// CodexSubscriptionUnavailableError instead of mis-routing to Azure.
|
|
2648
|
-
const parsedRegistry =
|
|
2869
|
+
const parsedRegistry = configuredRegistryProviders(settings);
|
|
2649
2870
|
const registryOwnedIds = new Set(
|
|
2650
2871
|
parsedRegistry.flatMap((provider) => provider.models.map((model) => model.id)),
|
|
2651
2872
|
);
|
|
@@ -2671,6 +2892,9 @@ export function configuredModels(settings: Settings): ConfiguredModel[] {
|
|
|
2671
2892
|
reasoningEffort: true,
|
|
2672
2893
|
hostedWebSearch: settings.webSearchEnabled,
|
|
2673
2894
|
}),
|
|
2895
|
+
...(builtinPromptCachingForModel(id)
|
|
2896
|
+
? { promptCaching: builtinPromptCachingForModel(id)! }
|
|
2897
|
+
: {}),
|
|
2674
2898
|
latencyModes: builtinLatencyModesForModel(id),
|
|
2675
2899
|
};
|
|
2676
2900
|
return finalizeConfiguredModel(settings, builtinProvider, {
|
|
@@ -2804,9 +3028,13 @@ export type ResolveTurnExecutionPolicyV1Input = {
|
|
|
2804
3028
|
};
|
|
2805
3029
|
|
|
2806
3030
|
function settingsForTurnExecutionPolicy(settings: Settings, modelId: string): Settings {
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
3031
|
+
if (settings.codexSubscriptionEnabled && modelId.startsWith(CODEX_MODEL_ID_PREFIX)) {
|
|
3032
|
+
return withCodexCatalogProvider(settings);
|
|
3033
|
+
}
|
|
3034
|
+
if (modelId.startsWith(WORKSPACE_GATEWAY_MODEL_ID_PREFIX)) {
|
|
3035
|
+
return withWorkspaceGatewayCatalogProvider(settings);
|
|
3036
|
+
}
|
|
3037
|
+
return settings;
|
|
2810
3038
|
}
|
|
2811
3039
|
|
|
2812
3040
|
/**
|
|
@@ -2924,7 +3152,7 @@ export function configuredModelPricingSchedules(
|
|
|
2924
3152
|
]),
|
|
2925
3153
|
);
|
|
2926
3154
|
const registry: Record<string, ModelPricingScheduleV1> = {};
|
|
2927
|
-
for (const provider of
|
|
3155
|
+
for (const provider of configuredRegistryProviders(settings)) {
|
|
2928
3156
|
for (const model of provider.models) {
|
|
2929
3157
|
if (model.pricing) {
|
|
2930
3158
|
registry[model.id] = normalizeModelPricingSchedule(model.pricing);
|
|
@@ -4298,6 +4526,14 @@ function validateSettings(settings: Settings): void {
|
|
|
4298
4526
|
const builtinId = builtinProviderId(settings);
|
|
4299
4527
|
const providerIds = new Set<string>();
|
|
4300
4528
|
for (const provider of registryProviders) {
|
|
4529
|
+
if (
|
|
4530
|
+
provider.kind === "vercel-gateway-managed" ||
|
|
4531
|
+
provider.kind === "vercel-gateway-workspace"
|
|
4532
|
+
) {
|
|
4533
|
+
throw new Error(
|
|
4534
|
+
`OPENGENI_MODEL_PROVIDERS_JSON provider kind ${provider.kind} is reserved for the reviewed AI Gateway broker`,
|
|
4535
|
+
);
|
|
4536
|
+
}
|
|
4301
4537
|
if (provider.id === builtinId) {
|
|
4302
4538
|
throw new Error(
|
|
4303
4539
|
`OPENGENI_MODEL_PROVIDERS_JSON provider id ${provider.id} collides with the built-in provider id`,
|
|
@@ -4309,7 +4545,7 @@ function validateSettings(settings: Settings): void {
|
|
|
4309
4545
|
);
|
|
4310
4546
|
}
|
|
4311
4547
|
providerIds.add(provider.id);
|
|
4312
|
-
if (!resolveProviderApiKey(provider)) {
|
|
4548
|
+
if (provider.kind !== "codex-subscription" && !resolveProviderApiKey(provider)) {
|
|
4313
4549
|
throw new Error(
|
|
4314
4550
|
`OPENGENI_MODEL_PROVIDERS_JSON provider ${provider.id} requires a resolvable API key (set apiKey or apiKeyEnv)`,
|
|
4315
4551
|
);
|