@opengeni/config 2.0.0 → 2.1.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/dist/index.d.ts +172 -0
- package/dist/index.js +186 -15
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/index.ts +258 -45
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opengeni/config",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.0",
|
|
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": {
|
|
@@ -33,8 +33,8 @@
|
|
|
33
33
|
"prepublishOnly": "bash ../../scripts/prepublish-guard"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@opengeni/codex": "^0.2.
|
|
37
|
-
"@opengeni/contracts": "^5.
|
|
36
|
+
"@opengeni/codex": "^0.2.25",
|
|
37
|
+
"@opengeni/contracts": "^5.1.0",
|
|
38
38
|
"@opengeni/xai-subscription": "^0.1.4",
|
|
39
39
|
"zod": "^4.2.1"
|
|
40
40
|
},
|
package/src/index.ts
CHANGED
|
@@ -395,6 +395,7 @@ const SettingsSchema = z.object({
|
|
|
395
395
|
environmentsEncryptionKey: z.string().optional(),
|
|
396
396
|
integrationsEnabled: EnvBoolean.default(false),
|
|
397
397
|
integrationsStateSecret: z.string().optional(),
|
|
398
|
+
integrationsOauthShortStateEnabled: EnvBoolean.default(false),
|
|
398
399
|
integrationsAllowPrivateNetworkTargets: EnvBoolean.default(false),
|
|
399
400
|
integrationsOauthClientsJson: z.string().default("{}"),
|
|
400
401
|
slackClientId: z.string().optional(),
|
|
@@ -556,8 +557,8 @@ const SettingsSchema = z.object({
|
|
|
556
557
|
openaiProvider: z.enum(["openai", "azure"]).default("openai"),
|
|
557
558
|
openaiApiKey: z.string().optional(),
|
|
558
559
|
openaiBaseUrl: z.string().optional(),
|
|
559
|
-
openaiModel: z.string().default("gpt-
|
|
560
|
-
openaiAllowedModels: z.string().default("gpt-
|
|
560
|
+
openaiModel: z.string().default("gpt-6-astra"),
|
|
561
|
+
openaiAllowedModels: z.string().default("gpt-6-astra,gpt-6-sol,gpt-6-luna"),
|
|
561
562
|
// OpenGeni-managed Vercel AI Gateway. When configured, the two reviewed
|
|
562
563
|
// Gateway models below are added to the managed-credit catalog. Workspace
|
|
563
564
|
// Gateway keys use the encrypted connection broker and never this secret.
|
|
@@ -686,6 +687,7 @@ const SettingsSchema = z.object({
|
|
|
686
687
|
// binding so database mode cannot be bypassed with a second source.
|
|
687
688
|
resolvedGatewayModelsJson: z.string().optional(),
|
|
688
689
|
resolvedOpenRouterModelsJson: z.string().optional(),
|
|
690
|
+
resolvedCodexModelsJson: z.string().optional(),
|
|
689
691
|
// Extra (non-built-in) model providers, declared by the host as a JSON
|
|
690
692
|
// provider registry. Each entry carries its own base URL, API key, wire API
|
|
691
693
|
// ("responses" | "chat") and the models it exposes. The models a client may
|
|
@@ -1553,6 +1555,10 @@ export type VoiceInputProviderConfig =
|
|
|
1553
1555
|
experimental: true;
|
|
1554
1556
|
};
|
|
1555
1557
|
|
|
1558
|
+
function usableDeploymentSecret(value: string | null | undefined): string | undefined {
|
|
1559
|
+
return isUsableVoiceInputSecret(value) ? value : undefined;
|
|
1560
|
+
}
|
|
1561
|
+
|
|
1556
1562
|
/**
|
|
1557
1563
|
* Reject empty / template secrets so `.env.example` placeholders like
|
|
1558
1564
|
* `your-key` cannot advertise voice input as available and then 401 upstream.
|
|
@@ -2244,6 +2250,24 @@ const DeploymentGatewayCatalogModelSchema = GatewayCatalogModel.safeExtend({
|
|
|
2244
2250
|
pricing: z.never().optional(),
|
|
2245
2251
|
}).strict();
|
|
2246
2252
|
|
|
2253
|
+
const CodexCatalogModelSchema = RegistryModelSchema.safeExtend({
|
|
2254
|
+
retired: z.boolean().optional(),
|
|
2255
|
+
id: z.string().regex(/^codex\/[a-zA-Z0-9][a-zA-Z0-9._-]*$/),
|
|
2256
|
+
upstreamModelId: z.string().regex(/^[a-zA-Z0-9][a-zA-Z0-9._-]*$/),
|
|
2257
|
+
capabilities: ModelCapabilitiesV1Schema,
|
|
2258
|
+
pricing: z.never().optional(),
|
|
2259
|
+
})
|
|
2260
|
+
.strict()
|
|
2261
|
+
.superRefine((model, context) => {
|
|
2262
|
+
if (model.id !== `${CODEX_MODEL_ID_PREFIX}${model.upstreamModelId}`) {
|
|
2263
|
+
context.addIssue({
|
|
2264
|
+
code: "custom",
|
|
2265
|
+
path: ["id"],
|
|
2266
|
+
message: "Codex product id must match its upstream model slug",
|
|
2267
|
+
});
|
|
2268
|
+
}
|
|
2269
|
+
});
|
|
2270
|
+
|
|
2247
2271
|
export const ModelCatalogDocument = z
|
|
2248
2272
|
.object({
|
|
2249
2273
|
schemaVersion: z.literal(1),
|
|
@@ -2251,8 +2275,9 @@ export const ModelCatalogDocument = z
|
|
|
2251
2275
|
* fallback for existing documents; operators should set this explicitly
|
|
2252
2276
|
* when cutting over a registry or connected-subscription default. */
|
|
2253
2277
|
defaultModel: z.string().min(1).optional(),
|
|
2254
|
-
builtInModels: z.array(z.string().min(1))
|
|
2278
|
+
builtInModels: z.array(z.string().min(1)),
|
|
2255
2279
|
registryProviders: z.array(DeploymentRegistryProviderSchema).default([]),
|
|
2280
|
+
codexModels: z.array(CodexCatalogModelSchema).optional(),
|
|
2256
2281
|
gatewayModels: z.array(DeploymentGatewayCatalogModelSchema).default([]),
|
|
2257
2282
|
openrouterModels: z.array(OpenRouterCatalogModel).default([]),
|
|
2258
2283
|
modelNotes: z.record(z.string().min(1), ModelNote).default({}),
|
|
@@ -2263,6 +2288,13 @@ export const ModelCatalogDocument = z
|
|
|
2263
2288
|
})
|
|
2264
2289
|
.strict()
|
|
2265
2290
|
.superRefine((document, context) => {
|
|
2291
|
+
if (document.builtInModels.length === 0 && !document.defaultModel) {
|
|
2292
|
+
context.addIssue({
|
|
2293
|
+
code: "custom",
|
|
2294
|
+
path: ["defaultModel"],
|
|
2295
|
+
message: "catalogs without built-in models require an explicit default model",
|
|
2296
|
+
});
|
|
2297
|
+
}
|
|
2266
2298
|
const productIds = new Set<string>();
|
|
2267
2299
|
const providerIds = new Set<string>();
|
|
2268
2300
|
const gatewayUpstreamIds = new Set<string>();
|
|
@@ -2284,6 +2316,16 @@ export const ModelCatalogDocument = z
|
|
|
2284
2316
|
productIds.add(id);
|
|
2285
2317
|
};
|
|
2286
2318
|
document.builtInModels.forEach((id, index) => add(id, ["builtInModels", index]));
|
|
2319
|
+
document.codexModels?.forEach((model, index) => add(model.id, ["codexModels", index, "id"]));
|
|
2320
|
+
if (
|
|
2321
|
+
document.codexModels?.some((model) => model.retired && model.id === document.defaultModel)
|
|
2322
|
+
) {
|
|
2323
|
+
context.addIssue({
|
|
2324
|
+
code: "custom",
|
|
2325
|
+
path: ["defaultModel"],
|
|
2326
|
+
message: "a retired model cannot be the deployment default",
|
|
2327
|
+
});
|
|
2328
|
+
}
|
|
2287
2329
|
document.registryProviders.forEach((provider, providerIndex) => {
|
|
2288
2330
|
if (RESERVED_MODEL_PROVIDER_IDS.has(provider.id)) {
|
|
2289
2331
|
context.addIssue({
|
|
@@ -2333,7 +2375,10 @@ export const ModelCatalogDocument = z
|
|
|
2333
2375
|
if (
|
|
2334
2376
|
document.defaultModel &&
|
|
2335
2377
|
!productIds.has(document.defaultModel) &&
|
|
2336
|
-
!
|
|
2378
|
+
!(
|
|
2379
|
+
document.codexModels === undefined &&
|
|
2380
|
+
document.defaultModel.startsWith(CODEX_MODEL_ID_PREFIX)
|
|
2381
|
+
) &&
|
|
2337
2382
|
!document.defaultModel.startsWith(XAI_SUBSCRIPTION_MODEL_ID_PREFIX)
|
|
2338
2383
|
) {
|
|
2339
2384
|
context.addIssue({
|
|
@@ -2418,6 +2463,8 @@ export function applyModelCatalogDocument(settings: Settings, rawDocument: unkno
|
|
|
2418
2463
|
),
|
|
2419
2464
|
resolvedGatewayModelsJson: JSON.stringify(document.gatewayModels),
|
|
2420
2465
|
resolvedOpenRouterModelsJson: JSON.stringify(document.openrouterModels),
|
|
2466
|
+
resolvedCodexModelsJson:
|
|
2467
|
+
document.codexModels === undefined ? undefined : JSON.stringify(document.codexModels),
|
|
2421
2468
|
modelNotesJson: JSON.stringify(document.modelNotes),
|
|
2422
2469
|
};
|
|
2423
2470
|
return resolved;
|
|
@@ -2701,6 +2748,73 @@ export function configuredOpenRouterOrganizationProductModelIds(settings: Settin
|
|
|
2701
2748
|
* llm-prices.com as a ground-truth canary; it does not generate this table.
|
|
2702
2749
|
*/
|
|
2703
2750
|
export const defaultModelPricing: Record<string, ModelPricingScheduleV1> = {
|
|
2751
|
+
"gpt-6-astra": {
|
|
2752
|
+
default: {
|
|
2753
|
+
// OpenAI list price: $10 / $1 cached / $12.50 cache write / $50 output.
|
|
2754
|
+
inputMicrosPerMillionTokens: 10_000_000,
|
|
2755
|
+
cachedInputMicrosPerMillionTokens: 1_000_000,
|
|
2756
|
+
cacheWriteMicrosPerMillionTokens: 12_500_000,
|
|
2757
|
+
outputMicrosPerMillionTokens: 50_000_000,
|
|
2758
|
+
marginBps: 500,
|
|
2759
|
+
},
|
|
2760
|
+
inputTokenTiers: [
|
|
2761
|
+
{
|
|
2762
|
+
// Prompts with more than 272K input tokens use 2x input and 1.5x output.
|
|
2763
|
+
minimumInputTokens: 272_001,
|
|
2764
|
+
pricing: {
|
|
2765
|
+
inputMicrosPerMillionTokens: 20_000_000,
|
|
2766
|
+
cachedInputMicrosPerMillionTokens: 2_000_000,
|
|
2767
|
+
cacheWriteMicrosPerMillionTokens: 25_000_000,
|
|
2768
|
+
outputMicrosPerMillionTokens: 75_000_000,
|
|
2769
|
+
marginBps: 500,
|
|
2770
|
+
},
|
|
2771
|
+
},
|
|
2772
|
+
],
|
|
2773
|
+
},
|
|
2774
|
+
"gpt-6-sol": {
|
|
2775
|
+
default: {
|
|
2776
|
+
// OpenAI list price: $2 / $0.20 cached / $2.50 cache write / $10 output.
|
|
2777
|
+
inputMicrosPerMillionTokens: 2_000_000,
|
|
2778
|
+
cachedInputMicrosPerMillionTokens: 200_000,
|
|
2779
|
+
cacheWriteMicrosPerMillionTokens: 2_500_000,
|
|
2780
|
+
outputMicrosPerMillionTokens: 10_000_000,
|
|
2781
|
+
marginBps: 500,
|
|
2782
|
+
},
|
|
2783
|
+
inputTokenTiers: [
|
|
2784
|
+
{
|
|
2785
|
+
minimumInputTokens: 272_001,
|
|
2786
|
+
pricing: {
|
|
2787
|
+
inputMicrosPerMillionTokens: 4_000_000,
|
|
2788
|
+
cachedInputMicrosPerMillionTokens: 400_000,
|
|
2789
|
+
cacheWriteMicrosPerMillionTokens: 5_000_000,
|
|
2790
|
+
outputMicrosPerMillionTokens: 15_000_000,
|
|
2791
|
+
marginBps: 500,
|
|
2792
|
+
},
|
|
2793
|
+
},
|
|
2794
|
+
],
|
|
2795
|
+
},
|
|
2796
|
+
"gpt-6-luna": {
|
|
2797
|
+
default: {
|
|
2798
|
+
// OpenAI list price: $0.10 / $0.01 cached / $0.125 cache write / $0.50 output.
|
|
2799
|
+
inputMicrosPerMillionTokens: 100_000,
|
|
2800
|
+
cachedInputMicrosPerMillionTokens: 10_000,
|
|
2801
|
+
cacheWriteMicrosPerMillionTokens: 125_000,
|
|
2802
|
+
outputMicrosPerMillionTokens: 500_000,
|
|
2803
|
+
marginBps: 500,
|
|
2804
|
+
},
|
|
2805
|
+
inputTokenTiers: [
|
|
2806
|
+
{
|
|
2807
|
+
minimumInputTokens: 272_001,
|
|
2808
|
+
pricing: {
|
|
2809
|
+
inputMicrosPerMillionTokens: 200_000,
|
|
2810
|
+
cachedInputMicrosPerMillionTokens: 20_000,
|
|
2811
|
+
cacheWriteMicrosPerMillionTokens: 250_000,
|
|
2812
|
+
outputMicrosPerMillionTokens: 750_000,
|
|
2813
|
+
marginBps: 500,
|
|
2814
|
+
},
|
|
2815
|
+
},
|
|
2816
|
+
],
|
|
2817
|
+
},
|
|
2704
2818
|
"gpt-5.6-sol": {
|
|
2705
2819
|
default: {
|
|
2706
2820
|
// Promotional OpenAI pricing, guaranteed through at least 2026-11-21.
|
|
@@ -2981,6 +3095,7 @@ export function getSettings(source: NodeJS.ProcessEnv = process.env): Settings {
|
|
|
2981
3095
|
environmentsEncryptionKey: optional("OPENGENI_ENVIRONMENTS_ENCRYPTION_KEY"),
|
|
2982
3096
|
integrationsEnabled: optional("OPENGENI_INTEGRATIONS_ENABLED"),
|
|
2983
3097
|
integrationsStateSecret: optional("OPENGENI_INTEGRATIONS_STATE_SECRET"),
|
|
3098
|
+
integrationsOauthShortStateEnabled: optional("OPENGENI_INTEGRATIONS_OAUTH_SHORT_STATE_ENABLED"),
|
|
2984
3099
|
integrationsAllowPrivateNetworkTargets: optional(
|
|
2985
3100
|
"OPENGENI_INTEGRATIONS_ALLOW_PRIVATE_NETWORK_TARGETS",
|
|
2986
3101
|
),
|
|
@@ -4273,8 +4388,8 @@ export function productShortLabelForModelId(modelId: string): string | null {
|
|
|
4273
4388
|
? modelId.slice(CODEX_MODEL_ID_PREFIX.length)
|
|
4274
4389
|
: modelId;
|
|
4275
4390
|
switch (slug) {
|
|
4276
|
-
case "grok-4.
|
|
4277
|
-
return "4.
|
|
4391
|
+
case "grok-4.7":
|
|
4392
|
+
return "4.7";
|
|
4278
4393
|
case "gpt-5.6-sol":
|
|
4279
4394
|
return "5.6 Sol";
|
|
4280
4395
|
case "gpt-5.6-terra":
|
|
@@ -4283,6 +4398,10 @@ export function productShortLabelForModelId(modelId: string): string | null {
|
|
|
4283
4398
|
return "5.6 Luna";
|
|
4284
4399
|
case "gpt-6-astra":
|
|
4285
4400
|
return "6 Astra";
|
|
4401
|
+
case "gpt-6-sol":
|
|
4402
|
+
return "6 Sol";
|
|
4403
|
+
case "gpt-6-luna":
|
|
4404
|
+
return "6 Luna";
|
|
4286
4405
|
default:
|
|
4287
4406
|
return null;
|
|
4288
4407
|
}
|
|
@@ -4320,8 +4439,9 @@ function builtinLatencyModesForModel(modelId: string): Array<{
|
|
|
4320
4439
|
}> {
|
|
4321
4440
|
if (
|
|
4322
4441
|
isBuiltinGpt56ModelId(modelId) ||
|
|
4442
|
+
modelId.startsWith("gpt-6-") ||
|
|
4323
4443
|
modelId.startsWith("codex/gpt-5.6-") ||
|
|
4324
|
-
modelId
|
|
4444
|
+
modelId.startsWith("codex/gpt-6-")
|
|
4325
4445
|
) {
|
|
4326
4446
|
return [
|
|
4327
4447
|
{ id: "standard", upstream: "supported", runnable: true },
|
|
@@ -4342,7 +4462,7 @@ function builtinPromptCachingForModel(
|
|
|
4342
4462
|
const slug = modelId.startsWith(CODEX_MODEL_ID_PREFIX)
|
|
4343
4463
|
? modelId.slice(CODEX_MODEL_ID_PREFIX.length)
|
|
4344
4464
|
: modelId;
|
|
4345
|
-
return slug.startsWith("gpt-5.6-")
|
|
4465
|
+
return slug.startsWith("gpt-5.6-") || slug.startsWith("gpt-6-")
|
|
4346
4466
|
? { upstream: "supported", runnable: true, mode: "implicit" }
|
|
4347
4467
|
: undefined;
|
|
4348
4468
|
}
|
|
@@ -4352,7 +4472,7 @@ function builtinHostedImageGenerationForModel(settings: Settings, modelId: strin
|
|
|
4352
4472
|
return (
|
|
4353
4473
|
settings.openaiProvider === "openai" &&
|
|
4354
4474
|
isDirectOpenAiApiBaseUrl(settings.openaiBaseUrl) &&
|
|
4355
|
-
isBuiltinGpt56ModelId(modelId)
|
|
4475
|
+
(isBuiltinGpt56ModelId(modelId) || modelId.startsWith("gpt-6-"))
|
|
4356
4476
|
);
|
|
4357
4477
|
}
|
|
4358
4478
|
|
|
@@ -4650,12 +4770,13 @@ export function configuredProviders(
|
|
|
4650
4770
|
if (settings.openaiProvider === "azure") {
|
|
4651
4771
|
const baseUrl = settings.azureOpenaiBaseUrl ?? settings.azureOpenaiEndpoint;
|
|
4652
4772
|
builtin.baseUrl = baseUrl ? normalizeRegistryBaseUrl(baseUrl, builtin.id) : undefined;
|
|
4653
|
-
builtin.apiKey =
|
|
4773
|
+
builtin.apiKey =
|
|
4774
|
+
usableDeploymentSecret(settings.azureOpenaiApiKey) ?? settings.azureOpenaiAdToken;
|
|
4654
4775
|
} else {
|
|
4655
4776
|
builtin.baseUrl = settings.openaiBaseUrl
|
|
4656
4777
|
? normalizeRegistryBaseUrl(settings.openaiBaseUrl, builtin.id)
|
|
4657
4778
|
: undefined;
|
|
4658
|
-
builtin.apiKey = settings.openaiApiKey;
|
|
4779
|
+
builtin.apiKey = usableDeploymentSecret(settings.openaiApiKey);
|
|
4659
4780
|
}
|
|
4660
4781
|
const registry = configuredRegistryProviders(settings).map(
|
|
4661
4782
|
(provider): ResolvedModelProvider => ({
|
|
@@ -4689,6 +4810,15 @@ export function withCodexCatalogProvider(settings: Settings): Settings {
|
|
|
4689
4810
|
if (providers.some((provider) => provider.id === CODEX_PROVIDER_ID)) {
|
|
4690
4811
|
return settings;
|
|
4691
4812
|
}
|
|
4813
|
+
const catalogModels =
|
|
4814
|
+
settings.resolvedCodexModelsJson === undefined
|
|
4815
|
+
? undefined
|
|
4816
|
+
: z
|
|
4817
|
+
.array(CodexCatalogModelSchema)
|
|
4818
|
+
.parse(JSON.parse(settings.resolvedCodexModelsJson))
|
|
4819
|
+
.filter((model) => !model.retired)
|
|
4820
|
+
.map(({ retired: _retired, ...model }) => model);
|
|
4821
|
+
if (catalogModels?.length === 0) return settings;
|
|
4692
4822
|
const provider: RegistryProvider = {
|
|
4693
4823
|
kind: "codex-subscription",
|
|
4694
4824
|
id: CODEX_PROVIDER_ID,
|
|
@@ -4696,40 +4826,42 @@ export function withCodexCatalogProvider(settings: Settings): Settings {
|
|
|
4696
4826
|
api: "responses",
|
|
4697
4827
|
wireProfile: "openai",
|
|
4698
4828
|
baseUrl: CODEX_PROVIDER_BASE_URL,
|
|
4699
|
-
models:
|
|
4700
|
-
|
|
4701
|
-
|
|
4829
|
+
models:
|
|
4830
|
+
catalogModels ??
|
|
4831
|
+
CODEX_FALLBACK_MODEL_SLUGS.map((slug) => {
|
|
4832
|
+
const capabilities = {
|
|
4833
|
+
...legacyModelCapabilities(settings, {
|
|
4834
|
+
reasoningEffort: true,
|
|
4835
|
+
hostedWebSearch: true,
|
|
4836
|
+
vision: slug.startsWith("gpt-5.6-") || slug.startsWith("gpt-6-"),
|
|
4837
|
+
}),
|
|
4838
|
+
...(builtinPromptCachingForModel(`${CODEX_MODEL_ID_PREFIX}${slug}`)
|
|
4839
|
+
? {
|
|
4840
|
+
promptCaching: builtinPromptCachingForModel(`${CODEX_MODEL_ID_PREFIX}${slug}`)!,
|
|
4841
|
+
}
|
|
4842
|
+
: {}),
|
|
4843
|
+
latencyModes: builtinLatencyModesForModel(`${CODEX_MODEL_ID_PREFIX}${slug}`),
|
|
4844
|
+
};
|
|
4845
|
+
return {
|
|
4846
|
+
id: `${CODEX_MODEL_ID_PREFIX}${slug}`,
|
|
4847
|
+
upstreamModelId: slug,
|
|
4848
|
+
label: productLabelForModelId(slug),
|
|
4849
|
+
...(productShortLabelForModelId(slug)
|
|
4850
|
+
? { shortLabel: productShortLabelForModelId(slug)! }
|
|
4851
|
+
: {}),
|
|
4702
4852
|
reasoningEffort: true,
|
|
4853
|
+
// The ChatGPT/Codex Responses backend accepts the native web_search
|
|
4854
|
+
// hosted tool (unlike hosted apply_patch/computer transports). Declaring
|
|
4855
|
+
// this here makes provider resolution truthful; the worker still applies
|
|
4856
|
+
// the durable session/turn policy gate before attaching it.
|
|
4703
4857
|
hostedWebSearch: true,
|
|
4704
|
-
|
|
4705
|
-
|
|
4706
|
-
|
|
4707
|
-
|
|
4708
|
-
|
|
4709
|
-
|
|
4710
|
-
|
|
4711
|
-
latencyModes: builtinLatencyModesForModel(`${CODEX_MODEL_ID_PREFIX}${slug}`),
|
|
4712
|
-
};
|
|
4713
|
-
return {
|
|
4714
|
-
id: `${CODEX_MODEL_ID_PREFIX}${slug}`,
|
|
4715
|
-
upstreamModelId: slug,
|
|
4716
|
-
label: productLabelForModelId(slug),
|
|
4717
|
-
...(productShortLabelForModelId(slug)
|
|
4718
|
-
? { shortLabel: productShortLabelForModelId(slug)! }
|
|
4719
|
-
: {}),
|
|
4720
|
-
reasoningEffort: true,
|
|
4721
|
-
// The ChatGPT/Codex Responses backend accepts the native web_search
|
|
4722
|
-
// hosted tool (unlike hosted apply_patch/computer transports). Declaring
|
|
4723
|
-
// this here makes provider resolution truthful; the worker still applies
|
|
4724
|
-
// the durable session/turn policy gate before attaching it.
|
|
4725
|
-
hostedWebSearch: true,
|
|
4726
|
-
capabilities,
|
|
4727
|
-
contextWindowTokens: CODEX_MODEL_CONTEXT_WINDOW_TOKENS,
|
|
4728
|
-
effectiveContextWindowTokens: CODEX_MODEL_EFFECTIVE_CONTEXT_WINDOW_TOKENS,
|
|
4729
|
-
autoCompactTokenLimit: CODEX_MODEL_AUTO_COMPACT_TOKEN_LIMIT,
|
|
4730
|
-
toolOutputTruncationTokens: CODEX_MODEL_TOOL_OUTPUT_TRUNCATION_TOKENS,
|
|
4731
|
-
};
|
|
4732
|
-
}),
|
|
4858
|
+
capabilities,
|
|
4859
|
+
contextWindowTokens: CODEX_MODEL_CONTEXT_WINDOW_TOKENS,
|
|
4860
|
+
effectiveContextWindowTokens: CODEX_MODEL_EFFECTIVE_CONTEXT_WINDOW_TOKENS,
|
|
4861
|
+
autoCompactTokenLimit: CODEX_MODEL_AUTO_COMPACT_TOKEN_LIMIT,
|
|
4862
|
+
toolOutputTruncationTokens: CODEX_MODEL_TOOL_OUTPUT_TRUNCATION_TOKENS,
|
|
4863
|
+
};
|
|
4864
|
+
}),
|
|
4733
4865
|
};
|
|
4734
4866
|
return {
|
|
4735
4867
|
...settings,
|
|
@@ -4968,7 +5100,7 @@ export function configuredModels(
|
|
|
4968
5100
|
reasoningEffort: true,
|
|
4969
5101
|
hostedWebSearch: settings.webSearchEnabled,
|
|
4970
5102
|
hostedImageGeneration: builtinHostedImageGenerationForModel(settings, id),
|
|
4971
|
-
vision: id.startsWith("gpt-5.6-"),
|
|
5103
|
+
vision: id.startsWith("gpt-5.6-") || id.startsWith("gpt-6-"),
|
|
4972
5104
|
}),
|
|
4973
5105
|
...(builtinPromptCachingForModel(id)
|
|
4974
5106
|
? { promptCaching: builtinPromptCachingForModel(id)! }
|
|
@@ -5079,7 +5211,78 @@ export function canonicalizeConfiguredModelId(settings: Settings, modelId: strin
|
|
|
5079
5211
|
* openai allow-list, then registry ids.
|
|
5080
5212
|
*/
|
|
5081
5213
|
export function configuredAllowedModels(settings: Settings): string[] {
|
|
5082
|
-
return configuredModels(settings)
|
|
5214
|
+
return configuredModels(settings)
|
|
5215
|
+
.filter((model) => isModelAvailableForNewSelection(settings, model.id))
|
|
5216
|
+
.map((model) => model.id);
|
|
5217
|
+
}
|
|
5218
|
+
|
|
5219
|
+
/** Retirement is selection metadata, never provider authority or executable identity. */
|
|
5220
|
+
export function isModelAvailableForNewSelection(settings: Settings, modelId: string): boolean {
|
|
5221
|
+
if (!modelId.startsWith(CODEX_MODEL_ID_PREFIX) || settings.resolvedCodexModelsJson === undefined)
|
|
5222
|
+
return true;
|
|
5223
|
+
const models = z
|
|
5224
|
+
.array(CodexCatalogModelSchema)
|
|
5225
|
+
.parse(JSON.parse(settings.resolvedCodexModelsJson));
|
|
5226
|
+
return models.some((model) => model.id === modelId && !model.retired);
|
|
5227
|
+
}
|
|
5228
|
+
|
|
5229
|
+
/** Called only with the durable policy installed for a claimed accepted attempt. */
|
|
5230
|
+
export function settingsForAcceptedSubscriptionTurn(
|
|
5231
|
+
settings: Settings,
|
|
5232
|
+
policy: TurnExecutionPolicyV1,
|
|
5233
|
+
expected: {
|
|
5234
|
+
modelId: string;
|
|
5235
|
+
reasoningEffort: Settings["openaiReasoningEffort"];
|
|
5236
|
+
latencyMode?: LatencyMode;
|
|
5237
|
+
},
|
|
5238
|
+
): Settings {
|
|
5239
|
+
const parsed = TurnExecutionPolicyV1.parse(policy);
|
|
5240
|
+
if (!parsed.productModelId.startsWith(CODEX_MODEL_ID_PREFIX)) return settings;
|
|
5241
|
+
if (!settings.codexSubscriptionEnabled) throw new Error("Codex subscription is disabled");
|
|
5242
|
+
const models =
|
|
5243
|
+
settings.resolvedCodexModelsJson === undefined
|
|
5244
|
+
? []
|
|
5245
|
+
: z.array(CodexCatalogModelSchema).parse(JSON.parse(settings.resolvedCodexModelsJson));
|
|
5246
|
+
const retained = models.find((model) => model.id === parsed.productModelId && model.retired);
|
|
5247
|
+
if (!retained) {
|
|
5248
|
+
assertTurnExecutionPolicyMatchesConfigV1(settings, parsed, expected);
|
|
5249
|
+
return settings;
|
|
5250
|
+
}
|
|
5251
|
+
const { retired: _retired, ...model } = retained;
|
|
5252
|
+
// Use the existing fixed broker transport. Only this exact definition is added;
|
|
5253
|
+
// selection metadata remains retired in the original catalog JSON.
|
|
5254
|
+
const withoutCodex = {
|
|
5255
|
+
...settings,
|
|
5256
|
+
modelProvidersJson: JSON.stringify(
|
|
5257
|
+
parseModelProvidersJson(settings.modelProvidersJson).filter(
|
|
5258
|
+
(provider) => provider.id !== CODEX_PROVIDER_ID,
|
|
5259
|
+
),
|
|
5260
|
+
),
|
|
5261
|
+
};
|
|
5262
|
+
const providerSettings = withCodexCatalogProvider({
|
|
5263
|
+
...withoutCodex,
|
|
5264
|
+
resolvedCodexModelsJson: JSON.stringify([model]),
|
|
5265
|
+
});
|
|
5266
|
+
const retainedProvider = parseModelProvidersJson(providerSettings.modelProvidersJson).find(
|
|
5267
|
+
(provider) => provider.id === CODEX_PROVIDER_ID,
|
|
5268
|
+
)!;
|
|
5269
|
+
const providers = parseModelProvidersJson(settings.modelProvidersJson);
|
|
5270
|
+
const current = providers.find((provider) => provider.id === CODEX_PROVIDER_ID);
|
|
5271
|
+
const restored = {
|
|
5272
|
+
...settings,
|
|
5273
|
+
modelProvidersJson: JSON.stringify([
|
|
5274
|
+
...providers.filter((provider) => provider.id !== CODEX_PROVIDER_ID),
|
|
5275
|
+
{
|
|
5276
|
+
...retainedProvider,
|
|
5277
|
+
models: [
|
|
5278
|
+
...(current?.models ?? []).filter((candidate) => candidate.id !== model.id),
|
|
5279
|
+
model,
|
|
5280
|
+
],
|
|
5281
|
+
},
|
|
5282
|
+
]),
|
|
5283
|
+
};
|
|
5284
|
+
assertTurnExecutionPolicyMatchesConfigV1(restored, parsed, expected);
|
|
5285
|
+
return restored;
|
|
5083
5286
|
}
|
|
5084
5287
|
|
|
5085
5288
|
/**
|
|
@@ -5184,6 +5387,9 @@ export function resolveTurnExecutionPolicyV1(
|
|
|
5184
5387
|
): TurnExecutionPolicyV1 {
|
|
5185
5388
|
const catalogSettings = settingsForTurnExecutionPolicy(settings, input.modelId);
|
|
5186
5389
|
const productModelId = canonicalizeConfiguredModelId(catalogSettings, input.modelId);
|
|
5390
|
+
if (!isModelAvailableForNewSelection(catalogSettings, productModelId)) {
|
|
5391
|
+
throw new Error("Turn execution policy model is retired from new selection");
|
|
5392
|
+
}
|
|
5187
5393
|
const resolved = resolveModelProvider(catalogSettings, productModelId);
|
|
5188
5394
|
if (!resolved) {
|
|
5189
5395
|
throw new Error("Turn execution policy model is not present in the configured catalog");
|
|
@@ -7073,6 +7279,13 @@ export function validateModelCatalogSettings(
|
|
|
7073
7279
|
models.filter((model) => model.credentialSource.kind === "deployment").map((model) => model.id),
|
|
7074
7280
|
);
|
|
7075
7281
|
const noteProductIds = new Set(models.map((model) => model.id));
|
|
7282
|
+
if (settings.resolvedCodexModelsJson !== undefined) {
|
|
7283
|
+
for (const model of z
|
|
7284
|
+
.array(CodexCatalogModelSchema)
|
|
7285
|
+
.parse(JSON.parse(settings.resolvedCodexModelsJson))) {
|
|
7286
|
+
noteProductIds.add(model.id);
|
|
7287
|
+
}
|
|
7288
|
+
}
|
|
7076
7289
|
for (const model of configuredGatewayCatalogModels(settings)) {
|
|
7077
7290
|
deploymentProductIds.add(model.productId);
|
|
7078
7291
|
noteProductIds.add(model.productId);
|