@opengeni/config 0.7.13 → 0.8.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 +277 -174
- package/dist/index.js +424 -121
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
- package/src/index.ts +620 -122
package/dist/index.js
CHANGED
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
CAPABILITY_DESCRIPTORS,
|
|
5
5
|
Entitlements,
|
|
6
6
|
EntitlementsMode,
|
|
7
|
+
LatencyMode,
|
|
7
8
|
MAX_NESTED_AGENT_DEPTH,
|
|
8
9
|
ProductAccessMode,
|
|
9
10
|
ReasoningEffort,
|
|
@@ -175,6 +176,9 @@ var SettingsSchema = z.object({
|
|
|
175
176
|
observabilityOtlpEndpoint: z.string().url().optional(),
|
|
176
177
|
observabilityOtlpHeaders: z.string().default(""),
|
|
177
178
|
publicBaseUrl: z.string().url().optional(),
|
|
179
|
+
// Browser origin when the web app and API use separate origins in local
|
|
180
|
+
// development. Production normally leaves this unset and uses publicBaseUrl.
|
|
181
|
+
webBaseUrl: z.string().url().optional(),
|
|
178
182
|
// Base URL for the bring-your-own-compute agent release assets the get.<domain>
|
|
179
183
|
// install routes redirect to. Defaults to this repo's GitHub Releases. The route
|
|
180
184
|
// appends `/download/agent-v<ver>/<asset>`.
|
|
@@ -213,6 +217,9 @@ var SettingsSchema = z.object({
|
|
|
213
217
|
integrationsOauthClientsJson: z.string().default("{}"),
|
|
214
218
|
slackClientId: z.string().optional(),
|
|
215
219
|
slackClientSecret: z.string().optional(),
|
|
220
|
+
slackSigningSecret: z.string().optional(),
|
|
221
|
+
googleDriveClientId: z.string().optional(),
|
|
222
|
+
googleDriveClientSecret: z.string().optional(),
|
|
216
223
|
// Undefined is meaningful: the migration boundary persists the product
|
|
217
224
|
// default of 3 when no deployment override is supplied.
|
|
218
225
|
maxNestedAgentDepth: z.coerce.number().int().nonnegative().max(MAX_NESTED_AGENT_DEPTH).optional(),
|
|
@@ -267,12 +274,45 @@ var SettingsSchema = z.object({
|
|
|
267
274
|
apiPort: z.coerce.number().int().positive().default(8e3),
|
|
268
275
|
workerHttpPort: z.coerce.number().int().positive().default(8001),
|
|
269
276
|
opengeniMcpUrl: z.string().url().optional(),
|
|
277
|
+
// Origins allowed to send browser cookies cross-origin. Other origins may
|
|
278
|
+
// call the public API with bearer credentials, but never receive credentialed
|
|
279
|
+
// CORS responses.
|
|
270
280
|
corsAllowOriginRegex: z.string().default(String.raw`^https?://(localhost|127\.0\.0\.1)(:\d+)?$`),
|
|
271
281
|
openaiProvider: z.enum(["openai", "azure"]).default("openai"),
|
|
272
282
|
openaiApiKey: z.string().optional(),
|
|
273
283
|
openaiBaseUrl: z.string().optional(),
|
|
274
284
|
openaiModel: z.string().default("gpt-5.6-sol"),
|
|
275
285
|
openaiAllowedModels: z.string().default("gpt-5.6-sol,gpt-5.6-terra,gpt-5.6-luna"),
|
|
286
|
+
// Native composer voice input (browser MediaRecorder → API transcription).
|
|
287
|
+
// Provider credentials stay server-side; ClientConfig only projects availability
|
|
288
|
+
// and hard ceilings. Selection happens once before audio is sent — never retry
|
|
289
|
+
// the same clip across vendors after an upstream request may have started.
|
|
290
|
+
voiceInputMaxDurationSeconds: z.coerce.number().int().positive().max(600).default(60),
|
|
291
|
+
voiceInputMaxSizeBytes: z.coerce.number().int().positive().max(100 * 1024 * 1024).default(25 * 1024 * 1024),
|
|
292
|
+
// Preferred provider order (comma-separated ids). First configured+ready wins.
|
|
293
|
+
// Codex subscription STT is preferred by default when subscription routing is
|
|
294
|
+
// enabled; operators can put openai/azure-openai first explicitly.
|
|
295
|
+
// Supported: openai, azure-openai, codex-subscription.
|
|
296
|
+
voiceInputProviderOrder: z.string().default("codex-subscription,openai,azure-openai"),
|
|
297
|
+
// OpenAI public /v1/audio/transcriptions path. Reuses OPENGENI_OPENAI_API_KEY
|
|
298
|
+
// when voiceInputOpenaiApiKey is unset. Default model is gpt-transcribe.
|
|
299
|
+
voiceInputOpenaiEnabled: EnvBoolean.default(true),
|
|
300
|
+
voiceInputOpenaiApiKey: z.string().optional(),
|
|
301
|
+
voiceInputOpenaiBaseUrl: z.string().optional(),
|
|
302
|
+
voiceInputOpenaiModel: z.string().default("gpt-transcribe"),
|
|
303
|
+
// Azure OpenAI deployment-scoped audio transcriptions. Reuses the turn-model
|
|
304
|
+
// Azure endpoint/key/AD token when voice-specific overrides are unset.
|
|
305
|
+
voiceInputAzureEnabled: EnvBoolean.default(true),
|
|
306
|
+
voiceInputAzureEndpoint: z.string().optional(),
|
|
307
|
+
voiceInputAzureDeployment: z.string().optional(),
|
|
308
|
+
voiceInputAzureApiVersion: z.string().optional(),
|
|
309
|
+
voiceInputAzureApiKey: z.string().optional(),
|
|
310
|
+
voiceInputAzureAdToken: z.string().optional(),
|
|
311
|
+
// Legacy opt-in for undocumented ChatGPT /backend-api/transcribe. When
|
|
312
|
+
// OPENGENI_CODEX_SUBSCRIPTION_ENABLED is true, Codex STT is included without
|
|
313
|
+
// this flag. Set false and omit codex-subscription from PROVIDER_ORDER to
|
|
314
|
+
// keep subscription model routing while disabling Codex voice input.
|
|
315
|
+
voiceInputCodexExperimentalEnabled: EnvBoolean.default(false),
|
|
276
316
|
modelPricingJson: z.string().default("{}"),
|
|
277
317
|
// Extra (non-built-in) model providers, declared by the host as a JSON
|
|
278
318
|
// provider registry. Each entry carries its own base URL, API key, wire API
|
|
@@ -375,17 +415,17 @@ var SettingsSchema = z.object({
|
|
|
375
415
|
// the named Secret and builds the image via `fromRegistry(tag, secret)` before the
|
|
376
416
|
// first sandbox is created. Knob: OPENGENI_MODAL_IMAGE_REGISTRY_SECRET.
|
|
377
417
|
modalImageRegistrySecret: z.string().optional(),
|
|
378
|
-
// Modal's hard sandbox lifetime (timeoutMs = this * 1000), counted from
|
|
379
|
-
//
|
|
380
|
-
// down, NOT the warm-window
|
|
381
|
-
//
|
|
382
|
-
//
|
|
383
|
-
//
|
|
384
|
-
//
|
|
385
|
-
//
|
|
386
|
-
//
|
|
418
|
+
// Modal's hard sandbox lifetime (timeoutMs = this * 1000), counted from box
|
|
419
|
+
// creation. A resume-by-id does NOT reset that provider clock. It is the
|
|
420
|
+
// BACKSTOP that reclaims a box if the reaper/worker is down, NOT the warm-window
|
|
421
|
+
// controller (that's sandboxIdleGraceMs). It must comfortably exceed
|
|
422
|
+
// reaperPeriod + idleGrace so the reaper terminates a genuinely-idle box FIRST;
|
|
423
|
+
// the boot invariant below enforces that. Default 24h, Modal's documented
|
|
424
|
+
// maximum, to reduce premature active-box loss and leave headroom for the
|
|
425
|
+
// deadline-aware snapshot/rematerialization transition. The transition—not a
|
|
426
|
+
// larger timeout—is what lets a session outlive one finite provider box.
|
|
387
427
|
// Knob: OPENGENI_MODAL_TIMEOUT_SECONDS.
|
|
388
|
-
modalTimeoutSeconds: z.coerce.number().int().positive().default(
|
|
428
|
+
modalTimeoutSeconds: z.coerce.number().int().positive().max(86400).default(86400),
|
|
389
429
|
modalTokenId: z.string().optional(),
|
|
390
430
|
modalTokenSecret: z.string().optional(),
|
|
391
431
|
modalEnvironment: z.string().optional(),
|
|
@@ -416,13 +456,6 @@ var SettingsSchema = z.object({
|
|
|
416
456
|
// OPENGENI_MODAL_WORKSPACE_PERSISTENCE=tar to opt back out (no native snapshot;
|
|
417
457
|
// the reaper persists a tar archive — same store+hydrate plumbing, slower).
|
|
418
458
|
modalWorkspacePersistence: z.enum(["tar", "snapshot_filesystem", "snapshot_directory"]).default("snapshot_filesystem"),
|
|
419
|
-
// Snapshot GC backstop (sandbox-file-persistence): the reaper keeps ONE latest
|
|
420
|
-
// filesystem snapshot per lease (delete-prior-on-supersede + delete-on-teardown).
|
|
421
|
-
// This is the TTL retention floor for the periodic orphan sweep — a snapshot
|
|
422
|
-
// whose lease is cold and older than this is best-effort deleted so a crashed
|
|
423
|
-
// persist-then-no-restore never leaks a Modal image. 0 disables the TTL sweep
|
|
424
|
-
// (delete-on-supersede/teardown still run). Default 7 days.
|
|
425
|
-
modalSnapshotRetentionSeconds: z.coerce.number().int().nonnegative().default(604800),
|
|
426
459
|
// Shared desktop toggle: this module reads it for the 6080 port-merge; the
|
|
427
460
|
// owner module (P4.x) acts on it to launch the display stack.
|
|
428
461
|
sandboxDesktopEnabled: EnvBoolean.default(false),
|
|
@@ -644,8 +677,9 @@ var SettingsSchema = z.object({
|
|
|
644
677
|
// this whole window so a "glanced away then came back" re-arms the SAME warm box
|
|
645
678
|
// (acquireLease re-arms draining->warm; the reaper's BEFORE-terminate re-read
|
|
646
679
|
// skips a re-armed box). Default 15min so a brief detour never cold-creates a
|
|
647
|
-
// fresh EMPTY box; lower it to trade warm cost for a snappier reclaim.
|
|
648
|
-
//
|
|
680
|
+
// fresh EMPTY box; lower it to trade warm cost for a snappier reclaim.
|
|
681
|
+
// getSettings caps the default at half a shorter configured Modal lifetime so
|
|
682
|
+
// the entire reaper window always fits. Knob: OPENGENI_SANDBOX_IDLE_GRACE_MS.
|
|
649
683
|
sandboxIdleGraceMs: z.coerce.number().int().positive().default(9e5),
|
|
650
684
|
// MID-SESSION /workspace snapshot cadence (sandbox-file-persistence). The
|
|
651
685
|
// reaper's drain-persist only protects boxes the reaper itself kills; a box
|
|
@@ -663,6 +697,22 @@ var SettingsSchema = z.object({
|
|
|
663
697
|
// treated exactly like a failed best-effort snapshot. Knob:
|
|
664
698
|
// OPENGENI_SANDBOX_SNAPSHOT_TIMEOUT_MS. Default 60s.
|
|
665
699
|
sandboxSnapshotTimeoutMs: z.coerce.number().int().positive().default(6e4),
|
|
700
|
+
// Begin a controlled snapshot/quiesce/drain/rematerialize transition this far
|
|
701
|
+
// ahead of a finite provider deadline. Modal's 24h creation clock cannot be
|
|
702
|
+
// extended; the logical sandbox outlives it by moving to one successor box.
|
|
703
|
+
// getSettings derives the actual default as min(1h, half the configured
|
|
704
|
+
// provider lifetime) so short-lived test/canary boxes remain bootable without
|
|
705
|
+
// an extra coupled environment override. An explicit value may be larger when
|
|
706
|
+
// an operator deliberately wants more rotation headroom; the boot invariant
|
|
707
|
+
// still requires it to remain below the provider lifetime.
|
|
708
|
+
sandboxRotationLeadMs: z.coerce.number().int().positive().default(36e5),
|
|
709
|
+
// Bound each global reaper pass so a rollout that discovers many legacy boxes
|
|
710
|
+
// with unknown creation clocks cannot create a provider/API thundering herd.
|
|
711
|
+
// One is the safe admission default: the reaper services provider transitions
|
|
712
|
+
// sequentially, so claiming a wider batch would fence boxes before the same
|
|
713
|
+
// sweep can service them. Larger fleets may raise this only as an explicit,
|
|
714
|
+
// observed deployment choice.
|
|
715
|
+
sandboxRotationBatchSize: z.coerce.number().int().positive().max(500).default(1),
|
|
666
716
|
// expires_at refresh window for a held lease (>> the turn 10s heartbeat so a
|
|
667
717
|
// single missed heartbeat never TTL-reaps a live turn). The warming TTL is the
|
|
668
718
|
// window a cold->warming spawner has to commit warm before a reaper resets it.
|
|
@@ -691,6 +741,7 @@ var SettingsSchema = z.object({
|
|
|
691
741
|
sandboxPreparationProfiles: z.string().default("none"),
|
|
692
742
|
sandboxEnvAllowlist: z.string().default(""),
|
|
693
743
|
objectStorageEndpoint: z.string().url().optional(),
|
|
744
|
+
objectStorageInternalEndpoint: z.string().url().optional(),
|
|
694
745
|
objectStorageSandboxEndpoint: z.string().url().optional(),
|
|
695
746
|
objectStorageBackend: z.enum(["s3-compatible", "aws-s3", "azure-blob", "gcs"]).default("s3-compatible"),
|
|
696
747
|
objectStorageBucket: z.string().min(1).default("opengeni-files"),
|
|
@@ -762,6 +813,81 @@ var SettingsSchema = z.object({
|
|
|
762
813
|
})
|
|
763
814
|
).default([])
|
|
764
815
|
});
|
|
816
|
+
function isUsableVoiceInputSecret(value) {
|
|
817
|
+
if (value == null) return false;
|
|
818
|
+
const trimmed = value.trim();
|
|
819
|
+
if (!trimmed) return false;
|
|
820
|
+
const normalized = trimmed.toLowerCase();
|
|
821
|
+
if (normalized === "your-key" || normalized === "your_key" || normalized === "changeme" || normalized === "replace-me" || normalized === "xxx" || normalized.startsWith("your-") || normalized.startsWith("your_")) {
|
|
822
|
+
return false;
|
|
823
|
+
}
|
|
824
|
+
return true;
|
|
825
|
+
}
|
|
826
|
+
function resolveVoiceInputProviderRegistry(settings) {
|
|
827
|
+
const order = settings.voiceInputProviderOrder.split(",").map((part) => part.trim()).filter(
|
|
828
|
+
(part) => part === "openai" || part === "azure-openai" || part === "codex-subscription"
|
|
829
|
+
);
|
|
830
|
+
const seen = /* @__PURE__ */ new Set();
|
|
831
|
+
const providers = [];
|
|
832
|
+
for (const id of order) {
|
|
833
|
+
if (seen.has(id)) continue;
|
|
834
|
+
seen.add(id);
|
|
835
|
+
if (id === "openai") {
|
|
836
|
+
if (!settings.voiceInputOpenaiEnabled) continue;
|
|
837
|
+
const apiKey = settings.voiceInputOpenaiApiKey ?? settings.openaiApiKey;
|
|
838
|
+
if (!isUsableVoiceInputSecret(apiKey)) continue;
|
|
839
|
+
if (settings.openaiProvider === "azure" && !settings.voiceInputOpenaiApiKey && !settings.voiceInputOpenaiBaseUrl) {
|
|
840
|
+
continue;
|
|
841
|
+
}
|
|
842
|
+
providers.push({
|
|
843
|
+
id: "openai",
|
|
844
|
+
kind: "openai",
|
|
845
|
+
apiKey,
|
|
846
|
+
baseUrl: (settings.voiceInputOpenaiBaseUrl ?? settings.openaiBaseUrl ?? "https://api.openai.com/v1").replace(/\/+$/, ""),
|
|
847
|
+
model: settings.voiceInputOpenaiModel
|
|
848
|
+
});
|
|
849
|
+
continue;
|
|
850
|
+
}
|
|
851
|
+
if (id === "azure-openai") {
|
|
852
|
+
if (!settings.voiceInputAzureEnabled) continue;
|
|
853
|
+
const endpoint = (settings.voiceInputAzureEndpoint ?? settings.azureOpenaiEndpoint ?? "").replace(/\/+$/, "");
|
|
854
|
+
const deployment = settings.voiceInputAzureDeployment ?? settings.azureOpenaiDeployment ?? "";
|
|
855
|
+
const apiVersion = settings.voiceInputAzureApiVersion ?? settings.azureOpenaiApiVersion ?? "2025-04-01-preview";
|
|
856
|
+
const apiKey = settings.voiceInputAzureApiKey ?? settings.azureOpenaiApiKey ?? null;
|
|
857
|
+
const adToken = settings.voiceInputAzureAdToken ?? settings.azureOpenaiAdToken ?? null;
|
|
858
|
+
if (!endpoint || !deployment || !isUsableVoiceInputSecret(apiKey) && !isUsableVoiceInputSecret(adToken)) {
|
|
859
|
+
continue;
|
|
860
|
+
}
|
|
861
|
+
if (settings.openaiProvider !== "azure" && !settings.voiceInputAzureEndpoint && !settings.voiceInputAzureDeployment && !settings.voiceInputAzureApiKey && !settings.voiceInputAzureAdToken) {
|
|
862
|
+
continue;
|
|
863
|
+
}
|
|
864
|
+
providers.push({
|
|
865
|
+
id: "azure-openai",
|
|
866
|
+
kind: "azure-openai",
|
|
867
|
+
endpoint,
|
|
868
|
+
deployment,
|
|
869
|
+
apiVersion,
|
|
870
|
+
apiKey: isUsableVoiceInputSecret(apiKey) ? apiKey : null,
|
|
871
|
+
adToken: isUsableVoiceInputSecret(adToken) ? adToken : null
|
|
872
|
+
});
|
|
873
|
+
continue;
|
|
874
|
+
}
|
|
875
|
+
if (id === "codex-subscription") {
|
|
876
|
+
if (!settings.codexSubscriptionEnabled) continue;
|
|
877
|
+
providers.push({
|
|
878
|
+
id: "codex-subscription",
|
|
879
|
+
kind: "codex-subscription",
|
|
880
|
+
experimental: true
|
|
881
|
+
});
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
return providers;
|
|
885
|
+
}
|
|
886
|
+
function voiceInputDeploymentConfigured(settings) {
|
|
887
|
+
return resolveVoiceInputProviderRegistry(settings).some(
|
|
888
|
+
(provider) => provider.kind !== "codex-subscription"
|
|
889
|
+
);
|
|
890
|
+
}
|
|
765
891
|
var ModelPricingSchema = z.object({
|
|
766
892
|
inputMicrosPerMillionTokens: z.number().int().nonnegative(),
|
|
767
893
|
cachedInputMicrosPerMillionTokens: z.number().int().nonnegative().optional(),
|
|
@@ -955,86 +1081,74 @@ var IntegrationOAuthClientConfigSchema = z.object({
|
|
|
955
1081
|
});
|
|
956
1082
|
var defaultModelPricing = {
|
|
957
1083
|
"gpt-5.6-sol": {
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
1084
|
+
default: {
|
|
1085
|
+
inputMicrosPerMillionTokens: 5e6,
|
|
1086
|
+
cachedInputMicrosPerMillionTokens: 5e5,
|
|
1087
|
+
outputMicrosPerMillionTokens: 3e7,
|
|
1088
|
+
marginBps: 2500
|
|
1089
|
+
},
|
|
1090
|
+
inputTokenTiers: [
|
|
1091
|
+
{
|
|
1092
|
+
// OpenAI: prompts with >272K input tokens use the long-context rate.
|
|
1093
|
+
minimumInputTokens: 272001,
|
|
1094
|
+
pricing: {
|
|
1095
|
+
inputMicrosPerMillionTokens: 1e7,
|
|
1096
|
+
cachedInputMicrosPerMillionTokens: 1e6,
|
|
1097
|
+
outputMicrosPerMillionTokens: 45e6,
|
|
1098
|
+
marginBps: 2500
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
]
|
|
962
1102
|
},
|
|
963
1103
|
"gpt-5.6-terra": {
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
1104
|
+
default: {
|
|
1105
|
+
inputMicrosPerMillionTokens: 2e6,
|
|
1106
|
+
cachedInputMicrosPerMillionTokens: 2e5,
|
|
1107
|
+
outputMicrosPerMillionTokens: 12e6,
|
|
1108
|
+
marginBps: 2500
|
|
1109
|
+
},
|
|
1110
|
+
inputTokenTiers: [
|
|
1111
|
+
{
|
|
1112
|
+
minimumInputTokens: 272001,
|
|
1113
|
+
pricing: {
|
|
1114
|
+
inputMicrosPerMillionTokens: 4e6,
|
|
1115
|
+
cachedInputMicrosPerMillionTokens: 4e5,
|
|
1116
|
+
outputMicrosPerMillionTokens: 18e6,
|
|
1117
|
+
marginBps: 2500
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1120
|
+
]
|
|
968
1121
|
},
|
|
969
1122
|
"gpt-5.6-luna": {
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
"gpt-5.2": {
|
|
988
|
-
inputMicrosPerMillionTokens: 175e4,
|
|
989
|
-
cachedInputMicrosPerMillionTokens: 175e3,
|
|
990
|
-
outputMicrosPerMillionTokens: 14e6,
|
|
991
|
-
marginBps: 2500
|
|
992
|
-
},
|
|
993
|
-
"gpt-5.2-chat-latest": {
|
|
994
|
-
inputMicrosPerMillionTokens: 175e4,
|
|
995
|
-
cachedInputMicrosPerMillionTokens: 175e3,
|
|
996
|
-
outputMicrosPerMillionTokens: 14e6,
|
|
997
|
-
marginBps: 2500
|
|
998
|
-
},
|
|
999
|
-
"gpt-5.2-codex": {
|
|
1000
|
-
inputMicrosPerMillionTokens: 175e4,
|
|
1001
|
-
cachedInputMicrosPerMillionTokens: 175e3,
|
|
1002
|
-
outputMicrosPerMillionTokens: 14e6,
|
|
1003
|
-
marginBps: 2500
|
|
1004
|
-
},
|
|
1005
|
-
"gpt-5.1": {
|
|
1006
|
-
inputMicrosPerMillionTokens: 125e4,
|
|
1007
|
-
cachedInputMicrosPerMillionTokens: 125e3,
|
|
1008
|
-
outputMicrosPerMillionTokens: 1e7,
|
|
1009
|
-
marginBps: 2500
|
|
1010
|
-
},
|
|
1011
|
-
"gpt-5": {
|
|
1012
|
-
inputMicrosPerMillionTokens: 125e4,
|
|
1013
|
-
cachedInputMicrosPerMillionTokens: 125e3,
|
|
1014
|
-
outputMicrosPerMillionTokens: 1e7,
|
|
1015
|
-
marginBps: 2500
|
|
1016
|
-
},
|
|
1017
|
-
"gpt-5-mini": {
|
|
1018
|
-
inputMicrosPerMillionTokens: 25e4,
|
|
1019
|
-
cachedInputMicrosPerMillionTokens: 25e3,
|
|
1020
|
-
outputMicrosPerMillionTokens: 2e6,
|
|
1021
|
-
marginBps: 2500
|
|
1022
|
-
},
|
|
1023
|
-
"gpt-5-nano": {
|
|
1024
|
-
inputMicrosPerMillionTokens: 5e4,
|
|
1025
|
-
cachedInputMicrosPerMillionTokens: 5e3,
|
|
1026
|
-
outputMicrosPerMillionTokens: 4e5,
|
|
1027
|
-
marginBps: 2500
|
|
1123
|
+
default: {
|
|
1124
|
+
inputMicrosPerMillionTokens: 2e5,
|
|
1125
|
+
cachedInputMicrosPerMillionTokens: 2e4,
|
|
1126
|
+
outputMicrosPerMillionTokens: 12e5,
|
|
1127
|
+
marginBps: 2500
|
|
1128
|
+
},
|
|
1129
|
+
inputTokenTiers: [
|
|
1130
|
+
{
|
|
1131
|
+
minimumInputTokens: 272001,
|
|
1132
|
+
pricing: {
|
|
1133
|
+
inputMicrosPerMillionTokens: 4e5,
|
|
1134
|
+
cachedInputMicrosPerMillionTokens: 4e4,
|
|
1135
|
+
outputMicrosPerMillionTokens: 18e5,
|
|
1136
|
+
marginBps: 2500
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
]
|
|
1028
1140
|
},
|
|
1029
1141
|
// Fireworks AI / GLM 5.2 — the first shipped non-OpenAI registry model. A
|
|
1030
1142
|
// built-in default pricing entry makes managed billing work out of the box
|
|
1031
1143
|
// for hosts that expose this model via OPENGENI_MODEL_PROVIDERS_JSON without
|
|
1032
1144
|
// also setting OPENGENI_MODEL_PRICING_JSON.
|
|
1033
1145
|
"accounts/fireworks/models/glm-5p2": {
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1146
|
+
default: {
|
|
1147
|
+
inputMicrosPerMillionTokens: 14e5,
|
|
1148
|
+
cachedInputMicrosPerMillionTokens: 14e4,
|
|
1149
|
+
outputMicrosPerMillionTokens: 44e5,
|
|
1150
|
+
marginBps: 2500
|
|
1151
|
+
}
|
|
1038
1152
|
}
|
|
1039
1153
|
};
|
|
1040
1154
|
var SANDBOX_REQUIRED_ENV = {
|
|
@@ -1105,6 +1219,7 @@ function getSettings() {
|
|
|
1105
1219
|
observabilityOtlpEndpoint: optional("OPENGENI_OTEL_EXPORTER_OTLP_ENDPOINT") ?? optional("OTEL_EXPORTER_OTLP_ENDPOINT"),
|
|
1106
1220
|
observabilityOtlpHeaders: optional("OPENGENI_OTEL_EXPORTER_OTLP_HEADERS") ?? optional("OTEL_EXPORTER_OTLP_HEADERS"),
|
|
1107
1221
|
publicBaseUrl: optional("OPENGENI_PUBLIC_BASE_URL"),
|
|
1222
|
+
webBaseUrl: optional("OPENGENI_WEB_BASE_URL"),
|
|
1108
1223
|
agentReleasesBaseUrl: optional("OPENGENI_AGENT_RELEASES_BASE_URL"),
|
|
1109
1224
|
agentStableVersion: optional("OPENGENI_AGENT_STABLE_VERSION"),
|
|
1110
1225
|
productAccessMode: optional("OPENGENI_PRODUCT_ACCESS_MODE"),
|
|
@@ -1128,6 +1243,9 @@ function getSettings() {
|
|
|
1128
1243
|
integrationsOauthClientsJson: optional("OPENGENI_INTEGRATIONS_OAUTH_CLIENTS_JSON"),
|
|
1129
1244
|
slackClientId: optional("OPENGENI_SLACK_CLIENT_ID"),
|
|
1130
1245
|
slackClientSecret: optional("OPENGENI_SLACK_CLIENT_SECRET"),
|
|
1246
|
+
slackSigningSecret: optional("OPENGENI_SLACK_SIGNING_SECRET"),
|
|
1247
|
+
googleDriveClientId: optional("OPENGENI_GOOGLE_DRIVE_CLIENT_ID"),
|
|
1248
|
+
googleDriveClientSecret: optional("OPENGENI_GOOGLE_DRIVE_CLIENT_SECRET"),
|
|
1131
1249
|
maxNestedAgentDepth: optional("OPENGENI_MAX_NESTED_AGENT_DEPTH"),
|
|
1132
1250
|
goalMaxAutoContinuations: optional("OPENGENI_GOAL_MAX_AUTO_CONTINUATIONS"),
|
|
1133
1251
|
goalNoProgressLimit: optional("OPENGENI_GOAL_NO_PROGRESS_LIMIT"),
|
|
@@ -1152,6 +1270,20 @@ function getSettings() {
|
|
|
1152
1270
|
openaiBaseUrl: optional("OPENGENI_OPENAI_BASE_URL") ?? optional("OPENAI_BASE_URL"),
|
|
1153
1271
|
openaiModel: optional("OPENGENI_OPENAI_MODEL"),
|
|
1154
1272
|
openaiAllowedModels: optional("OPENGENI_OPENAI_ALLOWED_MODELS"),
|
|
1273
|
+
voiceInputMaxDurationSeconds: optional("OPENGENI_VOICE_INPUT_MAX_DURATION_SECONDS"),
|
|
1274
|
+
voiceInputMaxSizeBytes: optional("OPENGENI_VOICE_INPUT_MAX_SIZE_BYTES"),
|
|
1275
|
+
voiceInputProviderOrder: optional("OPENGENI_VOICE_INPUT_PROVIDER_ORDER"),
|
|
1276
|
+
voiceInputOpenaiEnabled: optional("OPENGENI_VOICE_INPUT_OPENAI_ENABLED"),
|
|
1277
|
+
voiceInputOpenaiApiKey: optional("OPENGENI_VOICE_INPUT_OPENAI_API_KEY"),
|
|
1278
|
+
voiceInputOpenaiBaseUrl: optional("OPENGENI_VOICE_INPUT_OPENAI_BASE_URL"),
|
|
1279
|
+
voiceInputOpenaiModel: optional("OPENGENI_VOICE_INPUT_OPENAI_MODEL"),
|
|
1280
|
+
voiceInputAzureEnabled: optional("OPENGENI_VOICE_INPUT_AZURE_ENABLED"),
|
|
1281
|
+
voiceInputAzureEndpoint: optional("OPENGENI_VOICE_INPUT_AZURE_ENDPOINT"),
|
|
1282
|
+
voiceInputAzureDeployment: optional("OPENGENI_VOICE_INPUT_AZURE_DEPLOYMENT"),
|
|
1283
|
+
voiceInputAzureApiVersion: optional("OPENGENI_VOICE_INPUT_AZURE_API_VERSION"),
|
|
1284
|
+
voiceInputAzureApiKey: optional("OPENGENI_VOICE_INPUT_AZURE_API_KEY"),
|
|
1285
|
+
voiceInputAzureAdToken: optional("OPENGENI_VOICE_INPUT_AZURE_AD_TOKEN"),
|
|
1286
|
+
voiceInputCodexExperimentalEnabled: optional("OPENGENI_VOICE_INPUT_CODEX_EXPERIMENTAL"),
|
|
1155
1287
|
modelPricingJson: optional("OPENGENI_MODEL_PRICING_JSON"),
|
|
1156
1288
|
modelProvidersJson: optional("OPENGENI_MODEL_PROVIDERS_JSON"),
|
|
1157
1289
|
codexSubscriptionEnabled: optional("OPENGENI_CODEX_SUBSCRIPTION_ENABLED"),
|
|
@@ -1189,7 +1321,6 @@ function getSettings() {
|
|
|
1189
1321
|
modalEnvironment: optional("OPENGENI_MODAL_ENVIRONMENT"),
|
|
1190
1322
|
modalIdleTimeoutSeconds: optional("OPENGENI_MODAL_IDLE_TIMEOUT_SECONDS"),
|
|
1191
1323
|
modalWorkspacePersistence: optional("OPENGENI_MODAL_WORKSPACE_PERSISTENCE"),
|
|
1192
|
-
modalSnapshotRetentionSeconds: optional("OPENGENI_MODAL_SNAPSHOT_RETENTION_SECONDS"),
|
|
1193
1324
|
sandboxDesktopEnabled: optional("OPENGENI_SANDBOX_DESKTOP_ENABLED"),
|
|
1194
1325
|
sandboxDesktopInteractive: optional("OPENGENI_SANDBOX_DESKTOP_INTERACTIVE"),
|
|
1195
1326
|
sandboxTerminalEnabled: optional("OPENGENI_SANDBOX_TERMINAL_ENABLED"),
|
|
@@ -1262,6 +1393,8 @@ function getSettings() {
|
|
|
1262
1393
|
sandboxIdleGraceMs: optional("OPENGENI_SANDBOX_IDLE_GRACE_MS"),
|
|
1263
1394
|
sandboxSnapshotIntervalMs: optional("OPENGENI_SANDBOX_SNAPSHOT_INTERVAL_MS"),
|
|
1264
1395
|
sandboxSnapshotTimeoutMs: optional("OPENGENI_SANDBOX_SNAPSHOT_TIMEOUT_MS"),
|
|
1396
|
+
sandboxRotationLeadMs: optional("OPENGENI_SANDBOX_ROTATION_LEAD_MS"),
|
|
1397
|
+
sandboxRotationBatchSize: optional("OPENGENI_SANDBOX_ROTATION_BATCH_SIZE"),
|
|
1265
1398
|
sandboxLeaseTtlMs: optional("OPENGENI_SANDBOX_LEASE_TTL_MS"),
|
|
1266
1399
|
sandboxLeaseWarmingTtlMs: optional("OPENGENI_SANDBOX_LEASE_WARMING_TTL_MS"),
|
|
1267
1400
|
sandboxWarmingTimeoutMs: optional("OPENGENI_SANDBOX_WARMING_TIMEOUT_MS"),
|
|
@@ -1273,6 +1406,7 @@ function getSettings() {
|
|
|
1273
1406
|
sandboxPreparationProfiles: optional("OPENGENI_SANDBOX_PREPARATION_PROFILES"),
|
|
1274
1407
|
sandboxEnvAllowlist: optional("OPENGENI_SANDBOX_ENV_ALLOWLIST"),
|
|
1275
1408
|
objectStorageEndpoint: optional("OPENGENI_OBJECT_STORAGE_ENDPOINT"),
|
|
1409
|
+
objectStorageInternalEndpoint: optional("OPENGENI_OBJECT_STORAGE_INTERNAL_ENDPOINT"),
|
|
1276
1410
|
objectStorageSandboxEndpoint: optional("OPENGENI_OBJECT_STORAGE_SANDBOX_ENDPOINT"),
|
|
1277
1411
|
objectStorageBackend: optional("OPENGENI_OBJECT_STORAGE_BACKEND"),
|
|
1278
1412
|
objectStorageBucket: optional("OPENGENI_OBJECT_STORAGE_BUCKET"),
|
|
@@ -1328,14 +1462,28 @@ function getSettings() {
|
|
|
1328
1462
|
const parsed = SettingsSchema.parse(raw);
|
|
1329
1463
|
const settings = {
|
|
1330
1464
|
...parsed,
|
|
1465
|
+
sandboxIdleGraceMs: raw.sandboxIdleGraceMs === void 0 ? Math.min(9e5, Math.floor(parsed.modalTimeoutSeconds * 1e3 / 2)) : parsed.sandboxIdleGraceMs,
|
|
1466
|
+
sandboxRotationLeadMs: raw.sandboxRotationLeadMs === void 0 ? Math.min(36e5, Math.floor(parsed.modalTimeoutSeconds * 1e3 / 2)) : parsed.sandboxRotationLeadMs,
|
|
1331
1467
|
mcpServers: ensureBuiltInMcpServers(parsed)
|
|
1332
1468
|
};
|
|
1333
1469
|
validateSettings(settings);
|
|
1334
1470
|
return settings;
|
|
1335
1471
|
}
|
|
1472
|
+
var LOCAL_FIRST_PARTY_DELEGATION_SECRET = "opengeni-local-first-party-delegation-secret-v1";
|
|
1473
|
+
function resolveFirstPartyDelegationSecret(settings) {
|
|
1474
|
+
const explicit = settings.delegationSecret?.trim();
|
|
1475
|
+
if (explicit) return explicit;
|
|
1476
|
+
return settings.productAccessMode === "local" && (settings.environment === "local" || settings.environment === "test") ? LOCAL_FIRST_PARTY_DELEGATION_SECRET : void 0;
|
|
1477
|
+
}
|
|
1336
1478
|
function effectiveModalIdleTimeoutSeconds(settings) {
|
|
1337
1479
|
return settings.modalIdleTimeoutSeconds ?? settings.modalTimeoutSeconds;
|
|
1338
1480
|
}
|
|
1481
|
+
function sandboxArchiveCaptureTimeoutMs(settings) {
|
|
1482
|
+
return Math.min(
|
|
1483
|
+
60 * 6e4,
|
|
1484
|
+
Math.max(settings.sandboxSnapshotTimeoutMs + 3e4, settings.sandboxSnapshotTimeoutMs * 2)
|
|
1485
|
+
);
|
|
1486
|
+
}
|
|
1339
1487
|
function collectSandboxEnvironment(settings, source = process.env) {
|
|
1340
1488
|
const out = {};
|
|
1341
1489
|
for (const name of sandboxEnvironmentVariableNames(settings)) {
|
|
@@ -1572,6 +1720,68 @@ function legacyModelCapabilities(settings, input) {
|
|
|
1572
1720
|
latencyModes: [{ id: "standard", upstream: "unknown", runnable: true }]
|
|
1573
1721
|
});
|
|
1574
1722
|
}
|
|
1723
|
+
var GPT56_FAST_BILLING_MULTIPLIER_BPS = 2e4;
|
|
1724
|
+
function productLabelForModelId(modelId) {
|
|
1725
|
+
const slug = modelId.startsWith(CODEX_MODEL_ID_PREFIX) ? modelId.slice(CODEX_MODEL_ID_PREFIX.length) : modelId;
|
|
1726
|
+
const match = /^(gpt-\d+(?:\.\d+)?)(?:-(.+))?$/i.exec(slug);
|
|
1727
|
+
if (!match) {
|
|
1728
|
+
return slug;
|
|
1729
|
+
}
|
|
1730
|
+
const family = match[1].replace(/^gpt/i, "GPT");
|
|
1731
|
+
const rest = match[2];
|
|
1732
|
+
if (!rest) {
|
|
1733
|
+
return family;
|
|
1734
|
+
}
|
|
1735
|
+
const suffix = rest.split("-").filter((part) => part.length > 0).map((part) => part.charAt(0).toUpperCase() + part.slice(1).toLowerCase()).join(" ");
|
|
1736
|
+
return suffix.length > 0 ? `${family} ${suffix}` : family;
|
|
1737
|
+
}
|
|
1738
|
+
function builtinLatencyModesForModel(modelId) {
|
|
1739
|
+
if (modelId === "gpt-5.6-sol" || modelId === "gpt-5.6-terra" || modelId === "gpt-5.6-luna" || modelId.startsWith("codex/gpt-5.6-")) {
|
|
1740
|
+
return [
|
|
1741
|
+
{ id: "standard", upstream: "supported", runnable: true },
|
|
1742
|
+
{
|
|
1743
|
+
id: "fast",
|
|
1744
|
+
upstream: "supported",
|
|
1745
|
+
runnable: true,
|
|
1746
|
+
billingMultiplierBps: GPT56_FAST_BILLING_MULTIPLIER_BPS
|
|
1747
|
+
}
|
|
1748
|
+
];
|
|
1749
|
+
}
|
|
1750
|
+
return [{ id: "standard", upstream: "unknown", runnable: true }];
|
|
1751
|
+
}
|
|
1752
|
+
function serviceTierForLatencyMode(providerId, latencyMode) {
|
|
1753
|
+
if (latencyMode === "standard") {
|
|
1754
|
+
return void 0;
|
|
1755
|
+
}
|
|
1756
|
+
if (providerId === "azure" || providerId === CODEX_PROVIDER_ID) {
|
|
1757
|
+
return "priority";
|
|
1758
|
+
}
|
|
1759
|
+
return "fast";
|
|
1760
|
+
}
|
|
1761
|
+
function responseSatisfiesLatencyMode(requested, responseServiceTier) {
|
|
1762
|
+
if (requested === "standard") {
|
|
1763
|
+
return true;
|
|
1764
|
+
}
|
|
1765
|
+
return responseServiceTier === "priority" || responseServiceTier === "fast";
|
|
1766
|
+
}
|
|
1767
|
+
function runnableLatencyModesForModel(settings, modelId) {
|
|
1768
|
+
const resolved = resolveModelProvider(
|
|
1769
|
+
settingsForTurnExecutionPolicy(settings, modelId),
|
|
1770
|
+
canonicalizeConfiguredModelId(settings, modelId)
|
|
1771
|
+
);
|
|
1772
|
+
if (!resolved) {
|
|
1773
|
+
return ["standard"];
|
|
1774
|
+
}
|
|
1775
|
+
return resolved.model.capabilities.latencyModes.filter((mode) => mode.runnable).map((mode) => LatencyMode.parse(mode.id));
|
|
1776
|
+
}
|
|
1777
|
+
function assertLatencyModeRunnable(settings, modelId, latencyMode) {
|
|
1778
|
+
const runnable = runnableLatencyModesForModel(settings, modelId);
|
|
1779
|
+
if (!runnable.includes(latencyMode)) {
|
|
1780
|
+
throw new Error(
|
|
1781
|
+
`latency mode ${latencyMode} is not runnable for model ${modelId} (allowed: ${runnable.join(", ")})`
|
|
1782
|
+
);
|
|
1783
|
+
}
|
|
1784
|
+
}
|
|
1575
1785
|
function registryCredentialSource(provider) {
|
|
1576
1786
|
return provider.kind === "codex-subscription" ? { kind: "connected_subscription", provider: "codex" } : { kind: "deployment", mechanism: "api_key" };
|
|
1577
1787
|
}
|
|
@@ -1692,23 +1902,36 @@ function withCodexCatalogProvider(settings) {
|
|
|
1692
1902
|
label: "Codex (ChatGPT subscription)",
|
|
1693
1903
|
api: "responses",
|
|
1694
1904
|
baseUrl: CODEX_PROVIDER_BASE_URL,
|
|
1695
|
-
models: CODEX_FALLBACK_MODEL_SLUGS.map((slug) =>
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1905
|
+
models: CODEX_FALLBACK_MODEL_SLUGS.map((slug) => {
|
|
1906
|
+
const capabilities = {
|
|
1907
|
+
...legacyModelCapabilities(settings, {
|
|
1908
|
+
reasoningEffort: true,
|
|
1909
|
+
hostedWebSearch: true
|
|
1910
|
+
}),
|
|
1911
|
+
latencyModes: builtinLatencyModesForModel(`${CODEX_MODEL_ID_PREFIX}${slug}`)
|
|
1912
|
+
};
|
|
1913
|
+
return {
|
|
1914
|
+
id: `${CODEX_MODEL_ID_PREFIX}${slug}`,
|
|
1915
|
+
upstreamModelId: slug,
|
|
1916
|
+
label: productLabelForModelId(slug),
|
|
1917
|
+
reasoningEffort: true,
|
|
1918
|
+
// The ChatGPT/Codex Responses backend accepts the native web_search
|
|
1919
|
+
// hosted tool (unlike hosted apply_patch/computer transports). Declaring
|
|
1920
|
+
// this here makes provider resolution truthful; the worker still applies
|
|
1921
|
+
// the durable session/turn policy gate before attaching it.
|
|
1922
|
+
hostedWebSearch: true,
|
|
1923
|
+
capabilities,
|
|
1924
|
+
contextWindowTokens: CODEX_MODEL_CONTEXT_WINDOW_TOKENS,
|
|
1925
|
+
effectiveContextWindowTokens: CODEX_MODEL_EFFECTIVE_CONTEXT_WINDOW_TOKENS,
|
|
1926
|
+
autoCompactTokenLimit: CODEX_MODEL_AUTO_COMPACT_TOKEN_LIMIT,
|
|
1927
|
+
toolOutputTruncationTokens: CODEX_MODEL_TOOL_OUTPUT_TRUNCATION_TOKENS
|
|
1928
|
+
};
|
|
1929
|
+
})
|
|
1930
|
+
};
|
|
1931
|
+
return {
|
|
1932
|
+
...settings,
|
|
1933
|
+
modelProvidersJson: JSON.stringify([...providers, provider])
|
|
1710
1934
|
};
|
|
1711
|
-
return { ...settings, modelProvidersJson: JSON.stringify([...providers, provider]) };
|
|
1712
1935
|
}
|
|
1713
1936
|
function policyProviderIdForModel(settings, modelId) {
|
|
1714
1937
|
const canonicalModelId = canonicalizeConfiguredModelId(settings, modelId);
|
|
@@ -1790,14 +2013,17 @@ function configuredModels(settings) {
|
|
|
1790
2013
|
settings.openaiModel,
|
|
1791
2014
|
...splitCsv(settings.openaiAllowedModels)
|
|
1792
2015
|
]).filter((id) => !isRegistryNamespaced(id)).map((id) => {
|
|
1793
|
-
const capabilities =
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
2016
|
+
const capabilities = {
|
|
2017
|
+
...legacyModelCapabilities(settings, {
|
|
2018
|
+
reasoningEffort: true,
|
|
2019
|
+
hostedWebSearch: settings.webSearchEnabled
|
|
2020
|
+
}),
|
|
2021
|
+
latencyModes: builtinLatencyModesForModel(id)
|
|
2022
|
+
};
|
|
1797
2023
|
return finalizeConfiguredModel(settings, builtinProvider, {
|
|
1798
2024
|
id,
|
|
1799
2025
|
aliases: [],
|
|
1800
|
-
label: id,
|
|
2026
|
+
label: productLabelForModelId(id),
|
|
1801
2027
|
providerId: builtinId,
|
|
1802
2028
|
providerLabel: builtinLabel,
|
|
1803
2029
|
api: "responses",
|
|
@@ -1829,7 +2055,7 @@ function configuredModels(settings) {
|
|
|
1829
2055
|
finalizeConfiguredModel(settings, resolvedProvider, {
|
|
1830
2056
|
id: model.id,
|
|
1831
2057
|
aliases: [...model.aliases ?? []],
|
|
1832
|
-
label: model.label ?? model.id,
|
|
2058
|
+
label: model.label ?? productLabelForModelId(model.id),
|
|
1833
2059
|
providerId: provider.id,
|
|
1834
2060
|
providerLabel,
|
|
1835
2061
|
api: provider.api,
|
|
@@ -1840,7 +2066,9 @@ function configuredModels(settings) {
|
|
|
1840
2066
|
capabilities,
|
|
1841
2067
|
...pricingSchedules[model.id] === void 0 ? {} : { pricing: pricingSchedules[model.id] },
|
|
1842
2068
|
...model.contextWindowTokens === void 0 ? {} : { contextWindowTokens: model.contextWindowTokens },
|
|
1843
|
-
...model.effectiveContextWindowTokens === void 0 ? {} : {
|
|
2069
|
+
...model.effectiveContextWindowTokens === void 0 ? {} : {
|
|
2070
|
+
effectiveContextWindowTokens: model.effectiveContextWindowTokens
|
|
2071
|
+
},
|
|
1844
2072
|
...model.autoCompactTokenLimit === void 0 ? {} : { autoCompactTokenLimit: model.autoCompactTokenLimit },
|
|
1845
2073
|
...model.toolOutputTruncationTokens === void 0 ? {} : { toolOutputTruncationTokens: model.toolOutputTruncationTokens },
|
|
1846
2074
|
reasoningEffort: capabilities.reasoning.runnable,
|
|
@@ -1890,6 +2118,9 @@ function resolveTurnExecutionPolicyV1(settings, input) {
|
|
|
1890
2118
|
if (input.requestedModelId !== null && canonicalizeConfiguredModelId(catalogSettings, input.requestedModelId) !== productModelId) {
|
|
1891
2119
|
throw new Error("Turn execution policy requested model does not canonicalize to its product");
|
|
1892
2120
|
}
|
|
2121
|
+
const latencyMode = LatencyMode.parse(input.latencyMode ?? "standard");
|
|
2122
|
+
const latencyModeSource = input.latencyModeSource ?? "deployment";
|
|
2123
|
+
assertLatencyModeRunnable(catalogSettings, productModelId, latencyMode);
|
|
1893
2124
|
return TurnExecutionPolicyV1.parse({
|
|
1894
2125
|
schemaVersion: 1,
|
|
1895
2126
|
productModelId,
|
|
@@ -1897,6 +2128,8 @@ function resolveTurnExecutionPolicyV1(settings, input) {
|
|
|
1897
2128
|
modelSource: input.modelSource,
|
|
1898
2129
|
reasoningEffort: input.reasoningEffort,
|
|
1899
2130
|
reasoningSource: input.reasoningSource,
|
|
2131
|
+
latencyMode,
|
|
2132
|
+
latencyModeSource,
|
|
1900
2133
|
providerId: resolved.provider.id,
|
|
1901
2134
|
upstreamModelId: resolved.model.upstreamModelId,
|
|
1902
2135
|
wireApi: resolved.model.api,
|
|
@@ -1909,9 +2142,13 @@ function assertTurnExecutionPolicyMatchesConfigV1(settings, policy, expected) {
|
|
|
1909
2142
|
const parsed = TurnExecutionPolicyV1.parse(policy);
|
|
1910
2143
|
const catalogSettings = settingsForTurnExecutionPolicy(settings, parsed.productModelId);
|
|
1911
2144
|
const canonicalExpectedModel = canonicalizeConfiguredModelId(catalogSettings, expected.modelId);
|
|
1912
|
-
|
|
1913
|
-
|
|
2145
|
+
const expectedLatencyMode = expected.latencyMode ?? parsed.latencyMode;
|
|
2146
|
+
if (parsed.productModelId !== canonicalExpectedModel || parsed.reasoningEffort !== expected.reasoningEffort || parsed.latencyMode !== expectedLatencyMode) {
|
|
2147
|
+
throw new Error(
|
|
2148
|
+
"Turn execution policy does not match the accepted turn model/reasoning/latency"
|
|
2149
|
+
);
|
|
1914
2150
|
}
|
|
2151
|
+
assertLatencyModeRunnable(catalogSettings, parsed.productModelId, parsed.latencyMode);
|
|
1915
2152
|
if (parsed.requestedModelId !== null && canonicalizeConfiguredModelId(catalogSettings, parsed.requestedModelId) !== parsed.productModelId) {
|
|
1916
2153
|
throw new Error("Turn execution policy requested model does not match its product model");
|
|
1917
2154
|
}
|
|
@@ -1927,7 +2164,10 @@ function assertTurnExecutionPolicyMatchesConfigV1(settings, policy, expected) {
|
|
|
1927
2164
|
}
|
|
1928
2165
|
function configuredModelPricingSchedules(settings) {
|
|
1929
2166
|
const defaults = Object.fromEntries(
|
|
1930
|
-
Object.entries(defaultModelPricing).map(([model, pricing]) => [
|
|
2167
|
+
Object.entries(defaultModelPricing).map(([model, pricing]) => [
|
|
2168
|
+
model,
|
|
2169
|
+
normalizeModelPricingSchedule(pricing)
|
|
2170
|
+
])
|
|
1931
2171
|
);
|
|
1932
2172
|
const registry = {};
|
|
1933
2173
|
for (const provider of parseModelProvidersJson(settings.modelProvidersJson)) {
|
|
@@ -2009,7 +2249,7 @@ function configuredEntitlements(settings) {
|
|
|
2009
2249
|
...configured
|
|
2010
2250
|
};
|
|
2011
2251
|
}
|
|
2012
|
-
function calculateModelUsageCostMicros(settings, model, usage) {
|
|
2252
|
+
function calculateModelUsageCostMicros(settings, model, usage, options) {
|
|
2013
2253
|
const schedule = configuredModelPricingSchedules(settings)[model];
|
|
2014
2254
|
if (!schedule) {
|
|
2015
2255
|
throw new Error(`Missing model pricing for ${model}`);
|
|
@@ -2028,6 +2268,20 @@ function calculateModelUsageCostMicros(settings, model, usage) {
|
|
|
2028
2268
|
const marginBps = pricing.marginBps ?? 0;
|
|
2029
2269
|
total += Math.ceil(rawCost * (1e4 + marginBps) / 1e4);
|
|
2030
2270
|
}
|
|
2271
|
+
const latencyMode = options?.latencyMode ?? "standard";
|
|
2272
|
+
if (latencyMode !== "standard") {
|
|
2273
|
+
const catalogSettings = settingsForTurnExecutionPolicy(settings, model);
|
|
2274
|
+
const resolved = resolveModelProvider(
|
|
2275
|
+
catalogSettings,
|
|
2276
|
+
canonicalizeConfiguredModelId(catalogSettings, model)
|
|
2277
|
+
);
|
|
2278
|
+
const multiplierBps = resolved?.model.capabilities.latencyModes.find(
|
|
2279
|
+
(mode) => mode.id === latencyMode && mode.runnable
|
|
2280
|
+
)?.billingMultiplierBps;
|
|
2281
|
+
if (multiplierBps && multiplierBps > 0) {
|
|
2282
|
+
total = Math.ceil(total * multiplierBps / 1e4);
|
|
2283
|
+
}
|
|
2284
|
+
}
|
|
2031
2285
|
return total;
|
|
2032
2286
|
}
|
|
2033
2287
|
function configuredAllowedReasoningEfforts(settings) {
|
|
@@ -2263,7 +2517,9 @@ function parseMcpServers(raw) {
|
|
|
2263
2517
|
return parsed;
|
|
2264
2518
|
} catch (error) {
|
|
2265
2519
|
const message = error instanceof Error ? error.message : String(error);
|
|
2266
|
-
throw new Error(`OPENGENI_MCP_SERVERS must be a JSON array: ${message}`, {
|
|
2520
|
+
throw new Error(`OPENGENI_MCP_SERVERS must be a JSON array: ${message}`, {
|
|
2521
|
+
cause: error
|
|
2522
|
+
});
|
|
2267
2523
|
}
|
|
2268
2524
|
}
|
|
2269
2525
|
function parseModelPricingJson(raw) {
|
|
@@ -2575,6 +2831,11 @@ function validateSettings(settings) {
|
|
|
2575
2831
|
);
|
|
2576
2832
|
}
|
|
2577
2833
|
if (settings.slackClientId) {
|
|
2834
|
+
if (!settings.slackSigningSecret) {
|
|
2835
|
+
throw new Error(
|
|
2836
|
+
"OPENGENI_SLACK_SIGNING_SECRET is required when the OpenGeni Slack app is configured"
|
|
2837
|
+
);
|
|
2838
|
+
}
|
|
2578
2839
|
if (!settings.publicBaseUrl) {
|
|
2579
2840
|
throw new Error(
|
|
2580
2841
|
"OPENGENI_PUBLIC_BASE_URL is required when the OpenGeni Slack app is configured"
|
|
@@ -2591,6 +2852,28 @@ function validateSettings(settings) {
|
|
|
2591
2852
|
);
|
|
2592
2853
|
}
|
|
2593
2854
|
}
|
|
2855
|
+
if (Boolean(settings.googleDriveClientId) !== Boolean(settings.googleDriveClientSecret)) {
|
|
2856
|
+
throw new Error(
|
|
2857
|
+
"OPENGENI_GOOGLE_DRIVE_CLIENT_ID and OPENGENI_GOOGLE_DRIVE_CLIENT_SECRET must be configured together"
|
|
2858
|
+
);
|
|
2859
|
+
}
|
|
2860
|
+
if (settings.googleDriveClientId) {
|
|
2861
|
+
if (!settings.publicBaseUrl) {
|
|
2862
|
+
throw new Error(
|
|
2863
|
+
"OPENGENI_PUBLIC_BASE_URL is required when the Google Drive integration is configured"
|
|
2864
|
+
);
|
|
2865
|
+
}
|
|
2866
|
+
if (!settings.publicBaseUrl.startsWith("https://") && !["local", "test"].includes(settings.environment)) {
|
|
2867
|
+
throw new Error(
|
|
2868
|
+
"OPENGENI_PUBLIC_BASE_URL must use https when the Google Drive integration is configured outside local/test"
|
|
2869
|
+
);
|
|
2870
|
+
}
|
|
2871
|
+
if (!settings.integrationsStateSecret) {
|
|
2872
|
+
throw new Error(
|
|
2873
|
+
"OPENGENI_INTEGRATIONS_STATE_SECRET is required when the Google Drive integration is configured"
|
|
2874
|
+
);
|
|
2875
|
+
}
|
|
2876
|
+
}
|
|
2594
2877
|
parseIntegrationsOauthClientsJson(settings.integrationsOauthClientsJson);
|
|
2595
2878
|
if (settings.productAccessMode === "configured" && !["local", "test"].includes(settings.environment) && !settings.delegationSecret && !settings.authRequired) {
|
|
2596
2879
|
throw new Error(
|
|
@@ -2674,7 +2957,7 @@ function validateSettings(settings) {
|
|
|
2674
2957
|
"OPENGENI_OBJECT_STORAGE_ACCESS_KEY_ID and OPENGENI_OBJECT_STORAGE_SECRET_ACCESS_KEY must both be set or both omitted"
|
|
2675
2958
|
);
|
|
2676
2959
|
}
|
|
2677
|
-
if (settings.objectStorageBackend === "s3-compatible" && (settings.objectStorageEndpoint || settings.objectStorageSandboxEndpoint) && (!settings.objectStorageAccessKeyId || !settings.objectStorageSecretAccessKey)) {
|
|
2960
|
+
if (settings.objectStorageBackend === "s3-compatible" && (settings.objectStorageEndpoint || settings.objectStorageInternalEndpoint || settings.objectStorageSandboxEndpoint) && (!settings.objectStorageAccessKeyId || !settings.objectStorageSecretAccessKey)) {
|
|
2678
2961
|
throw new Error(
|
|
2679
2962
|
"S3-compatible object storage endpoints require OPENGENI_OBJECT_STORAGE_ACCESS_KEY_ID and OPENGENI_OBJECT_STORAGE_SECRET_ACCESS_KEY"
|
|
2680
2963
|
);
|
|
@@ -2690,7 +2973,7 @@ function validateSettings(settings) {
|
|
|
2690
2973
|
);
|
|
2691
2974
|
}
|
|
2692
2975
|
} else if (settings.objectStorageBackend === "azure-blob") {
|
|
2693
|
-
if (settings.objectStorageEndpoint || settings.objectStorageSandboxEndpoint || settings.objectStorageAccessKeyId || settings.objectStorageSecretAccessKey) {
|
|
2976
|
+
if (settings.objectStorageEndpoint || settings.objectStorageInternalEndpoint || settings.objectStorageSandboxEndpoint || settings.objectStorageAccessKeyId || settings.objectStorageSecretAccessKey) {
|
|
2694
2977
|
throw new Error(
|
|
2695
2978
|
"Azure Blob storage uses OPENGENI_OBJECT_STORAGE_AZURE_* settings, not S3-compatible object storage settings"
|
|
2696
2979
|
);
|
|
@@ -2708,7 +2991,7 @@ function validateSettings(settings) {
|
|
|
2708
2991
|
);
|
|
2709
2992
|
}
|
|
2710
2993
|
} else {
|
|
2711
|
-
if (settings.objectStorageEndpoint || settings.objectStorageSandboxEndpoint || settings.objectStorageAccessKeyId || settings.objectStorageSecretAccessKey) {
|
|
2994
|
+
if (settings.objectStorageEndpoint || settings.objectStorageInternalEndpoint || settings.objectStorageSandboxEndpoint || settings.objectStorageAccessKeyId || settings.objectStorageSecretAccessKey) {
|
|
2712
2995
|
throw new Error(
|
|
2713
2996
|
"GCS object storage uses OPENGENI_OBJECT_STORAGE_GCS_* settings, not S3-compatible object storage settings"
|
|
2714
2997
|
);
|
|
@@ -2743,6 +3026,7 @@ function validateSettings(settings) {
|
|
|
2743
3026
|
const viewerTtl = settings.sandboxViewerHolderTtlMs;
|
|
2744
3027
|
const idleGraceMs = settings.sandboxIdleGraceMs;
|
|
2745
3028
|
const providerLifetimeMs = settings.modalTimeoutSeconds * 1e3;
|
|
3029
|
+
const rotationLeadMs = settings.sandboxRotationLeadMs;
|
|
2746
3030
|
const idleTimeoutMs = effectiveModalIdleTimeoutSeconds(settings) * 1e3;
|
|
2747
3031
|
if (!(reaperPeriod < viewerTtl)) {
|
|
2748
3032
|
throw new Error(
|
|
@@ -2754,6 +3038,16 @@ function validateSettings(settings) {
|
|
|
2754
3038
|
`OPENGENI_MODAL_IDLE_TIMEOUT_SECONDS*1000 (${idleTimeoutMs}) must not exceed the hard provider lifetime (OPENGENI_MODAL_TIMEOUT_SECONDS*1000 = ${providerLifetimeMs}): the idle timeout is a floor under the hard lifetime, not above it.`
|
|
2755
3039
|
);
|
|
2756
3040
|
}
|
|
3041
|
+
if (!(rotationLeadMs < providerLifetimeMs)) {
|
|
3042
|
+
throw new Error(
|
|
3043
|
+
`OPENGENI_SANDBOX_ROTATION_LEAD_MS (${rotationLeadMs}) must be strictly less than OPENGENI_MODAL_TIMEOUT_SECONDS*1000 (${providerLifetimeMs}).`
|
|
3044
|
+
);
|
|
3045
|
+
}
|
|
3046
|
+
if (!(rotationLeadMs > settings.sandboxSnapshotTimeoutMs + 2 * reaperPeriod)) {
|
|
3047
|
+
throw new Error(
|
|
3048
|
+
`OPENGENI_SANDBOX_ROTATION_LEAD_MS (${rotationLeadMs}) must exceed the snapshot timeout plus two reaper periods (${settings.sandboxSnapshotTimeoutMs + 2 * reaperPeriod}).`
|
|
3049
|
+
);
|
|
3050
|
+
}
|
|
2757
3051
|
if (!(viewerTtl < idleTimeoutMs)) {
|
|
2758
3052
|
throw new Error(
|
|
2759
3053
|
`OPENGENI_SANDBOX_VIEWER_HOLDER_TTL_MS (${viewerTtl}) must be strictly less than the effective box idle timeout (${idleTimeoutMs}): a viewer holder must be reapable before the box idles out from under it (the provider idle-timeout is the backstop).`
|
|
@@ -2911,6 +3205,7 @@ export {
|
|
|
2911
3205
|
getSettings,
|
|
2912
3206
|
hasGitCredentialRepositorySelection,
|
|
2913
3207
|
hasGitHubRepositorySelection,
|
|
3208
|
+
isUsableVoiceInputSecret,
|
|
2914
3209
|
parseExposedPorts,
|
|
2915
3210
|
parseIntegrationsOauthClientsJson,
|
|
2916
3211
|
parseMcpServers,
|
|
@@ -2920,8 +3215,10 @@ export {
|
|
|
2920
3215
|
parseStaticEntitlementsJson,
|
|
2921
3216
|
parseStaticUsageLimitsJson,
|
|
2922
3217
|
policyProviderIdForModel,
|
|
3218
|
+
productLabelForModelId,
|
|
2923
3219
|
requiredSandboxEnvForBackend,
|
|
2924
3220
|
resolveEnrollmentSigningSecret,
|
|
3221
|
+
resolveFirstPartyDelegationSecret,
|
|
2925
3222
|
resolveModelProvider,
|
|
2926
3223
|
resolveNatsCalloutConfig,
|
|
2927
3224
|
resolveNatsControlPlaneAuth,
|
|
@@ -2929,17 +3226,23 @@ export {
|
|
|
2929
3226
|
resolveRelayTokenSecret,
|
|
2930
3227
|
resolveStreamTokenSecret,
|
|
2931
3228
|
resolveTurnExecutionPolicyV1,
|
|
3229
|
+
resolveVoiceInputProviderRegistry,
|
|
3230
|
+
responseSatisfiesLatencyMode,
|
|
2932
3231
|
retryStartupDependency,
|
|
3232
|
+
runnableLatencyModesForModel,
|
|
3233
|
+
sandboxArchiveCaptureTimeoutMs,
|
|
2933
3234
|
sandboxEnvironmentVariableNames,
|
|
2934
3235
|
sandboxLifecycleHookIds,
|
|
2935
3236
|
sandboxPreparationProfiles,
|
|
2936
3237
|
sandboxWarmRateMicrosPerSecond,
|
|
2937
3238
|
selectModelPricing,
|
|
3239
|
+
serviceTierForLatencyMode,
|
|
2938
3240
|
settingsWithResolvedModelContext,
|
|
2939
3241
|
stableSandboxEnvironmentForRun,
|
|
2940
3242
|
startupRetryOptions,
|
|
2941
3243
|
streamTokenDegraded,
|
|
2942
3244
|
temporalConnectionOptions,
|
|
3245
|
+
voiceInputDeploymentConfigured,
|
|
2943
3246
|
withCodexCatalogProvider
|
|
2944
3247
|
};
|
|
2945
3248
|
//# sourceMappingURL=index.js.map
|