@opengeni/config 0.6.2 → 0.6.9
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 +376 -8
- package/dist/index.js +742 -65
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/index.ts +1002 -86
package/dist/index.js
CHANGED
|
@@ -7,10 +7,22 @@ import {
|
|
|
7
7
|
ProductAccessMode,
|
|
8
8
|
ReasoningEffort,
|
|
9
9
|
SandboxBackend,
|
|
10
|
+
SessionMcpApprovalPolicy,
|
|
10
11
|
StaticUsageLimits,
|
|
12
|
+
TurnExecutionPolicyV1,
|
|
11
13
|
UsageLimitsMode
|
|
12
14
|
} from "@opengeni/contracts";
|
|
13
|
-
import {
|
|
15
|
+
import { CODEX_MODEL_TOOL_OUTPUT_TRUNCATION_TOKENS } from "@opengeni/codex";
|
|
16
|
+
import {
|
|
17
|
+
CODEX_FALLBACK_MODEL_SLUGS,
|
|
18
|
+
CODEX_MODEL_AUTO_COMPACT_TOKEN_LIMIT,
|
|
19
|
+
CODEX_MODEL_CONTEXT_WINDOW_TOKENS,
|
|
20
|
+
CODEX_MODEL_EFFECTIVE_CONTEXT_WINDOW_TOKENS,
|
|
21
|
+
CODEX_MODEL_ID_PREFIX,
|
|
22
|
+
CODEX_PROVIDER_BASE_URL,
|
|
23
|
+
CODEX_PROVIDER_ID
|
|
24
|
+
} from "@opengeni/codex/constants";
|
|
25
|
+
import { createHash } from "crypto";
|
|
14
26
|
import { z } from "zod";
|
|
15
27
|
var envName = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
16
28
|
var registryId = /^[A-Za-z0-9_-]+$/;
|
|
@@ -175,6 +187,9 @@ var SettingsSchema = z.object({
|
|
|
175
187
|
// holder of stream:control gets 403 until this flips. Keeps stream:control a
|
|
176
188
|
// declared-but-inert permission so later hardening is a flag flip.
|
|
177
189
|
streamControlEnabled: EnvBoolean.default(false),
|
|
190
|
+
// Existing-session explicit tool replacement is gated until every API and
|
|
191
|
+
// worker instance understands durable tools_provided provenance.
|
|
192
|
+
sessionTurnToolReplacementEnabled: EnvBoolean.default(false),
|
|
178
193
|
toolspaceEnabled: EnvBoolean.default(false),
|
|
179
194
|
toolspaceMaxCallsPerTurn: z.coerce.number().int().positive().default(200),
|
|
180
195
|
// Optional release-coherent bootstrap hint for custom rigs/connected machines
|
|
@@ -470,6 +485,15 @@ var SettingsSchema = z.object({
|
|
|
470
485
|
// EnvBoolean (NOT z.coerce.boolean(), which would coerce "false" -> true and
|
|
471
486
|
// turn the flag ON the moment anyone set the env var to disable it).
|
|
472
487
|
sandboxOwnershipEnabled: EnvBoolean.default(false),
|
|
488
|
+
// --- standalone rig-verifier ownership rollout flag, default OFF ---
|
|
489
|
+
// Rig verification creates a throwaway provider sandbox outside the normal
|
|
490
|
+
// session-turn path. When enabled, that sandbox must first acquire the same
|
|
491
|
+
// durable lease lifecycle used by session boxes so the global orphan sweep
|
|
492
|
+
// recognizes its exact provider instance. Keep this separate from the general
|
|
493
|
+
// sandboxOwnershipEnabled rollout: every reaper worker must understand verifier
|
|
494
|
+
// leases before dispatch is enabled. When false the verifier fails closed before
|
|
495
|
+
// provider create; it never falls back to the legacy unowned path.
|
|
496
|
+
rigVerificationLeaseOwnershipEnabled: EnvBoolean.default(false),
|
|
473
497
|
// --- lazy sandbox provisioning rollout flag, default OFF ---
|
|
474
498
|
// Only effective when sandboxOwnershipEnabled is ALSO on (lazy provisioning is a
|
|
475
499
|
// property of the owned path — the SDK never creates/resumes an injected session,
|
|
@@ -687,14 +711,8 @@ var SettingsSchema = z.object({
|
|
|
687
711
|
allowedTools: z.array(z.string().min(1)).optional(),
|
|
688
712
|
timeoutMs: z.number().int().positive().optional(),
|
|
689
713
|
cacheToolsList: z.boolean().default(false),
|
|
690
|
-
/**
|
|
691
|
-
|
|
692
|
-
* session MCP server row (never from OPENGENI_MCP_SERVERS). `true` = all
|
|
693
|
-
* tools require approval; a string[] = only the listed UNPREFIXED tool
|
|
694
|
-
* names do; absent = auto-run (the historical default). Enforced in the
|
|
695
|
-
* runtime by attaching `needsApproval` to the matching MCP tools.
|
|
696
|
-
*/
|
|
697
|
-
requireApproval: z.union([z.boolean(), z.array(z.string().min(1))]).optional(),
|
|
714
|
+
/** Runtime approval policy, overlaid from an attempt-frozen session snapshot. */
|
|
715
|
+
requireApproval: SessionMcpApprovalPolicy.optional(),
|
|
698
716
|
/**
|
|
699
717
|
* Extra request headers sent to this MCP server (credential injection
|
|
700
718
|
* for workspace-enabled capability MCPs). Populated at runtime from
|
|
@@ -712,11 +730,130 @@ var ModelPricingSchema = z.object({
|
|
|
712
730
|
outputMicrosPerMillionTokens: z.number().int().nonnegative(),
|
|
713
731
|
marginBps: z.number().int().min(0).max(1e5).optional()
|
|
714
732
|
});
|
|
733
|
+
var ModelPricingScheduleSchema = z.object({
|
|
734
|
+
default: ModelPricingSchema,
|
|
735
|
+
inputTokenTiers: z.array(
|
|
736
|
+
z.object({
|
|
737
|
+
minimumInputTokens: z.number().int().nonnegative(),
|
|
738
|
+
pricing: ModelPricingSchema
|
|
739
|
+
})
|
|
740
|
+
).optional()
|
|
741
|
+
}).superRefine((schedule, ctx) => {
|
|
742
|
+
let previous = -1;
|
|
743
|
+
for (const [index, tier] of (schedule.inputTokenTiers ?? []).entries()) {
|
|
744
|
+
if (tier.minimumInputTokens <= previous) {
|
|
745
|
+
ctx.addIssue({
|
|
746
|
+
code: "custom",
|
|
747
|
+
path: ["inputTokenTiers", index, "minimumInputTokens"],
|
|
748
|
+
message: "input-token tier thresholds must be strictly increasing"
|
|
749
|
+
});
|
|
750
|
+
}
|
|
751
|
+
previous = tier.minimumInputTokens;
|
|
752
|
+
}
|
|
753
|
+
});
|
|
754
|
+
var CapabilitySupportV1 = z.enum(["supported", "unsupported", "unknown"]);
|
|
755
|
+
var CapabilityStateV1Schema = z.object({
|
|
756
|
+
upstream: CapabilitySupportV1,
|
|
757
|
+
runnable: z.boolean()
|
|
758
|
+
}).superRefine((state, ctx) => {
|
|
759
|
+
if (state.upstream === "unsupported" && state.runnable) {
|
|
760
|
+
ctx.addIssue({
|
|
761
|
+
code: "custom",
|
|
762
|
+
path: ["runnable"],
|
|
763
|
+
message: "an upstream-unsupported capability cannot be runnable"
|
|
764
|
+
});
|
|
765
|
+
}
|
|
766
|
+
});
|
|
767
|
+
var ModelModalityV1 = z.enum(["text", "image", "audio"]);
|
|
768
|
+
var ModelLatencyModeV1 = z.enum(["standard", "priority", "fast"]);
|
|
769
|
+
var ModelCapabilitiesV1Schema = z.object({
|
|
770
|
+
reasoning: CapabilityStateV1Schema.extend({
|
|
771
|
+
efforts: z.array(ReasoningEffort),
|
|
772
|
+
defaultEffort: ReasoningEffort.nullable(),
|
|
773
|
+
required: z.boolean()
|
|
774
|
+
}),
|
|
775
|
+
functionCalling: CapabilityStateV1Schema,
|
|
776
|
+
structuredOutput: CapabilityStateV1Schema,
|
|
777
|
+
hostedTools: z.object({
|
|
778
|
+
webSearch: CapabilityStateV1Schema,
|
|
779
|
+
xSearch: CapabilityStateV1Schema,
|
|
780
|
+
codeExecution: CapabilityStateV1Schema
|
|
781
|
+
}),
|
|
782
|
+
inputModalities: z.array(ModelModalityV1).min(1),
|
|
783
|
+
outputModalities: z.array(ModelModalityV1).min(1),
|
|
784
|
+
transports: z.object({
|
|
785
|
+
sse: CapabilityStateV1Schema,
|
|
786
|
+
responsesWebSocket: CapabilityStateV1Schema,
|
|
787
|
+
realtimeAudio: CapabilityStateV1Schema
|
|
788
|
+
}),
|
|
789
|
+
latencyModes: z.array(
|
|
790
|
+
z.object({
|
|
791
|
+
id: ModelLatencyModeV1,
|
|
792
|
+
upstream: CapabilitySupportV1,
|
|
793
|
+
runnable: z.boolean(),
|
|
794
|
+
billingMultiplierBps: z.number().int().positive().optional()
|
|
795
|
+
})
|
|
796
|
+
).min(1)
|
|
797
|
+
}).superRefine((capabilities, ctx) => {
|
|
798
|
+
const efforts = new Set(capabilities.reasoning.efforts);
|
|
799
|
+
if (efforts.size !== capabilities.reasoning.efforts.length) {
|
|
800
|
+
ctx.addIssue({
|
|
801
|
+
code: "custom",
|
|
802
|
+
path: ["reasoning", "efforts"],
|
|
803
|
+
message: "reasoning efforts must be unique"
|
|
804
|
+
});
|
|
805
|
+
}
|
|
806
|
+
if (capabilities.reasoning.defaultEffort !== null && !efforts.has(capabilities.reasoning.defaultEffort)) {
|
|
807
|
+
ctx.addIssue({
|
|
808
|
+
code: "custom",
|
|
809
|
+
path: ["reasoning", "defaultEffort"],
|
|
810
|
+
message: "the default reasoning effort must be one of the supported efforts"
|
|
811
|
+
});
|
|
812
|
+
}
|
|
813
|
+
if (capabilities.reasoning.runnable && capabilities.reasoning.efforts.length === 0) {
|
|
814
|
+
ctx.addIssue({
|
|
815
|
+
code: "custom",
|
|
816
|
+
path: ["reasoning", "efforts"],
|
|
817
|
+
message: "a runnable reasoning capability must declare at least one effort"
|
|
818
|
+
});
|
|
819
|
+
}
|
|
820
|
+
for (const field of ["inputModalities", "outputModalities"]) {
|
|
821
|
+
if (new Set(capabilities[field]).size !== capabilities[field].length) {
|
|
822
|
+
ctx.addIssue({
|
|
823
|
+
code: "custom",
|
|
824
|
+
path: [field],
|
|
825
|
+
message: `${field} must be unique`
|
|
826
|
+
});
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
const latencyIds = /* @__PURE__ */ new Set();
|
|
830
|
+
for (const [index, mode] of capabilities.latencyModes.entries()) {
|
|
831
|
+
if (latencyIds.has(mode.id)) {
|
|
832
|
+
ctx.addIssue({
|
|
833
|
+
code: "custom",
|
|
834
|
+
path: ["latencyModes", index, "id"],
|
|
835
|
+
message: "latency mode ids must be unique"
|
|
836
|
+
});
|
|
837
|
+
}
|
|
838
|
+
latencyIds.add(mode.id);
|
|
839
|
+
if (mode.upstream === "unsupported" && mode.runnable) {
|
|
840
|
+
ctx.addIssue({
|
|
841
|
+
code: "custom",
|
|
842
|
+
path: ["latencyModes", index, "runnable"],
|
|
843
|
+
message: "an upstream-unsupported latency mode cannot be runnable"
|
|
844
|
+
});
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
});
|
|
715
848
|
var ModelProviderApi = z.enum(["responses", "chat"]);
|
|
716
849
|
var RegistryProviderKind = z.enum(["api-key", "codex-subscription"]);
|
|
717
850
|
var RegistryModelSchema = z.object({
|
|
718
851
|
id: z.string().min(1),
|
|
719
|
-
//
|
|
852
|
+
// canonical OpenGeni product id
|
|
853
|
+
upstreamModelId: z.string().min(1).optional(),
|
|
854
|
+
// exact provider slug; defaults to id
|
|
855
|
+
aliases: z.array(z.string().min(1)).optional(),
|
|
856
|
+
// accepted input only; never sent upstream
|
|
720
857
|
label: z.string().min(1).optional(),
|
|
721
858
|
// display name; defaults to id
|
|
722
859
|
contextWindowTokens: z.number().int().positive().optional(),
|
|
@@ -726,10 +863,30 @@ var RegistryModelSchema = z.object({
|
|
|
726
863
|
// the same 1.2x serialization allowance as Codex when materializing output.
|
|
727
864
|
toolOutputTruncationTokens: z.number().int().positive().optional(),
|
|
728
865
|
reasoningEffort: z.boolean().optional(),
|
|
729
|
-
//
|
|
866
|
+
// legacy compatibility input/projection
|
|
730
867
|
hostedWebSearch: z.boolean().optional(),
|
|
731
|
-
//
|
|
732
|
-
|
|
868
|
+
// legacy compatibility input/projection
|
|
869
|
+
capabilities: ModelCapabilitiesV1Schema.optional(),
|
|
870
|
+
pricing: z.union([ModelPricingSchema, ModelPricingScheduleSchema]).optional(),
|
|
871
|
+
// Reserved normalized contracts are derived by OpenGeni in V1. Generic
|
|
872
|
+
// registry JSON must not opt itself into workspace BYOK or reattribute cost.
|
|
873
|
+
credentialSource: z.never().optional(),
|
|
874
|
+
billing: z.never().optional()
|
|
875
|
+
}).superRefine((model, ctx) => {
|
|
876
|
+
if (model.capabilities && model.reasoningEffort !== void 0 && model.reasoningEffort !== model.capabilities.reasoning.runnable) {
|
|
877
|
+
ctx.addIssue({
|
|
878
|
+
code: "custom",
|
|
879
|
+
path: ["reasoningEffort"],
|
|
880
|
+
message: "legacy reasoningEffort must agree with capabilities.reasoning.runnable"
|
|
881
|
+
});
|
|
882
|
+
}
|
|
883
|
+
if (model.capabilities && model.hostedWebSearch !== void 0 && model.hostedWebSearch !== model.capabilities.hostedTools.webSearch.runnable) {
|
|
884
|
+
ctx.addIssue({
|
|
885
|
+
code: "custom",
|
|
886
|
+
path: ["hostedWebSearch"],
|
|
887
|
+
message: "legacy hostedWebSearch must agree with capabilities.hostedTools.webSearch.runnable"
|
|
888
|
+
});
|
|
889
|
+
}
|
|
733
890
|
});
|
|
734
891
|
var RegistryProviderSchema = z.object({
|
|
735
892
|
kind: RegistryProviderKind.default("api-key"),
|
|
@@ -745,6 +902,12 @@ var RegistryProviderSchema = z.object({
|
|
|
745
902
|
// ... OR name of the env var holding the key (preferred)
|
|
746
903
|
defaultQuery: z.record(z.string(), z.string()).optional(),
|
|
747
904
|
defaultHeaders: z.record(z.string(), z.string()).optional(),
|
|
905
|
+
publicDefaultQueryNames: z.array(z.string().min(1)).optional(),
|
|
906
|
+
publicDefaultHeaderNames: z.array(z.string().min(1)).optional(),
|
|
907
|
+
// V1 derives these from provider kind. Workspace BYOK is deliberately not a
|
|
908
|
+
// registry switch and requires a separately reviewed encrypted broker.
|
|
909
|
+
credentialSource: z.never().optional(),
|
|
910
|
+
billing: z.never().optional(),
|
|
748
911
|
models: z.array(RegistryModelSchema).min(1)
|
|
749
912
|
});
|
|
750
913
|
var IntegrationOAuthClientConfigSchema = z.object({
|
|
@@ -909,6 +1072,7 @@ function getSettings() {
|
|
|
909
1072
|
delegationSecret: optional("OPENGENI_DELEGATION_SECRET"),
|
|
910
1073
|
streamTokenSecret: optional("OPENGENI_STREAM_TOKEN_SECRET"),
|
|
911
1074
|
streamControlEnabled: optional("OPENGENI_STREAM_CONTROL_ENABLED"),
|
|
1075
|
+
sessionTurnToolReplacementEnabled: optional("OPENGENI_SESSION_TURN_TOOL_REPLACEMENT_ENABLED"),
|
|
912
1076
|
toolspaceEnabled: optional("OPENGENI_TOOLSPACE_ENABLED"),
|
|
913
1077
|
toolspaceMaxCallsPerTurn: optional("OPENGENI_TOOLSPACE_MAX_CALLS_PER_TURN"),
|
|
914
1078
|
ogtoolPackageSpec: optional("OPENGENI_OGTOOL_PACKAGE_SPEC"),
|
|
@@ -1025,6 +1189,9 @@ function getSettings() {
|
|
|
1025
1189
|
vercelTeamId: optional("OPENGENI_VERCEL_TEAM_ID"),
|
|
1026
1190
|
vercelRuntime: optional("OPENGENI_VERCEL_RUNTIME"),
|
|
1027
1191
|
sandboxOwnershipEnabled: optional("OPENGENI_SANDBOX_OWNERSHIP_ENABLED"),
|
|
1192
|
+
rigVerificationLeaseOwnershipEnabled: optional(
|
|
1193
|
+
"OPENGENI_RIG_VERIFICATION_LEASE_OWNERSHIP_ENABLED"
|
|
1194
|
+
),
|
|
1028
1195
|
sandboxLazyProvisionEnabled: optional("OPENGENI_SANDBOX_LAZY_PROVISION"),
|
|
1029
1196
|
sandboxSelfhostedEnabled: optional("OPENGENI_SANDBOX_SELFHOSTED_ENABLED"),
|
|
1030
1197
|
agentOpStreamEnabled: optional("OPENGENI_AGENT_OP_STREAM_ENABLED"),
|
|
@@ -1136,6 +1303,287 @@ function resolveProviderApiKey(provider, source = process.env) {
|
|
|
1136
1303
|
}
|
|
1137
1304
|
return void 0;
|
|
1138
1305
|
}
|
|
1306
|
+
var HTTP_FIELD_NAME = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/;
|
|
1307
|
+
var CREDENTIAL_LIKE_NAME_PARTS = /* @__PURE__ */ new Set([
|
|
1308
|
+
"apikey",
|
|
1309
|
+
"auth",
|
|
1310
|
+
"authorization",
|
|
1311
|
+
"bearer",
|
|
1312
|
+
"credential",
|
|
1313
|
+
"cookie",
|
|
1314
|
+
"key",
|
|
1315
|
+
"password",
|
|
1316
|
+
"secret",
|
|
1317
|
+
"session",
|
|
1318
|
+
"signature",
|
|
1319
|
+
"token"
|
|
1320
|
+
]);
|
|
1321
|
+
var REASONING_EFFORT_ORDER = new Map(
|
|
1322
|
+
ReasoningEffort.options.map((effort, index) => [effort, index])
|
|
1323
|
+
);
|
|
1324
|
+
var MODALITY_ORDER = new Map(["text", "image", "audio"].map((value, index) => [value, index]));
|
|
1325
|
+
var LATENCY_MODE_ORDER = new Map(
|
|
1326
|
+
["standard", "priority", "fast"].map((value, index) => [value, index])
|
|
1327
|
+
);
|
|
1328
|
+
function normalizeRegistryBaseUrl(value, providerId) {
|
|
1329
|
+
const url = new URL(value);
|
|
1330
|
+
if (url.username || url.password) {
|
|
1331
|
+
throw new Error(`provider ${providerId} baseUrl must not contain userinfo`);
|
|
1332
|
+
}
|
|
1333
|
+
if (url.search) {
|
|
1334
|
+
throw new Error(
|
|
1335
|
+
`provider ${providerId} baseUrl must not contain a query; move query entries to defaultQuery`
|
|
1336
|
+
);
|
|
1337
|
+
}
|
|
1338
|
+
if (url.hash) {
|
|
1339
|
+
throw new Error(`provider ${providerId} baseUrl must not contain a fragment`);
|
|
1340
|
+
}
|
|
1341
|
+
return url.toString();
|
|
1342
|
+
}
|
|
1343
|
+
function isCredentialLikeMetadataName(name) {
|
|
1344
|
+
return name.toLowerCase().split(/[-_.]/u).some((part) => CREDENTIAL_LIKE_NAME_PARTS.has(part));
|
|
1345
|
+
}
|
|
1346
|
+
function normalizeHeaderMap(providerId, headers) {
|
|
1347
|
+
if (!headers) {
|
|
1348
|
+
return void 0;
|
|
1349
|
+
}
|
|
1350
|
+
const normalized = {};
|
|
1351
|
+
const rawByNormalized = /* @__PURE__ */ new Map();
|
|
1352
|
+
for (const [rawName, value] of Object.entries(headers)) {
|
|
1353
|
+
if (!HTTP_FIELD_NAME.test(rawName)) {
|
|
1354
|
+
throw new Error(
|
|
1355
|
+
`provider ${providerId} defaultHeaders contains invalid HTTP field name ${JSON.stringify(rawName)}`
|
|
1356
|
+
);
|
|
1357
|
+
}
|
|
1358
|
+
const name = rawName.toLowerCase();
|
|
1359
|
+
const previous = rawByNormalized.get(name);
|
|
1360
|
+
if (previous !== void 0) {
|
|
1361
|
+
throw new Error(
|
|
1362
|
+
`provider ${providerId} defaultHeaders names ${JSON.stringify(previous)} and ${JSON.stringify(rawName)} collide after lowercase normalization`
|
|
1363
|
+
);
|
|
1364
|
+
}
|
|
1365
|
+
if (name === "authorization") {
|
|
1366
|
+
throw new Error(
|
|
1367
|
+
`provider ${providerId} defaultHeaders must not override SDK-managed Authorization`
|
|
1368
|
+
);
|
|
1369
|
+
}
|
|
1370
|
+
rawByNormalized.set(name, rawName);
|
|
1371
|
+
normalized[name] = value;
|
|
1372
|
+
}
|
|
1373
|
+
return normalized;
|
|
1374
|
+
}
|
|
1375
|
+
function normalizePublicHeaderNames(providerId, names, headers) {
|
|
1376
|
+
if (!names) {
|
|
1377
|
+
return void 0;
|
|
1378
|
+
}
|
|
1379
|
+
const normalized = [];
|
|
1380
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1381
|
+
for (const rawName of names) {
|
|
1382
|
+
if (!HTTP_FIELD_NAME.test(rawName)) {
|
|
1383
|
+
throw new Error(
|
|
1384
|
+
`provider ${providerId} publicDefaultHeaderNames contains invalid HTTP field name ${JSON.stringify(rawName)}`
|
|
1385
|
+
);
|
|
1386
|
+
}
|
|
1387
|
+
const name = rawName.toLowerCase();
|
|
1388
|
+
if (seen.has(name)) {
|
|
1389
|
+
throw new Error(
|
|
1390
|
+
`provider ${providerId} publicDefaultHeaderNames contains duplicate normalized name ${JSON.stringify(name)}`
|
|
1391
|
+
);
|
|
1392
|
+
}
|
|
1393
|
+
if (!(name in (headers ?? {}))) {
|
|
1394
|
+
throw new Error(
|
|
1395
|
+
`provider ${providerId} publicDefaultHeaderNames declares absent defaultHeaders entry ${JSON.stringify(name)}`
|
|
1396
|
+
);
|
|
1397
|
+
}
|
|
1398
|
+
if (isCredentialLikeMetadataName(name)) {
|
|
1399
|
+
throw new Error(
|
|
1400
|
+
`provider ${providerId} publicDefaultHeaderNames cannot classify credential-like name ${JSON.stringify(name)} as public`
|
|
1401
|
+
);
|
|
1402
|
+
}
|
|
1403
|
+
seen.add(name);
|
|
1404
|
+
normalized.push(name);
|
|
1405
|
+
}
|
|
1406
|
+
return normalized;
|
|
1407
|
+
}
|
|
1408
|
+
function normalizeQueryMap(providerId, query) {
|
|
1409
|
+
if (!query) {
|
|
1410
|
+
return void 0;
|
|
1411
|
+
}
|
|
1412
|
+
for (const name of Object.keys(query)) {
|
|
1413
|
+
if (!name) {
|
|
1414
|
+
throw new Error(`provider ${providerId} defaultQuery contains an empty name`);
|
|
1415
|
+
}
|
|
1416
|
+
}
|
|
1417
|
+
return { ...query };
|
|
1418
|
+
}
|
|
1419
|
+
function normalizePublicQueryNames(providerId, names, query) {
|
|
1420
|
+
if (!names) {
|
|
1421
|
+
return void 0;
|
|
1422
|
+
}
|
|
1423
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1424
|
+
for (const name of names) {
|
|
1425
|
+
if (seen.has(name)) {
|
|
1426
|
+
throw new Error(
|
|
1427
|
+
`provider ${providerId} publicDefaultQueryNames contains duplicate name ${JSON.stringify(name)}`
|
|
1428
|
+
);
|
|
1429
|
+
}
|
|
1430
|
+
if (!(name in (query ?? {}))) {
|
|
1431
|
+
throw new Error(
|
|
1432
|
+
`provider ${providerId} publicDefaultQueryNames declares absent defaultQuery entry ${JSON.stringify(name)}`
|
|
1433
|
+
);
|
|
1434
|
+
}
|
|
1435
|
+
if (isCredentialLikeMetadataName(name)) {
|
|
1436
|
+
throw new Error(
|
|
1437
|
+
`provider ${providerId} publicDefaultQueryNames cannot classify credential-like name ${JSON.stringify(name)} as public`
|
|
1438
|
+
);
|
|
1439
|
+
}
|
|
1440
|
+
seen.add(name);
|
|
1441
|
+
}
|
|
1442
|
+
return [...names];
|
|
1443
|
+
}
|
|
1444
|
+
function normalizeRegistryProvider(provider) {
|
|
1445
|
+
const defaultHeaders = normalizeHeaderMap(provider.id, provider.defaultHeaders);
|
|
1446
|
+
const defaultQuery = normalizeQueryMap(provider.id, provider.defaultQuery);
|
|
1447
|
+
return {
|
|
1448
|
+
...provider,
|
|
1449
|
+
baseUrl: normalizeRegistryBaseUrl(provider.baseUrl, provider.id),
|
|
1450
|
+
...defaultHeaders === void 0 ? {} : { defaultHeaders },
|
|
1451
|
+
...defaultQuery === void 0 ? {} : { defaultQuery },
|
|
1452
|
+
...provider.publicDefaultHeaderNames === void 0 ? {} : {
|
|
1453
|
+
publicDefaultHeaderNames: normalizePublicHeaderNames(
|
|
1454
|
+
provider.id,
|
|
1455
|
+
provider.publicDefaultHeaderNames,
|
|
1456
|
+
defaultHeaders
|
|
1457
|
+
)
|
|
1458
|
+
},
|
|
1459
|
+
...provider.publicDefaultQueryNames === void 0 ? {} : {
|
|
1460
|
+
publicDefaultQueryNames: normalizePublicQueryNames(
|
|
1461
|
+
provider.id,
|
|
1462
|
+
provider.publicDefaultQueryNames,
|
|
1463
|
+
defaultQuery
|
|
1464
|
+
)
|
|
1465
|
+
}
|
|
1466
|
+
};
|
|
1467
|
+
}
|
|
1468
|
+
function normalizeModelPricingSchedule(pricing) {
|
|
1469
|
+
return "default" in pricing ? pricing : { default: pricing };
|
|
1470
|
+
}
|
|
1471
|
+
function normalizeCapabilities(capabilities) {
|
|
1472
|
+
const parsed = ModelCapabilitiesV1Schema.parse(capabilities);
|
|
1473
|
+
return {
|
|
1474
|
+
...parsed,
|
|
1475
|
+
reasoning: {
|
|
1476
|
+
...parsed.reasoning,
|
|
1477
|
+
efforts: [...parsed.reasoning.efforts].sort(
|
|
1478
|
+
(left, right) => (REASONING_EFFORT_ORDER.get(left) ?? 0) - (REASONING_EFFORT_ORDER.get(right) ?? 0)
|
|
1479
|
+
)
|
|
1480
|
+
},
|
|
1481
|
+
inputModalities: [...parsed.inputModalities].sort(
|
|
1482
|
+
(left, right) => (MODALITY_ORDER.get(left) ?? 0) - (MODALITY_ORDER.get(right) ?? 0)
|
|
1483
|
+
),
|
|
1484
|
+
outputModalities: [...parsed.outputModalities].sort(
|
|
1485
|
+
(left, right) => (MODALITY_ORDER.get(left) ?? 0) - (MODALITY_ORDER.get(right) ?? 0)
|
|
1486
|
+
),
|
|
1487
|
+
latencyModes: [...parsed.latencyModes].sort(
|
|
1488
|
+
(left, right) => (LATENCY_MODE_ORDER.get(left.id) ?? 0) - (LATENCY_MODE_ORDER.get(right.id) ?? 0)
|
|
1489
|
+
)
|
|
1490
|
+
};
|
|
1491
|
+
}
|
|
1492
|
+
function legacyModelCapabilities(settings, input) {
|
|
1493
|
+
const reasoningEfforts = input.reasoningEffort ? configuredAllowedReasoningEfforts(settings) : [];
|
|
1494
|
+
return normalizeCapabilities({
|
|
1495
|
+
reasoning: {
|
|
1496
|
+
upstream: input.reasoningEffort ? "supported" : "unknown",
|
|
1497
|
+
runnable: input.reasoningEffort,
|
|
1498
|
+
efforts: reasoningEfforts,
|
|
1499
|
+
defaultEffort: input.reasoningEffort ? settings.openaiReasoningEffort : null,
|
|
1500
|
+
required: false
|
|
1501
|
+
},
|
|
1502
|
+
functionCalling: { upstream: "unknown", runnable: true },
|
|
1503
|
+
structuredOutput: { upstream: "unknown", runnable: false },
|
|
1504
|
+
hostedTools: {
|
|
1505
|
+
webSearch: {
|
|
1506
|
+
upstream: input.hostedWebSearch ? "supported" : "unknown",
|
|
1507
|
+
runnable: input.hostedWebSearch
|
|
1508
|
+
},
|
|
1509
|
+
xSearch: { upstream: "unknown", runnable: false },
|
|
1510
|
+
codeExecution: { upstream: "unknown", runnable: false }
|
|
1511
|
+
},
|
|
1512
|
+
inputModalities: ["text"],
|
|
1513
|
+
outputModalities: ["text"],
|
|
1514
|
+
transports: {
|
|
1515
|
+
sse: { upstream: "unknown", runnable: true },
|
|
1516
|
+
responsesWebSocket: { upstream: "unknown", runnable: false },
|
|
1517
|
+
realtimeAudio: { upstream: "unknown", runnable: false }
|
|
1518
|
+
},
|
|
1519
|
+
latencyModes: [{ id: "standard", upstream: "unknown", runnable: true }]
|
|
1520
|
+
});
|
|
1521
|
+
}
|
|
1522
|
+
function registryCredentialSource(provider) {
|
|
1523
|
+
return provider.kind === "codex-subscription" ? { kind: "connected_subscription", provider: "codex" } : { kind: "deployment", mechanism: "api_key" };
|
|
1524
|
+
}
|
|
1525
|
+
function registryBilling(provider) {
|
|
1526
|
+
return provider.kind === "codex-subscription" ? { upstreamPayer: "connected_subscription", metering: "external" } : { upstreamPayer: "deployment", metering: "opengeni_credits" };
|
|
1527
|
+
}
|
|
1528
|
+
function builtinCredentialSource(settings) {
|
|
1529
|
+
if (settings.openaiProvider === "azure" && !settings.azureOpenaiApiKey) {
|
|
1530
|
+
return { kind: "deployment", mechanism: "azure_ad_bearer" };
|
|
1531
|
+
}
|
|
1532
|
+
return { kind: "deployment", mechanism: "api_key" };
|
|
1533
|
+
}
|
|
1534
|
+
function staticRequestMetadataForDigest(provider) {
|
|
1535
|
+
const publicHeaders = new Set(provider.publicDefaultHeaderNames ?? []);
|
|
1536
|
+
const publicQuery = new Set(provider.publicDefaultQueryNames ?? []);
|
|
1537
|
+
return {
|
|
1538
|
+
headers: Object.entries(provider.defaultHeaders ?? {}).sort(([left], [right]) => left.localeCompare(right)).map(
|
|
1539
|
+
([name, value]) => publicHeaders.has(name) ? { name, classification: "public", value } : { name, classification: "secret" }
|
|
1540
|
+
),
|
|
1541
|
+
query: Object.entries(provider.defaultQuery ?? {}).sort(([left], [right]) => left.localeCompare(right)).map(
|
|
1542
|
+
([name, value]) => publicQuery.has(name) ? { name, classification: "public", value } : { name, classification: "secret" }
|
|
1543
|
+
)
|
|
1544
|
+
};
|
|
1545
|
+
}
|
|
1546
|
+
function canonicalJson(value) {
|
|
1547
|
+
const normalize = (input) => {
|
|
1548
|
+
if (Array.isArray(input)) {
|
|
1549
|
+
return input.map((entry) => normalize(entry));
|
|
1550
|
+
}
|
|
1551
|
+
if (input && typeof input === "object") {
|
|
1552
|
+
const out = {};
|
|
1553
|
+
for (const key of Object.keys(input).sort()) {
|
|
1554
|
+
const child = input[key];
|
|
1555
|
+
if (child !== void 0) {
|
|
1556
|
+
out[key] = normalize(child);
|
|
1557
|
+
}
|
|
1558
|
+
}
|
|
1559
|
+
return out;
|
|
1560
|
+
}
|
|
1561
|
+
return input;
|
|
1562
|
+
};
|
|
1563
|
+
return JSON.stringify(normalize(value));
|
|
1564
|
+
}
|
|
1565
|
+
function definitionVersionFor(model, provider) {
|
|
1566
|
+
const requestMetadata = staticRequestMetadataForDigest(provider);
|
|
1567
|
+
const digestInput = canonicalJson({
|
|
1568
|
+
schemaVersion: model.schemaVersion,
|
|
1569
|
+
id: model.id,
|
|
1570
|
+
providerId: model.providerId,
|
|
1571
|
+
deployment: model.deployment,
|
|
1572
|
+
provider: {
|
|
1573
|
+
adapterKind: provider.kind,
|
|
1574
|
+
wireApi: provider.api,
|
|
1575
|
+
baseUrl: provider.baseUrl ?? null,
|
|
1576
|
+
defaultHeaders: requestMetadata.headers,
|
|
1577
|
+
defaultQuery: requestMetadata.query
|
|
1578
|
+
},
|
|
1579
|
+
credentialSource: model.credentialSource,
|
|
1580
|
+
billing: model.billing,
|
|
1581
|
+
executionLimits: model.executionLimits,
|
|
1582
|
+
capabilities: model.capabilities,
|
|
1583
|
+
pricing: model.pricing ?? null
|
|
1584
|
+
});
|
|
1585
|
+
return `sha256:${createHash("sha256").update("opengeni:model-definition:v1\n", "utf8").update(digestInput, "utf8").digest("hex")}`;
|
|
1586
|
+
}
|
|
1139
1587
|
function builtinProviderId(settings) {
|
|
1140
1588
|
return settings.openaiProvider === "azure" ? "azure" : "openai";
|
|
1141
1589
|
}
|
|
@@ -1143,18 +1591,22 @@ function builtinProviderLabel(settings) {
|
|
|
1143
1591
|
return settings.openaiProvider === "azure" ? "Azure OpenAI" : "OpenAI";
|
|
1144
1592
|
}
|
|
1145
1593
|
function configuredProviders(settings) {
|
|
1594
|
+
const credentialSource = builtinCredentialSource(settings);
|
|
1146
1595
|
const builtin = {
|
|
1147
1596
|
id: builtinProviderId(settings),
|
|
1148
1597
|
label: builtinProviderLabel(settings),
|
|
1149
1598
|
kind: "api-key",
|
|
1150
1599
|
api: "responses",
|
|
1151
|
-
builtin: true
|
|
1600
|
+
builtin: true,
|
|
1601
|
+
credentialSource,
|
|
1602
|
+
billing: { upstreamPayer: "deployment", metering: "opengeni_credits" }
|
|
1152
1603
|
};
|
|
1153
1604
|
if (settings.openaiProvider === "azure") {
|
|
1154
|
-
|
|
1605
|
+
const baseUrl = settings.azureOpenaiBaseUrl ?? settings.azureOpenaiEndpoint;
|
|
1606
|
+
builtin.baseUrl = baseUrl ? normalizeRegistryBaseUrl(baseUrl, builtin.id) : void 0;
|
|
1155
1607
|
builtin.apiKey = settings.azureOpenaiApiKey ?? settings.azureOpenaiAdToken;
|
|
1156
1608
|
} else {
|
|
1157
|
-
builtin.baseUrl = settings.openaiBaseUrl;
|
|
1609
|
+
builtin.baseUrl = settings.openaiBaseUrl ? normalizeRegistryBaseUrl(settings.openaiBaseUrl, builtin.id) : void 0;
|
|
1158
1610
|
builtin.apiKey = settings.openaiApiKey;
|
|
1159
1611
|
}
|
|
1160
1612
|
const registry = parseModelProvidersJson(settings.modelProvidersJson).map(
|
|
@@ -1167,73 +1619,195 @@ function configuredProviders(settings) {
|
|
|
1167
1619
|
baseUrl: provider.baseUrl,
|
|
1168
1620
|
apiKey: resolveProviderApiKey(provider),
|
|
1169
1621
|
defaultQuery: provider.defaultQuery,
|
|
1170
|
-
defaultHeaders: provider.defaultHeaders
|
|
1622
|
+
defaultHeaders: provider.defaultHeaders,
|
|
1623
|
+
publicDefaultQueryNames: provider.publicDefaultQueryNames,
|
|
1624
|
+
publicDefaultHeaderNames: provider.publicDefaultHeaderNames,
|
|
1625
|
+
credentialSource: registryCredentialSource(provider),
|
|
1626
|
+
billing: registryBilling(provider)
|
|
1171
1627
|
})
|
|
1172
1628
|
);
|
|
1173
1629
|
return [builtin, ...registry];
|
|
1174
1630
|
}
|
|
1631
|
+
function withCodexCatalogProvider(settings) {
|
|
1632
|
+
const providers = parseModelProvidersJson(settings.modelProvidersJson);
|
|
1633
|
+
if (providers.some((provider2) => provider2.id === CODEX_PROVIDER_ID)) {
|
|
1634
|
+
return settings;
|
|
1635
|
+
}
|
|
1636
|
+
const provider = {
|
|
1637
|
+
kind: "codex-subscription",
|
|
1638
|
+
id: CODEX_PROVIDER_ID,
|
|
1639
|
+
label: "Codex (ChatGPT subscription)",
|
|
1640
|
+
api: "responses",
|
|
1641
|
+
baseUrl: CODEX_PROVIDER_BASE_URL,
|
|
1642
|
+
models: CODEX_FALLBACK_MODEL_SLUGS.map((slug) => ({
|
|
1643
|
+
id: `${CODEX_MODEL_ID_PREFIX}${slug}`,
|
|
1644
|
+
upstreamModelId: slug,
|
|
1645
|
+
label: slug,
|
|
1646
|
+
reasoningEffort: true,
|
|
1647
|
+
contextWindowTokens: CODEX_MODEL_CONTEXT_WINDOW_TOKENS,
|
|
1648
|
+
effectiveContextWindowTokens: CODEX_MODEL_EFFECTIVE_CONTEXT_WINDOW_TOKENS,
|
|
1649
|
+
autoCompactTokenLimit: CODEX_MODEL_AUTO_COMPACT_TOKEN_LIMIT,
|
|
1650
|
+
toolOutputTruncationTokens: CODEX_MODEL_TOOL_OUTPUT_TRUNCATION_TOKENS
|
|
1651
|
+
}))
|
|
1652
|
+
};
|
|
1653
|
+
return { ...settings, modelProvidersJson: JSON.stringify([...providers, provider]) };
|
|
1654
|
+
}
|
|
1175
1655
|
function policyProviderIdForModel(settings, modelId) {
|
|
1176
|
-
|
|
1656
|
+
const canonicalModelId = canonicalizeConfiguredModelId(settings, modelId);
|
|
1657
|
+
if (canonicalModelId.startsWith(CODEX_MODEL_ID_PREFIX)) {
|
|
1177
1658
|
return CODEX_PROVIDER_ID;
|
|
1178
1659
|
}
|
|
1179
|
-
const configured = configuredModels(settings).find((model) => model.id ===
|
|
1660
|
+
const configured = configuredModels(settings).find((model) => model.id === canonicalModelId);
|
|
1180
1661
|
return configured?.providerId ?? builtinProviderId(settings);
|
|
1181
1662
|
}
|
|
1663
|
+
function resolvedExecutionLimits(settings, model) {
|
|
1664
|
+
return {
|
|
1665
|
+
contextWindowTokens: model.contextWindowTokens ?? settings.contextWindowTokens,
|
|
1666
|
+
effectiveContextWindowTokens: model.effectiveContextWindowTokens ?? settings.contextEffectiveWindowTokens ?? null,
|
|
1667
|
+
autoCompactTokenLimit: model.autoCompactTokenLimit ?? settings.contextAutoCompactThresholdTokens ?? null,
|
|
1668
|
+
toolOutputTruncationTokens: model.toolOutputTruncationTokens ?? settings.modelToolOutputTruncationTokens ?? null
|
|
1669
|
+
};
|
|
1670
|
+
}
|
|
1671
|
+
function finalizeConfiguredModel(settings, provider, input) {
|
|
1672
|
+
const modelWithoutVersion = {
|
|
1673
|
+
schemaVersion: 1,
|
|
1674
|
+
...input,
|
|
1675
|
+
executionLimits: resolvedExecutionLimits(settings, input)
|
|
1676
|
+
};
|
|
1677
|
+
return {
|
|
1678
|
+
...modelWithoutVersion,
|
|
1679
|
+
definitionVersion: definitionVersionFor(modelWithoutVersion, provider)
|
|
1680
|
+
};
|
|
1681
|
+
}
|
|
1682
|
+
function assertUniqueModelIdentities(models) {
|
|
1683
|
+
const canonicalOwners = /* @__PURE__ */ new Map();
|
|
1684
|
+
for (const model of models) {
|
|
1685
|
+
const previous = canonicalOwners.get(model.id);
|
|
1686
|
+
if (previous !== void 0) {
|
|
1687
|
+
throw new Error(
|
|
1688
|
+
`OPENGENI_MODEL_PROVIDERS_JSON model id ${JSON.stringify(model.id)} is declared by both ${previous} and ${model.providerId}`
|
|
1689
|
+
);
|
|
1690
|
+
}
|
|
1691
|
+
canonicalOwners.set(model.id, model.providerId);
|
|
1692
|
+
}
|
|
1693
|
+
const acceptedInputs = new Map(canonicalOwners);
|
|
1694
|
+
for (const model of models) {
|
|
1695
|
+
const ownAliases = /* @__PURE__ */ new Set();
|
|
1696
|
+
for (const alias of model.aliases) {
|
|
1697
|
+
if (ownAliases.has(alias)) {
|
|
1698
|
+
throw new Error(
|
|
1699
|
+
`OPENGENI_MODEL_PROVIDERS_JSON model ${JSON.stringify(model.id)} contains duplicate alias ${JSON.stringify(alias)}`
|
|
1700
|
+
);
|
|
1701
|
+
}
|
|
1702
|
+
ownAliases.add(alias);
|
|
1703
|
+
const previous = acceptedInputs.get(alias);
|
|
1704
|
+
if (previous !== void 0) {
|
|
1705
|
+
throw new Error(
|
|
1706
|
+
`OPENGENI_MODEL_PROVIDERS_JSON alias ${JSON.stringify(alias)} for model ${JSON.stringify(model.id)} collides with model/provider ${previous}`
|
|
1707
|
+
);
|
|
1708
|
+
}
|
|
1709
|
+
acceptedInputs.set(alias, model.id);
|
|
1710
|
+
}
|
|
1711
|
+
}
|
|
1712
|
+
}
|
|
1182
1713
|
function configuredModels(settings) {
|
|
1183
1714
|
const builtinId = builtinProviderId(settings);
|
|
1184
1715
|
const builtinLabel = builtinProviderLabel(settings);
|
|
1716
|
+
const providers = configuredProviders(settings);
|
|
1717
|
+
const providerById = new Map(providers.map((provider) => [provider.id, provider]));
|
|
1718
|
+
const pricingSchedules = configuredModelPricingSchedules(settings);
|
|
1719
|
+
const parsedRegistry = parseModelProvidersJson(settings.modelProvidersJson);
|
|
1185
1720
|
const registryOwnedIds = new Set(
|
|
1186
|
-
|
|
1187
|
-
(provider) => provider.models.map((model) => model.id)
|
|
1188
|
-
)
|
|
1721
|
+
parsedRegistry.flatMap((provider) => provider.models.map((model) => model.id))
|
|
1189
1722
|
);
|
|
1190
|
-
const
|
|
1723
|
+
const registryAliases = new Set(
|
|
1724
|
+
parsedRegistry.flatMap((provider) => provider.models.flatMap((model) => model.aliases ?? []))
|
|
1725
|
+
);
|
|
1726
|
+
const isRegistryNamespaced = (id) => id.startsWith(CODEX_MODEL_ID_PREFIX) || registryAliases.has(id) || id.includes("/") && registryOwnedIds.has(id);
|
|
1727
|
+
const builtinProvider = providerById.get(builtinId);
|
|
1728
|
+
if (!builtinProvider) {
|
|
1729
|
+
throw new Error(`Built-in model provider ${builtinId} is not configured`);
|
|
1730
|
+
}
|
|
1191
1731
|
const out = uniqueValues([
|
|
1192
1732
|
settings.openaiModel,
|
|
1193
1733
|
...splitCsv(settings.openaiAllowedModels)
|
|
1194
|
-
]).filter((id) => !isRegistryNamespaced(id)).map((id) =>
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1734
|
+
]).filter((id) => !isRegistryNamespaced(id)).map((id) => {
|
|
1735
|
+
const capabilities = legacyModelCapabilities(settings, {
|
|
1736
|
+
reasoningEffort: true,
|
|
1737
|
+
hostedWebSearch: settings.webSearchEnabled
|
|
1738
|
+
});
|
|
1739
|
+
return finalizeConfiguredModel(settings, builtinProvider, {
|
|
1740
|
+
id,
|
|
1741
|
+
aliases: [],
|
|
1742
|
+
label: id,
|
|
1743
|
+
providerId: builtinId,
|
|
1744
|
+
providerLabel: builtinLabel,
|
|
1745
|
+
api: "responses",
|
|
1746
|
+
upstreamModelId: id,
|
|
1747
|
+
deployment: { upstreamModelId: id, wireApi: "responses" },
|
|
1748
|
+
credentialSource: builtinProvider.credentialSource,
|
|
1749
|
+
billing: builtinProvider.billing,
|
|
1750
|
+
capabilities,
|
|
1751
|
+
...pricingSchedules[id] === void 0 ? {} : { pricing: pricingSchedules[id] },
|
|
1752
|
+
contextWindowTokens: settings.contextWindowTokens,
|
|
1753
|
+
toolOutputTruncationTokens: settings.modelToolOutputTruncationTokens,
|
|
1754
|
+
reasoningEffort: capabilities.reasoning.runnable,
|
|
1755
|
+
hostedWebSearch: capabilities.hostedTools.webSearch.runnable
|
|
1756
|
+
});
|
|
1757
|
+
});
|
|
1758
|
+
for (const provider of parsedRegistry) {
|
|
1206
1759
|
const providerLabel = provider.label ?? provider.id;
|
|
1760
|
+
const resolvedProvider = providerById.get(provider.id);
|
|
1761
|
+
if (!resolvedProvider) {
|
|
1762
|
+
throw new Error(`Registry model provider ${provider.id} is not configured`);
|
|
1763
|
+
}
|
|
1207
1764
|
for (const model of provider.models) {
|
|
1208
|
-
|
|
1209
|
-
id: model.id,
|
|
1210
|
-
label: model.label ?? model.id,
|
|
1211
|
-
providerId: provider.id,
|
|
1212
|
-
providerLabel,
|
|
1213
|
-
api: provider.api,
|
|
1214
|
-
...model.contextWindowTokens === void 0 ? {} : { contextWindowTokens: model.contextWindowTokens },
|
|
1215
|
-
...model.effectiveContextWindowTokens === void 0 ? {} : { effectiveContextWindowTokens: model.effectiveContextWindowTokens },
|
|
1216
|
-
...model.autoCompactTokenLimit === void 0 ? {} : { autoCompactTokenLimit: model.autoCompactTokenLimit },
|
|
1217
|
-
...model.toolOutputTruncationTokens === void 0 ? {} : { toolOutputTruncationTokens: model.toolOutputTruncationTokens },
|
|
1765
|
+
const capabilities = model.capabilities ? normalizeCapabilities(model.capabilities) : legacyModelCapabilities(settings, {
|
|
1218
1766
|
reasoningEffort: model.reasoningEffort ?? false,
|
|
1219
1767
|
hostedWebSearch: model.hostedWebSearch ?? false
|
|
1220
1768
|
});
|
|
1769
|
+
const upstreamModelId = model.upstreamModelId ?? model.id;
|
|
1770
|
+
out.push(
|
|
1771
|
+
finalizeConfiguredModel(settings, resolvedProvider, {
|
|
1772
|
+
id: model.id,
|
|
1773
|
+
aliases: [...model.aliases ?? []],
|
|
1774
|
+
label: model.label ?? model.id,
|
|
1775
|
+
providerId: provider.id,
|
|
1776
|
+
providerLabel,
|
|
1777
|
+
api: provider.api,
|
|
1778
|
+
upstreamModelId,
|
|
1779
|
+
deployment: { upstreamModelId, wireApi: provider.api },
|
|
1780
|
+
credentialSource: resolvedProvider.credentialSource,
|
|
1781
|
+
billing: resolvedProvider.billing,
|
|
1782
|
+
capabilities,
|
|
1783
|
+
...pricingSchedules[model.id] === void 0 ? {} : { pricing: pricingSchedules[model.id] },
|
|
1784
|
+
...model.contextWindowTokens === void 0 ? {} : { contextWindowTokens: model.contextWindowTokens },
|
|
1785
|
+
...model.effectiveContextWindowTokens === void 0 ? {} : { effectiveContextWindowTokens: model.effectiveContextWindowTokens },
|
|
1786
|
+
...model.autoCompactTokenLimit === void 0 ? {} : { autoCompactTokenLimit: model.autoCompactTokenLimit },
|
|
1787
|
+
...model.toolOutputTruncationTokens === void 0 ? {} : { toolOutputTruncationTokens: model.toolOutputTruncationTokens },
|
|
1788
|
+
reasoningEffort: capabilities.reasoning.runnable,
|
|
1789
|
+
hostedWebSearch: capabilities.hostedTools.webSearch.runnable
|
|
1790
|
+
})
|
|
1791
|
+
);
|
|
1221
1792
|
}
|
|
1222
1793
|
}
|
|
1223
|
-
|
|
1224
|
-
return out
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1794
|
+
assertUniqueModelIdentities(out);
|
|
1795
|
+
return out;
|
|
1796
|
+
}
|
|
1797
|
+
function canonicalizeConfiguredModelId(settings, modelId) {
|
|
1798
|
+
const models = configuredModels(settings);
|
|
1799
|
+
const canonical = models.find((model) => model.id === modelId);
|
|
1800
|
+
if (canonical) {
|
|
1801
|
+
return canonical.id;
|
|
1802
|
+
}
|
|
1803
|
+
return models.find((model) => model.aliases.includes(modelId))?.id ?? modelId;
|
|
1231
1804
|
}
|
|
1232
1805
|
function configuredAllowedModels(settings) {
|
|
1233
1806
|
return configuredModels(settings).map((model) => model.id);
|
|
1234
1807
|
}
|
|
1235
1808
|
function resolveModelProvider(settings, modelId) {
|
|
1236
|
-
const
|
|
1809
|
+
const canonicalModelId = canonicalizeConfiguredModelId(settings, modelId);
|
|
1810
|
+
const model = configuredModels(settings).find((candidate) => candidate.id === canonicalModelId);
|
|
1237
1811
|
if (!model) {
|
|
1238
1812
|
return void 0;
|
|
1239
1813
|
}
|
|
@@ -1245,22 +1819,97 @@ function resolveModelProvider(settings, modelId) {
|
|
|
1245
1819
|
}
|
|
1246
1820
|
return { provider, model };
|
|
1247
1821
|
}
|
|
1248
|
-
function
|
|
1822
|
+
function settingsForTurnExecutionPolicy(settings, modelId) {
|
|
1823
|
+
return settings.codexSubscriptionEnabled && modelId.startsWith(CODEX_MODEL_ID_PREFIX) ? withCodexCatalogProvider(settings) : settings;
|
|
1824
|
+
}
|
|
1825
|
+
function resolveTurnExecutionPolicyV1(settings, input) {
|
|
1826
|
+
const catalogSettings = settingsForTurnExecutionPolicy(settings, input.modelId);
|
|
1827
|
+
const productModelId = canonicalizeConfiguredModelId(catalogSettings, input.modelId);
|
|
1828
|
+
const resolved = resolveModelProvider(catalogSettings, productModelId);
|
|
1829
|
+
if (!resolved) {
|
|
1830
|
+
throw new Error("Turn execution policy model is not present in the configured catalog");
|
|
1831
|
+
}
|
|
1832
|
+
if (input.requestedModelId !== null && canonicalizeConfiguredModelId(catalogSettings, input.requestedModelId) !== productModelId) {
|
|
1833
|
+
throw new Error("Turn execution policy requested model does not canonicalize to its product");
|
|
1834
|
+
}
|
|
1835
|
+
return TurnExecutionPolicyV1.parse({
|
|
1836
|
+
schemaVersion: 1,
|
|
1837
|
+
productModelId,
|
|
1838
|
+
requestedModelId: input.requestedModelId,
|
|
1839
|
+
modelSource: input.modelSource,
|
|
1840
|
+
reasoningEffort: input.reasoningEffort,
|
|
1841
|
+
reasoningSource: input.reasoningSource,
|
|
1842
|
+
providerId: resolved.provider.id,
|
|
1843
|
+
upstreamModelId: resolved.model.upstreamModelId,
|
|
1844
|
+
wireApi: resolved.model.api,
|
|
1845
|
+
credentialSource: resolved.model.credentialSource,
|
|
1846
|
+
billing: resolved.model.billing,
|
|
1847
|
+
definitionVersion: resolved.model.definitionVersion
|
|
1848
|
+
});
|
|
1849
|
+
}
|
|
1850
|
+
function assertTurnExecutionPolicyMatchesConfigV1(settings, policy, expected) {
|
|
1851
|
+
const parsed = TurnExecutionPolicyV1.parse(policy);
|
|
1852
|
+
const catalogSettings = settingsForTurnExecutionPolicy(settings, parsed.productModelId);
|
|
1853
|
+
const canonicalExpectedModel = canonicalizeConfiguredModelId(catalogSettings, expected.modelId);
|
|
1854
|
+
if (parsed.productModelId !== canonicalExpectedModel || parsed.reasoningEffort !== expected.reasoningEffort) {
|
|
1855
|
+
throw new Error("Turn execution policy does not match the accepted turn model/reasoning");
|
|
1856
|
+
}
|
|
1857
|
+
if (parsed.requestedModelId !== null && canonicalizeConfiguredModelId(catalogSettings, parsed.requestedModelId) !== parsed.productModelId) {
|
|
1858
|
+
throw new Error("Turn execution policy requested model does not match its product model");
|
|
1859
|
+
}
|
|
1860
|
+
const resolved = resolveModelProvider(catalogSettings, parsed.productModelId);
|
|
1861
|
+
if (!resolved) {
|
|
1862
|
+
throw new Error("Turn execution policy model is no longer configured");
|
|
1863
|
+
}
|
|
1864
|
+
const mismatched = parsed.providerId !== resolved.provider.id || parsed.upstreamModelId !== resolved.model.upstreamModelId || parsed.wireApi !== resolved.model.api || parsed.definitionVersion !== resolved.model.definitionVersion || canonicalJson(parsed.credentialSource) !== canonicalJson(resolved.model.credentialSource) || canonicalJson(parsed.billing) !== canonicalJson(resolved.model.billing);
|
|
1865
|
+
if (mismatched) {
|
|
1866
|
+
throw new Error("Turn execution policy does not match the current provider definition");
|
|
1867
|
+
}
|
|
1868
|
+
return { policy: parsed, provider: resolved.provider, model: resolved.model };
|
|
1869
|
+
}
|
|
1870
|
+
function configuredModelPricingSchedules(settings) {
|
|
1871
|
+
const defaults = Object.fromEntries(
|
|
1872
|
+
Object.entries(defaultModelPricing).map(([model, pricing]) => [model, { default: pricing }])
|
|
1873
|
+
);
|
|
1249
1874
|
const registry = {};
|
|
1250
1875
|
for (const provider of parseModelProvidersJson(settings.modelProvidersJson)) {
|
|
1251
1876
|
for (const model of provider.models) {
|
|
1252
1877
|
if (model.pricing) {
|
|
1253
|
-
registry[model.id] = model.pricing;
|
|
1878
|
+
registry[model.id] = normalizeModelPricingSchedule(model.pricing);
|
|
1254
1879
|
}
|
|
1255
1880
|
}
|
|
1256
1881
|
}
|
|
1257
|
-
const configured =
|
|
1882
|
+
const configured = Object.fromEntries(
|
|
1883
|
+
Object.entries(parseModelPricingJson(settings.modelPricingJson)).map(([model, pricing]) => [
|
|
1884
|
+
model,
|
|
1885
|
+
{ default: pricing }
|
|
1886
|
+
])
|
|
1887
|
+
);
|
|
1258
1888
|
return {
|
|
1259
|
-
...
|
|
1889
|
+
...defaults,
|
|
1260
1890
|
...registry,
|
|
1261
1891
|
...configured
|
|
1262
1892
|
};
|
|
1263
1893
|
}
|
|
1894
|
+
function configuredModelPricing(settings) {
|
|
1895
|
+
return Object.fromEntries(
|
|
1896
|
+
Object.entries(configuredModelPricingSchedules(settings)).map(([model, schedule]) => [
|
|
1897
|
+
model,
|
|
1898
|
+
schedule.default
|
|
1899
|
+
])
|
|
1900
|
+
);
|
|
1901
|
+
}
|
|
1902
|
+
function selectModelPricing(schedule, inputTokens) {
|
|
1903
|
+
const normalizedInputTokens = Math.max(0, Math.floor(inputTokens));
|
|
1904
|
+
let selected = schedule.default;
|
|
1905
|
+
for (const tier of schedule.inputTokenTiers ?? []) {
|
|
1906
|
+
if (normalizedInputTokens < tier.minimumInputTokens) {
|
|
1907
|
+
break;
|
|
1908
|
+
}
|
|
1909
|
+
selected = tier.pricing;
|
|
1910
|
+
}
|
|
1911
|
+
return selected;
|
|
1912
|
+
}
|
|
1264
1913
|
function contextInputBudgetTokens(settings) {
|
|
1265
1914
|
if (settings.contextEffectiveWindowTokens !== void 0) {
|
|
1266
1915
|
return Math.min(settings.contextWindowTokens, settings.contextEffectiveWindowTokens);
|
|
@@ -1303,14 +1952,25 @@ function configuredEntitlements(settings) {
|
|
|
1303
1952
|
};
|
|
1304
1953
|
}
|
|
1305
1954
|
function calculateModelUsageCostMicros(settings, model, usage) {
|
|
1306
|
-
const
|
|
1307
|
-
if (!
|
|
1955
|
+
const schedule = configuredModelPricingSchedules(settings)[model];
|
|
1956
|
+
if (!schedule) {
|
|
1308
1957
|
throw new Error(`Missing model pricing for ${model}`);
|
|
1309
1958
|
}
|
|
1310
1959
|
const entries = usage.requestUsageEntries && usage.requestUsageEntries.length > 0 ? usage.requestUsageEntries : [usage];
|
|
1311
|
-
const
|
|
1312
|
-
const
|
|
1313
|
-
|
|
1960
|
+
const rawCostByPricing = /* @__PURE__ */ new Map();
|
|
1961
|
+
for (const entry of entries) {
|
|
1962
|
+
const pricing = selectModelPricing(schedule, positiveInt(entry.inputTokens));
|
|
1963
|
+
rawCostByPricing.set(
|
|
1964
|
+
pricing,
|
|
1965
|
+
(rawCostByPricing.get(pricing) ?? 0) + calculateEntryCostMicros(pricing, entry)
|
|
1966
|
+
);
|
|
1967
|
+
}
|
|
1968
|
+
let total = 0;
|
|
1969
|
+
for (const [pricing, rawCost] of rawCostByPricing) {
|
|
1970
|
+
const marginBps = pricing.marginBps ?? 0;
|
|
1971
|
+
total += Math.ceil(rawCost * (1e4 + marginBps) / 1e4);
|
|
1972
|
+
}
|
|
1973
|
+
return total;
|
|
1314
1974
|
}
|
|
1315
1975
|
function configuredAllowedReasoningEfforts(settings) {
|
|
1316
1976
|
return uniqueValues([
|
|
@@ -1634,7 +2294,14 @@ function parseModelProvidersJson(raw) {
|
|
|
1634
2294
|
`OPENGENI_MODEL_PROVIDERS_JSON provider[${index}] is invalid: ${result.error.message}`
|
|
1635
2295
|
);
|
|
1636
2296
|
}
|
|
1637
|
-
|
|
2297
|
+
try {
|
|
2298
|
+
return normalizeRegistryProvider(result.data);
|
|
2299
|
+
} catch (error) {
|
|
2300
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2301
|
+
throw new Error(`OPENGENI_MODEL_PROVIDERS_JSON provider[${index}] is invalid: ${message}`, {
|
|
2302
|
+
cause: error
|
|
2303
|
+
});
|
|
2304
|
+
}
|
|
1638
2305
|
});
|
|
1639
2306
|
}
|
|
1640
2307
|
function parseIntegrationsOauthClientsJson(raw) {
|
|
@@ -2040,6 +2707,7 @@ function validateSettings(settings) {
|
|
|
2040
2707
|
);
|
|
2041
2708
|
}
|
|
2042
2709
|
}
|
|
2710
|
+
configuredModels(settings);
|
|
2043
2711
|
}
|
|
2044
2712
|
function resolveStreamTokenSecret(settings) {
|
|
2045
2713
|
const explicit = settings.streamTokenSecret?.trim();
|
|
@@ -2125,21 +2793,27 @@ function delay(ms) {
|
|
|
2125
2793
|
}
|
|
2126
2794
|
export {
|
|
2127
2795
|
AGENT_INSTRUCTIONS_CORE_PLACEHOLDER,
|
|
2796
|
+
CapabilityStateV1Schema,
|
|
2797
|
+
CapabilitySupportV1,
|
|
2128
2798
|
DEFAULT_AGENT_INSTRUCTIONS,
|
|
2129
2799
|
IntegrationOAuthClientConfigSchema,
|
|
2130
2800
|
McpServerConnectionRefSchema,
|
|
2801
|
+
ModelCapabilitiesV1Schema,
|
|
2131
2802
|
ModelProviderApi,
|
|
2132
2803
|
RegistryProviderKind,
|
|
2133
2804
|
SANDBOX_REQUIRED_ENV,
|
|
2134
2805
|
applyGitAuthPointerEnvironment,
|
|
2806
|
+
assertTurnExecutionPolicyMatchesConfigV1,
|
|
2135
2807
|
builtinProviderId,
|
|
2136
2808
|
calculateModelUsageCostMicros,
|
|
2809
|
+
canonicalizeConfiguredModelId,
|
|
2137
2810
|
collectGitIdentityEnvironment,
|
|
2138
2811
|
collectSandboxEnvironment,
|
|
2139
2812
|
configuredAllowedModels,
|
|
2140
2813
|
configuredAllowedReasoningEfforts,
|
|
2141
2814
|
configuredEntitlements,
|
|
2142
2815
|
configuredModelPricing,
|
|
2816
|
+
configuredModelPricingSchedules,
|
|
2143
2817
|
configuredModels,
|
|
2144
2818
|
configuredProviders,
|
|
2145
2819
|
configuredStaticUsageLimits,
|
|
@@ -2170,15 +2844,18 @@ export {
|
|
|
2170
2844
|
resolveProviderApiKey,
|
|
2171
2845
|
resolveRelayTokenSecret,
|
|
2172
2846
|
resolveStreamTokenSecret,
|
|
2847
|
+
resolveTurnExecutionPolicyV1,
|
|
2173
2848
|
retryStartupDependency,
|
|
2174
2849
|
sandboxEnvironmentVariableNames,
|
|
2175
2850
|
sandboxLifecycleHookIds,
|
|
2176
2851
|
sandboxPreparationProfiles,
|
|
2177
2852
|
sandboxWarmRateMicrosPerSecond,
|
|
2853
|
+
selectModelPricing,
|
|
2178
2854
|
settingsWithResolvedModelContext,
|
|
2179
2855
|
stableSandboxEnvironmentForRun,
|
|
2180
2856
|
startupRetryOptions,
|
|
2181
2857
|
streamTokenDegraded,
|
|
2182
|
-
temporalConnectionOptions
|
|
2858
|
+
temporalConnectionOptions,
|
|
2859
|
+
withCodexCatalogProvider
|
|
2183
2860
|
};
|
|
2184
2861
|
//# sourceMappingURL=index.js.map
|