@opengeni/config 0.7.11 → 0.7.22
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 +270 -175
- package/dist/index.js +399 -122
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
- package/src/index.ts +575 -121
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>`.
|
|
@@ -200,9 +204,6 @@ var SettingsSchema = z.object({
|
|
|
200
204
|
// holder of stream:control gets 403 until this flips. Keeps stream:control a
|
|
201
205
|
// declared-but-inert permission so later hardening is a flag flip.
|
|
202
206
|
streamControlEnabled: EnvBoolean.default(false),
|
|
203
|
-
// Existing-session explicit tool replacement is gated until every API and
|
|
204
|
-
// worker instance understands durable tools_provided provenance.
|
|
205
|
-
sessionTurnToolReplacementEnabled: EnvBoolean.default(false),
|
|
206
207
|
toolspaceEnabled: EnvBoolean.default(false),
|
|
207
208
|
toolspaceMaxCallsPerTurn: z.coerce.number().int().positive().default(200),
|
|
208
209
|
// Optional release-coherent bootstrap hint for custom rigs/connected machines
|
|
@@ -216,6 +217,8 @@ var SettingsSchema = z.object({
|
|
|
216
217
|
integrationsOauthClientsJson: z.string().default("{}"),
|
|
217
218
|
slackClientId: z.string().optional(),
|
|
218
219
|
slackClientSecret: z.string().optional(),
|
|
220
|
+
googleDriveClientId: z.string().optional(),
|
|
221
|
+
googleDriveClientSecret: z.string().optional(),
|
|
219
222
|
// Undefined is meaningful: the migration boundary persists the product
|
|
220
223
|
// default of 3 when no deployment override is supplied.
|
|
221
224
|
maxNestedAgentDepth: z.coerce.number().int().nonnegative().max(MAX_NESTED_AGENT_DEPTH).optional(),
|
|
@@ -276,6 +279,36 @@ var SettingsSchema = z.object({
|
|
|
276
279
|
openaiBaseUrl: z.string().optional(),
|
|
277
280
|
openaiModel: z.string().default("gpt-5.6-sol"),
|
|
278
281
|
openaiAllowedModels: z.string().default("gpt-5.6-sol,gpt-5.6-terra,gpt-5.6-luna"),
|
|
282
|
+
// Native composer voice input (browser MediaRecorder → API transcription).
|
|
283
|
+
// Provider credentials stay server-side; ClientConfig only projects availability
|
|
284
|
+
// and hard ceilings. Selection happens once before audio is sent — never retry
|
|
285
|
+
// the same clip across vendors after an upstream request may have started.
|
|
286
|
+
voiceInputMaxDurationSeconds: z.coerce.number().int().positive().max(600).default(60),
|
|
287
|
+
voiceInputMaxSizeBytes: z.coerce.number().int().positive().max(100 * 1024 * 1024).default(25 * 1024 * 1024),
|
|
288
|
+
// Preferred provider order (comma-separated ids). First configured+ready wins.
|
|
289
|
+
// Codex subscription STT is preferred by default when subscription routing is
|
|
290
|
+
// enabled; operators can put openai/azure-openai first explicitly.
|
|
291
|
+
// Supported: openai, azure-openai, codex-subscription.
|
|
292
|
+
voiceInputProviderOrder: z.string().default("codex-subscription,openai,azure-openai"),
|
|
293
|
+
// OpenAI public /v1/audio/transcriptions path. Reuses OPENGENI_OPENAI_API_KEY
|
|
294
|
+
// when voiceInputOpenaiApiKey is unset. Default model is gpt-transcribe.
|
|
295
|
+
voiceInputOpenaiEnabled: EnvBoolean.default(true),
|
|
296
|
+
voiceInputOpenaiApiKey: z.string().optional(),
|
|
297
|
+
voiceInputOpenaiBaseUrl: z.string().optional(),
|
|
298
|
+
voiceInputOpenaiModel: z.string().default("gpt-transcribe"),
|
|
299
|
+
// Azure OpenAI deployment-scoped audio transcriptions. Reuses the turn-model
|
|
300
|
+
// Azure endpoint/key/AD token when voice-specific overrides are unset.
|
|
301
|
+
voiceInputAzureEnabled: EnvBoolean.default(true),
|
|
302
|
+
voiceInputAzureEndpoint: z.string().optional(),
|
|
303
|
+
voiceInputAzureDeployment: z.string().optional(),
|
|
304
|
+
voiceInputAzureApiVersion: z.string().optional(),
|
|
305
|
+
voiceInputAzureApiKey: z.string().optional(),
|
|
306
|
+
voiceInputAzureAdToken: z.string().optional(),
|
|
307
|
+
// Legacy opt-in for undocumented ChatGPT /backend-api/transcribe. When
|
|
308
|
+
// OPENGENI_CODEX_SUBSCRIPTION_ENABLED is true, Codex STT is included without
|
|
309
|
+
// this flag. Set false and omit codex-subscription from PROVIDER_ORDER to
|
|
310
|
+
// keep subscription model routing while disabling Codex voice input.
|
|
311
|
+
voiceInputCodexExperimentalEnabled: EnvBoolean.default(false),
|
|
279
312
|
modelPricingJson: z.string().default("{}"),
|
|
280
313
|
// Extra (non-built-in) model providers, declared by the host as a JSON
|
|
281
314
|
// provider registry. Each entry carries its own base URL, API key, wire API
|
|
@@ -362,6 +395,11 @@ var SettingsSchema = z.object({
|
|
|
362
395
|
dockerImage: z.string().default("opengeni-sandbox:local"),
|
|
363
396
|
dockerExposedPorts: z.string().default(""),
|
|
364
397
|
dockerNetwork: z.string().optional(),
|
|
398
|
+
// When the worker itself runs in a container and talks to a host Docker daemon,
|
|
399
|
+
// this directory must be bind-mounted at the exact same absolute path on both
|
|
400
|
+
// sides. The Agents SDK materializes the workspace here before bind-mounting it
|
|
401
|
+
// into the sandbox container.
|
|
402
|
+
dockerWorkspaceBaseDir: z.string().min(1).optional(),
|
|
365
403
|
modalAppName: z.string().default("opengeni-sandbox"),
|
|
366
404
|
modalImageRef: z.string().optional(),
|
|
367
405
|
// Name of a Modal Secret (containing REGISTRY_USERNAME + REGISTRY_PASSWORD) used
|
|
@@ -373,17 +411,17 @@ var SettingsSchema = z.object({
|
|
|
373
411
|
// the named Secret and builds the image via `fromRegistry(tag, secret)` before the
|
|
374
412
|
// first sandbox is created. Knob: OPENGENI_MODAL_IMAGE_REGISTRY_SECRET.
|
|
375
413
|
modalImageRegistrySecret: z.string().optional(),
|
|
376
|
-
// Modal's hard sandbox lifetime (timeoutMs = this * 1000), counted from
|
|
377
|
-
//
|
|
378
|
-
// down, NOT the warm-window
|
|
379
|
-
//
|
|
380
|
-
//
|
|
381
|
-
//
|
|
382
|
-
//
|
|
383
|
-
//
|
|
384
|
-
//
|
|
414
|
+
// Modal's hard sandbox lifetime (timeoutMs = this * 1000), counted from box
|
|
415
|
+
// creation. A resume-by-id does NOT reset that provider clock. It is the
|
|
416
|
+
// BACKSTOP that reclaims a box if the reaper/worker is down, NOT the warm-window
|
|
417
|
+
// controller (that's sandboxIdleGraceMs). It must comfortably exceed
|
|
418
|
+
// reaperPeriod + idleGrace so the reaper terminates a genuinely-idle box FIRST;
|
|
419
|
+
// the boot invariant below enforces that. Default 24h, Modal's documented
|
|
420
|
+
// maximum, to reduce premature active-box loss and leave headroom for the
|
|
421
|
+
// deadline-aware snapshot/rematerialization transition. The transition—not a
|
|
422
|
+
// larger timeout—is what lets a session outlive one finite provider box.
|
|
385
423
|
// Knob: OPENGENI_MODAL_TIMEOUT_SECONDS.
|
|
386
|
-
modalTimeoutSeconds: z.coerce.number().int().positive().default(
|
|
424
|
+
modalTimeoutSeconds: z.coerce.number().int().positive().max(86400).default(86400),
|
|
387
425
|
modalTokenId: z.string().optional(),
|
|
388
426
|
modalTokenSecret: z.string().optional(),
|
|
389
427
|
modalEnvironment: z.string().optional(),
|
|
@@ -414,13 +452,6 @@ var SettingsSchema = z.object({
|
|
|
414
452
|
// OPENGENI_MODAL_WORKSPACE_PERSISTENCE=tar to opt back out (no native snapshot;
|
|
415
453
|
// the reaper persists a tar archive — same store+hydrate plumbing, slower).
|
|
416
454
|
modalWorkspacePersistence: z.enum(["tar", "snapshot_filesystem", "snapshot_directory"]).default("snapshot_filesystem"),
|
|
417
|
-
// Snapshot GC backstop (sandbox-file-persistence): the reaper keeps ONE latest
|
|
418
|
-
// filesystem snapshot per lease (delete-prior-on-supersede + delete-on-teardown).
|
|
419
|
-
// This is the TTL retention floor for the periodic orphan sweep — a snapshot
|
|
420
|
-
// whose lease is cold and older than this is best-effort deleted so a crashed
|
|
421
|
-
// persist-then-no-restore never leaks a Modal image. 0 disables the TTL sweep
|
|
422
|
-
// (delete-on-supersede/teardown still run). Default 7 days.
|
|
423
|
-
modalSnapshotRetentionSeconds: z.coerce.number().int().nonnegative().default(604800),
|
|
424
455
|
// Shared desktop toggle: this module reads it for the 6080 port-merge; the
|
|
425
456
|
// owner module (P4.x) acts on it to launch the display stack.
|
|
426
457
|
sandboxDesktopEnabled: EnvBoolean.default(false),
|
|
@@ -642,8 +673,9 @@ var SettingsSchema = z.object({
|
|
|
642
673
|
// this whole window so a "glanced away then came back" re-arms the SAME warm box
|
|
643
674
|
// (acquireLease re-arms draining->warm; the reaper's BEFORE-terminate re-read
|
|
644
675
|
// skips a re-armed box). Default 15min so a brief detour never cold-creates a
|
|
645
|
-
// fresh EMPTY box; lower it to trade warm cost for a snappier reclaim.
|
|
646
|
-
//
|
|
676
|
+
// fresh EMPTY box; lower it to trade warm cost for a snappier reclaim.
|
|
677
|
+
// getSettings caps the default at half a shorter configured Modal lifetime so
|
|
678
|
+
// the entire reaper window always fits. Knob: OPENGENI_SANDBOX_IDLE_GRACE_MS.
|
|
647
679
|
sandboxIdleGraceMs: z.coerce.number().int().positive().default(9e5),
|
|
648
680
|
// MID-SESSION /workspace snapshot cadence (sandbox-file-persistence). The
|
|
649
681
|
// reaper's drain-persist only protects boxes the reaper itself kills; a box
|
|
@@ -661,6 +693,22 @@ var SettingsSchema = z.object({
|
|
|
661
693
|
// treated exactly like a failed best-effort snapshot. Knob:
|
|
662
694
|
// OPENGENI_SANDBOX_SNAPSHOT_TIMEOUT_MS. Default 60s.
|
|
663
695
|
sandboxSnapshotTimeoutMs: z.coerce.number().int().positive().default(6e4),
|
|
696
|
+
// Begin a controlled snapshot/quiesce/drain/rematerialize transition this far
|
|
697
|
+
// ahead of a finite provider deadline. Modal's 24h creation clock cannot be
|
|
698
|
+
// extended; the logical sandbox outlives it by moving to one successor box.
|
|
699
|
+
// getSettings derives the actual default as min(1h, half the configured
|
|
700
|
+
// provider lifetime) so short-lived test/canary boxes remain bootable without
|
|
701
|
+
// an extra coupled environment override. An explicit value may be larger when
|
|
702
|
+
// an operator deliberately wants more rotation headroom; the boot invariant
|
|
703
|
+
// still requires it to remain below the provider lifetime.
|
|
704
|
+
sandboxRotationLeadMs: z.coerce.number().int().positive().default(36e5),
|
|
705
|
+
// Bound each global reaper pass so a rollout that discovers many legacy boxes
|
|
706
|
+
// with unknown creation clocks cannot create a provider/API thundering herd.
|
|
707
|
+
// One is the safe admission default: the reaper services provider transitions
|
|
708
|
+
// sequentially, so claiming a wider batch would fence boxes before the same
|
|
709
|
+
// sweep can service them. Larger fleets may raise this only as an explicit,
|
|
710
|
+
// observed deployment choice.
|
|
711
|
+
sandboxRotationBatchSize: z.coerce.number().int().positive().max(500).default(1),
|
|
664
712
|
// expires_at refresh window for a held lease (>> the turn 10s heartbeat so a
|
|
665
713
|
// single missed heartbeat never TTL-reaps a live turn). The warming TTL is the
|
|
666
714
|
// window a cold->warming spawner has to commit warm before a reaper resets it.
|
|
@@ -689,6 +737,7 @@ var SettingsSchema = z.object({
|
|
|
689
737
|
sandboxPreparationProfiles: z.string().default("none"),
|
|
690
738
|
sandboxEnvAllowlist: z.string().default(""),
|
|
691
739
|
objectStorageEndpoint: z.string().url().optional(),
|
|
740
|
+
objectStorageInternalEndpoint: z.string().url().optional(),
|
|
692
741
|
objectStorageSandboxEndpoint: z.string().url().optional(),
|
|
693
742
|
objectStorageBackend: z.enum(["s3-compatible", "aws-s3", "azure-blob", "gcs"]).default("s3-compatible"),
|
|
694
743
|
objectStorageBucket: z.string().min(1).default("opengeni-files"),
|
|
@@ -760,6 +809,77 @@ var SettingsSchema = z.object({
|
|
|
760
809
|
})
|
|
761
810
|
).default([])
|
|
762
811
|
});
|
|
812
|
+
function isUsableVoiceInputSecret(value) {
|
|
813
|
+
if (value == null) return false;
|
|
814
|
+
const trimmed = value.trim();
|
|
815
|
+
if (!trimmed) return false;
|
|
816
|
+
const normalized = trimmed.toLowerCase();
|
|
817
|
+
if (normalized === "your-key" || normalized === "your_key" || normalized === "changeme" || normalized === "replace-me" || normalized === "xxx" || normalized.startsWith("your-") || normalized.startsWith("your_")) {
|
|
818
|
+
return false;
|
|
819
|
+
}
|
|
820
|
+
return true;
|
|
821
|
+
}
|
|
822
|
+
function resolveVoiceInputProviderRegistry(settings) {
|
|
823
|
+
const order = settings.voiceInputProviderOrder.split(",").map((part) => part.trim()).filter(
|
|
824
|
+
(part) => part === "openai" || part === "azure-openai" || part === "codex-subscription"
|
|
825
|
+
);
|
|
826
|
+
const seen = /* @__PURE__ */ new Set();
|
|
827
|
+
const providers = [];
|
|
828
|
+
for (const id of order) {
|
|
829
|
+
if (seen.has(id)) continue;
|
|
830
|
+
seen.add(id);
|
|
831
|
+
if (id === "openai") {
|
|
832
|
+
if (!settings.voiceInputOpenaiEnabled) continue;
|
|
833
|
+
const apiKey = settings.voiceInputOpenaiApiKey ?? settings.openaiApiKey;
|
|
834
|
+
if (!isUsableVoiceInputSecret(apiKey)) continue;
|
|
835
|
+
if (settings.openaiProvider === "azure" && !settings.voiceInputOpenaiApiKey && !settings.voiceInputOpenaiBaseUrl) {
|
|
836
|
+
continue;
|
|
837
|
+
}
|
|
838
|
+
providers.push({
|
|
839
|
+
id: "openai",
|
|
840
|
+
kind: "openai",
|
|
841
|
+
apiKey,
|
|
842
|
+
baseUrl: (settings.voiceInputOpenaiBaseUrl ?? settings.openaiBaseUrl ?? "https://api.openai.com/v1").replace(/\/+$/, ""),
|
|
843
|
+
model: settings.voiceInputOpenaiModel
|
|
844
|
+
});
|
|
845
|
+
continue;
|
|
846
|
+
}
|
|
847
|
+
if (id === "azure-openai") {
|
|
848
|
+
if (!settings.voiceInputAzureEnabled) continue;
|
|
849
|
+
const endpoint = (settings.voiceInputAzureEndpoint ?? settings.azureOpenaiEndpoint ?? "").replace(/\/+$/, "");
|
|
850
|
+
const deployment = settings.voiceInputAzureDeployment ?? settings.azureOpenaiDeployment ?? "";
|
|
851
|
+
const apiVersion = settings.voiceInputAzureApiVersion ?? settings.azureOpenaiApiVersion ?? "2025-04-01-preview";
|
|
852
|
+
const apiKey = settings.voiceInputAzureApiKey ?? settings.azureOpenaiApiKey ?? null;
|
|
853
|
+
const adToken = settings.voiceInputAzureAdToken ?? settings.azureOpenaiAdToken ?? null;
|
|
854
|
+
if (!endpoint || !deployment || !isUsableVoiceInputSecret(apiKey) && !isUsableVoiceInputSecret(adToken)) {
|
|
855
|
+
continue;
|
|
856
|
+
}
|
|
857
|
+
if (settings.openaiProvider !== "azure" && !settings.voiceInputAzureEndpoint && !settings.voiceInputAzureDeployment && !settings.voiceInputAzureApiKey && !settings.voiceInputAzureAdToken) {
|
|
858
|
+
continue;
|
|
859
|
+
}
|
|
860
|
+
providers.push({
|
|
861
|
+
id: "azure-openai",
|
|
862
|
+
kind: "azure-openai",
|
|
863
|
+
endpoint,
|
|
864
|
+
deployment,
|
|
865
|
+
apiVersion,
|
|
866
|
+
apiKey: isUsableVoiceInputSecret(apiKey) ? apiKey : null,
|
|
867
|
+
adToken: isUsableVoiceInputSecret(adToken) ? adToken : null
|
|
868
|
+
});
|
|
869
|
+
continue;
|
|
870
|
+
}
|
|
871
|
+
if (id === "codex-subscription") {
|
|
872
|
+
if (!settings.codexSubscriptionEnabled) continue;
|
|
873
|
+
providers.push({ id: "codex-subscription", kind: "codex-subscription", experimental: true });
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
return providers;
|
|
877
|
+
}
|
|
878
|
+
function voiceInputDeploymentConfigured(settings) {
|
|
879
|
+
return resolveVoiceInputProviderRegistry(settings).some(
|
|
880
|
+
(provider) => provider.kind !== "codex-subscription"
|
|
881
|
+
);
|
|
882
|
+
}
|
|
763
883
|
var ModelPricingSchema = z.object({
|
|
764
884
|
inputMicrosPerMillionTokens: z.number().int().nonnegative(),
|
|
765
885
|
cachedInputMicrosPerMillionTokens: z.number().int().nonnegative().optional(),
|
|
@@ -953,86 +1073,74 @@ var IntegrationOAuthClientConfigSchema = z.object({
|
|
|
953
1073
|
});
|
|
954
1074
|
var defaultModelPricing = {
|
|
955
1075
|
"gpt-5.6-sol": {
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
1076
|
+
default: {
|
|
1077
|
+
inputMicrosPerMillionTokens: 5e6,
|
|
1078
|
+
cachedInputMicrosPerMillionTokens: 5e5,
|
|
1079
|
+
outputMicrosPerMillionTokens: 3e7,
|
|
1080
|
+
marginBps: 2500
|
|
1081
|
+
},
|
|
1082
|
+
inputTokenTiers: [
|
|
1083
|
+
{
|
|
1084
|
+
// OpenAI: prompts with >272K input tokens use the long-context rate.
|
|
1085
|
+
minimumInputTokens: 272001,
|
|
1086
|
+
pricing: {
|
|
1087
|
+
inputMicrosPerMillionTokens: 1e7,
|
|
1088
|
+
cachedInputMicrosPerMillionTokens: 1e6,
|
|
1089
|
+
outputMicrosPerMillionTokens: 45e6,
|
|
1090
|
+
marginBps: 2500
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
]
|
|
960
1094
|
},
|
|
961
1095
|
"gpt-5.6-terra": {
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
1096
|
+
default: {
|
|
1097
|
+
inputMicrosPerMillionTokens: 2e6,
|
|
1098
|
+
cachedInputMicrosPerMillionTokens: 2e5,
|
|
1099
|
+
outputMicrosPerMillionTokens: 12e6,
|
|
1100
|
+
marginBps: 2500
|
|
1101
|
+
},
|
|
1102
|
+
inputTokenTiers: [
|
|
1103
|
+
{
|
|
1104
|
+
minimumInputTokens: 272001,
|
|
1105
|
+
pricing: {
|
|
1106
|
+
inputMicrosPerMillionTokens: 4e6,
|
|
1107
|
+
cachedInputMicrosPerMillionTokens: 4e5,
|
|
1108
|
+
outputMicrosPerMillionTokens: 18e6,
|
|
1109
|
+
marginBps: 2500
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
]
|
|
966
1113
|
},
|
|
967
1114
|
"gpt-5.6-luna": {
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
"gpt-5.2": {
|
|
986
|
-
inputMicrosPerMillionTokens: 175e4,
|
|
987
|
-
cachedInputMicrosPerMillionTokens: 175e3,
|
|
988
|
-
outputMicrosPerMillionTokens: 14e6,
|
|
989
|
-
marginBps: 2500
|
|
990
|
-
},
|
|
991
|
-
"gpt-5.2-chat-latest": {
|
|
992
|
-
inputMicrosPerMillionTokens: 175e4,
|
|
993
|
-
cachedInputMicrosPerMillionTokens: 175e3,
|
|
994
|
-
outputMicrosPerMillionTokens: 14e6,
|
|
995
|
-
marginBps: 2500
|
|
996
|
-
},
|
|
997
|
-
"gpt-5.2-codex": {
|
|
998
|
-
inputMicrosPerMillionTokens: 175e4,
|
|
999
|
-
cachedInputMicrosPerMillionTokens: 175e3,
|
|
1000
|
-
outputMicrosPerMillionTokens: 14e6,
|
|
1001
|
-
marginBps: 2500
|
|
1002
|
-
},
|
|
1003
|
-
"gpt-5.1": {
|
|
1004
|
-
inputMicrosPerMillionTokens: 125e4,
|
|
1005
|
-
cachedInputMicrosPerMillionTokens: 125e3,
|
|
1006
|
-
outputMicrosPerMillionTokens: 1e7,
|
|
1007
|
-
marginBps: 2500
|
|
1008
|
-
},
|
|
1009
|
-
"gpt-5": {
|
|
1010
|
-
inputMicrosPerMillionTokens: 125e4,
|
|
1011
|
-
cachedInputMicrosPerMillionTokens: 125e3,
|
|
1012
|
-
outputMicrosPerMillionTokens: 1e7,
|
|
1013
|
-
marginBps: 2500
|
|
1014
|
-
},
|
|
1015
|
-
"gpt-5-mini": {
|
|
1016
|
-
inputMicrosPerMillionTokens: 25e4,
|
|
1017
|
-
cachedInputMicrosPerMillionTokens: 25e3,
|
|
1018
|
-
outputMicrosPerMillionTokens: 2e6,
|
|
1019
|
-
marginBps: 2500
|
|
1020
|
-
},
|
|
1021
|
-
"gpt-5-nano": {
|
|
1022
|
-
inputMicrosPerMillionTokens: 5e4,
|
|
1023
|
-
cachedInputMicrosPerMillionTokens: 5e3,
|
|
1024
|
-
outputMicrosPerMillionTokens: 4e5,
|
|
1025
|
-
marginBps: 2500
|
|
1115
|
+
default: {
|
|
1116
|
+
inputMicrosPerMillionTokens: 2e5,
|
|
1117
|
+
cachedInputMicrosPerMillionTokens: 2e4,
|
|
1118
|
+
outputMicrosPerMillionTokens: 12e5,
|
|
1119
|
+
marginBps: 2500
|
|
1120
|
+
},
|
|
1121
|
+
inputTokenTiers: [
|
|
1122
|
+
{
|
|
1123
|
+
minimumInputTokens: 272001,
|
|
1124
|
+
pricing: {
|
|
1125
|
+
inputMicrosPerMillionTokens: 4e5,
|
|
1126
|
+
cachedInputMicrosPerMillionTokens: 4e4,
|
|
1127
|
+
outputMicrosPerMillionTokens: 18e5,
|
|
1128
|
+
marginBps: 2500
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
]
|
|
1026
1132
|
},
|
|
1027
1133
|
// Fireworks AI / GLM 5.2 — the first shipped non-OpenAI registry model. A
|
|
1028
1134
|
// built-in default pricing entry makes managed billing work out of the box
|
|
1029
1135
|
// for hosts that expose this model via OPENGENI_MODEL_PROVIDERS_JSON without
|
|
1030
1136
|
// also setting OPENGENI_MODEL_PRICING_JSON.
|
|
1031
1137
|
"accounts/fireworks/models/glm-5p2": {
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1138
|
+
default: {
|
|
1139
|
+
inputMicrosPerMillionTokens: 14e5,
|
|
1140
|
+
cachedInputMicrosPerMillionTokens: 14e4,
|
|
1141
|
+
outputMicrosPerMillionTokens: 44e5,
|
|
1142
|
+
marginBps: 2500
|
|
1143
|
+
}
|
|
1036
1144
|
}
|
|
1037
1145
|
};
|
|
1038
1146
|
var SANDBOX_REQUIRED_ENV = {
|
|
@@ -1103,6 +1211,7 @@ function getSettings() {
|
|
|
1103
1211
|
observabilityOtlpEndpoint: optional("OPENGENI_OTEL_EXPORTER_OTLP_ENDPOINT") ?? optional("OTEL_EXPORTER_OTLP_ENDPOINT"),
|
|
1104
1212
|
observabilityOtlpHeaders: optional("OPENGENI_OTEL_EXPORTER_OTLP_HEADERS") ?? optional("OTEL_EXPORTER_OTLP_HEADERS"),
|
|
1105
1213
|
publicBaseUrl: optional("OPENGENI_PUBLIC_BASE_URL"),
|
|
1214
|
+
webBaseUrl: optional("OPENGENI_WEB_BASE_URL"),
|
|
1106
1215
|
agentReleasesBaseUrl: optional("OPENGENI_AGENT_RELEASES_BASE_URL"),
|
|
1107
1216
|
agentStableVersion: optional("OPENGENI_AGENT_STABLE_VERSION"),
|
|
1108
1217
|
productAccessMode: optional("OPENGENI_PRODUCT_ACCESS_MODE"),
|
|
@@ -1114,7 +1223,6 @@ function getSettings() {
|
|
|
1114
1223
|
delegationSecret: optional("OPENGENI_DELEGATION_SECRET"),
|
|
1115
1224
|
streamTokenSecret: optional("OPENGENI_STREAM_TOKEN_SECRET"),
|
|
1116
1225
|
streamControlEnabled: optional("OPENGENI_STREAM_CONTROL_ENABLED"),
|
|
1117
|
-
sessionTurnToolReplacementEnabled: optional("OPENGENI_SESSION_TURN_TOOL_REPLACEMENT_ENABLED"),
|
|
1118
1226
|
toolspaceEnabled: optional("OPENGENI_TOOLSPACE_ENABLED"),
|
|
1119
1227
|
toolspaceMaxCallsPerTurn: optional("OPENGENI_TOOLSPACE_MAX_CALLS_PER_TURN"),
|
|
1120
1228
|
ogtoolPackageSpec: optional("OPENGENI_OGTOOL_PACKAGE_SPEC"),
|
|
@@ -1127,6 +1235,8 @@ function getSettings() {
|
|
|
1127
1235
|
integrationsOauthClientsJson: optional("OPENGENI_INTEGRATIONS_OAUTH_CLIENTS_JSON"),
|
|
1128
1236
|
slackClientId: optional("OPENGENI_SLACK_CLIENT_ID"),
|
|
1129
1237
|
slackClientSecret: optional("OPENGENI_SLACK_CLIENT_SECRET"),
|
|
1238
|
+
googleDriveClientId: optional("OPENGENI_GOOGLE_DRIVE_CLIENT_ID"),
|
|
1239
|
+
googleDriveClientSecret: optional("OPENGENI_GOOGLE_DRIVE_CLIENT_SECRET"),
|
|
1130
1240
|
maxNestedAgentDepth: optional("OPENGENI_MAX_NESTED_AGENT_DEPTH"),
|
|
1131
1241
|
goalMaxAutoContinuations: optional("OPENGENI_GOAL_MAX_AUTO_CONTINUATIONS"),
|
|
1132
1242
|
goalNoProgressLimit: optional("OPENGENI_GOAL_NO_PROGRESS_LIMIT"),
|
|
@@ -1151,6 +1261,20 @@ function getSettings() {
|
|
|
1151
1261
|
openaiBaseUrl: optional("OPENGENI_OPENAI_BASE_URL") ?? optional("OPENAI_BASE_URL"),
|
|
1152
1262
|
openaiModel: optional("OPENGENI_OPENAI_MODEL"),
|
|
1153
1263
|
openaiAllowedModels: optional("OPENGENI_OPENAI_ALLOWED_MODELS"),
|
|
1264
|
+
voiceInputMaxDurationSeconds: optional("OPENGENI_VOICE_INPUT_MAX_DURATION_SECONDS"),
|
|
1265
|
+
voiceInputMaxSizeBytes: optional("OPENGENI_VOICE_INPUT_MAX_SIZE_BYTES"),
|
|
1266
|
+
voiceInputProviderOrder: optional("OPENGENI_VOICE_INPUT_PROVIDER_ORDER"),
|
|
1267
|
+
voiceInputOpenaiEnabled: optional("OPENGENI_VOICE_INPUT_OPENAI_ENABLED"),
|
|
1268
|
+
voiceInputOpenaiApiKey: optional("OPENGENI_VOICE_INPUT_OPENAI_API_KEY"),
|
|
1269
|
+
voiceInputOpenaiBaseUrl: optional("OPENGENI_VOICE_INPUT_OPENAI_BASE_URL"),
|
|
1270
|
+
voiceInputOpenaiModel: optional("OPENGENI_VOICE_INPUT_OPENAI_MODEL"),
|
|
1271
|
+
voiceInputAzureEnabled: optional("OPENGENI_VOICE_INPUT_AZURE_ENABLED"),
|
|
1272
|
+
voiceInputAzureEndpoint: optional("OPENGENI_VOICE_INPUT_AZURE_ENDPOINT"),
|
|
1273
|
+
voiceInputAzureDeployment: optional("OPENGENI_VOICE_INPUT_AZURE_DEPLOYMENT"),
|
|
1274
|
+
voiceInputAzureApiVersion: optional("OPENGENI_VOICE_INPUT_AZURE_API_VERSION"),
|
|
1275
|
+
voiceInputAzureApiKey: optional("OPENGENI_VOICE_INPUT_AZURE_API_KEY"),
|
|
1276
|
+
voiceInputAzureAdToken: optional("OPENGENI_VOICE_INPUT_AZURE_AD_TOKEN"),
|
|
1277
|
+
voiceInputCodexExperimentalEnabled: optional("OPENGENI_VOICE_INPUT_CODEX_EXPERIMENTAL"),
|
|
1154
1278
|
modelPricingJson: optional("OPENGENI_MODEL_PRICING_JSON"),
|
|
1155
1279
|
modelProvidersJson: optional("OPENGENI_MODEL_PROVIDERS_JSON"),
|
|
1156
1280
|
codexSubscriptionEnabled: optional("OPENGENI_CODEX_SUBSCRIPTION_ENABLED"),
|
|
@@ -1178,6 +1302,7 @@ function getSettings() {
|
|
|
1178
1302
|
dockerImage: optional("OPENGENI_DOCKER_IMAGE"),
|
|
1179
1303
|
dockerExposedPorts: optional("OPENGENI_DOCKER_EXPOSED_PORTS"),
|
|
1180
1304
|
dockerNetwork: optional("OPENGENI_DOCKER_NETWORK"),
|
|
1305
|
+
dockerWorkspaceBaseDir: optional("OPENGENI_DOCKER_WORKSPACE_BASE_DIR"),
|
|
1181
1306
|
modalAppName: optional("OPENGENI_MODAL_APP_NAME"),
|
|
1182
1307
|
modalImageRef: optional("OPENGENI_MODAL_IMAGE_REF"),
|
|
1183
1308
|
modalImageRegistrySecret: optional("OPENGENI_MODAL_IMAGE_REGISTRY_SECRET"),
|
|
@@ -1187,7 +1312,6 @@ function getSettings() {
|
|
|
1187
1312
|
modalEnvironment: optional("OPENGENI_MODAL_ENVIRONMENT"),
|
|
1188
1313
|
modalIdleTimeoutSeconds: optional("OPENGENI_MODAL_IDLE_TIMEOUT_SECONDS"),
|
|
1189
1314
|
modalWorkspacePersistence: optional("OPENGENI_MODAL_WORKSPACE_PERSISTENCE"),
|
|
1190
|
-
modalSnapshotRetentionSeconds: optional("OPENGENI_MODAL_SNAPSHOT_RETENTION_SECONDS"),
|
|
1191
1315
|
sandboxDesktopEnabled: optional("OPENGENI_SANDBOX_DESKTOP_ENABLED"),
|
|
1192
1316
|
sandboxDesktopInteractive: optional("OPENGENI_SANDBOX_DESKTOP_INTERACTIVE"),
|
|
1193
1317
|
sandboxTerminalEnabled: optional("OPENGENI_SANDBOX_TERMINAL_ENABLED"),
|
|
@@ -1260,6 +1384,8 @@ function getSettings() {
|
|
|
1260
1384
|
sandboxIdleGraceMs: optional("OPENGENI_SANDBOX_IDLE_GRACE_MS"),
|
|
1261
1385
|
sandboxSnapshotIntervalMs: optional("OPENGENI_SANDBOX_SNAPSHOT_INTERVAL_MS"),
|
|
1262
1386
|
sandboxSnapshotTimeoutMs: optional("OPENGENI_SANDBOX_SNAPSHOT_TIMEOUT_MS"),
|
|
1387
|
+
sandboxRotationLeadMs: optional("OPENGENI_SANDBOX_ROTATION_LEAD_MS"),
|
|
1388
|
+
sandboxRotationBatchSize: optional("OPENGENI_SANDBOX_ROTATION_BATCH_SIZE"),
|
|
1263
1389
|
sandboxLeaseTtlMs: optional("OPENGENI_SANDBOX_LEASE_TTL_MS"),
|
|
1264
1390
|
sandboxLeaseWarmingTtlMs: optional("OPENGENI_SANDBOX_LEASE_WARMING_TTL_MS"),
|
|
1265
1391
|
sandboxWarmingTimeoutMs: optional("OPENGENI_SANDBOX_WARMING_TIMEOUT_MS"),
|
|
@@ -1271,6 +1397,7 @@ function getSettings() {
|
|
|
1271
1397
|
sandboxPreparationProfiles: optional("OPENGENI_SANDBOX_PREPARATION_PROFILES"),
|
|
1272
1398
|
sandboxEnvAllowlist: optional("OPENGENI_SANDBOX_ENV_ALLOWLIST"),
|
|
1273
1399
|
objectStorageEndpoint: optional("OPENGENI_OBJECT_STORAGE_ENDPOINT"),
|
|
1400
|
+
objectStorageInternalEndpoint: optional("OPENGENI_OBJECT_STORAGE_INTERNAL_ENDPOINT"),
|
|
1274
1401
|
objectStorageSandboxEndpoint: optional("OPENGENI_OBJECT_STORAGE_SANDBOX_ENDPOINT"),
|
|
1275
1402
|
objectStorageBackend: optional("OPENGENI_OBJECT_STORAGE_BACKEND"),
|
|
1276
1403
|
objectStorageBucket: optional("OPENGENI_OBJECT_STORAGE_BUCKET"),
|
|
@@ -1326,6 +1453,8 @@ function getSettings() {
|
|
|
1326
1453
|
const parsed = SettingsSchema.parse(raw);
|
|
1327
1454
|
const settings = {
|
|
1328
1455
|
...parsed,
|
|
1456
|
+
sandboxIdleGraceMs: raw.sandboxIdleGraceMs === void 0 ? Math.min(9e5, Math.floor(parsed.modalTimeoutSeconds * 1e3 / 2)) : parsed.sandboxIdleGraceMs,
|
|
1457
|
+
sandboxRotationLeadMs: raw.sandboxRotationLeadMs === void 0 ? Math.min(36e5, Math.floor(parsed.modalTimeoutSeconds * 1e3 / 2)) : parsed.sandboxRotationLeadMs,
|
|
1329
1458
|
mcpServers: ensureBuiltInMcpServers(parsed)
|
|
1330
1459
|
};
|
|
1331
1460
|
validateSettings(settings);
|
|
@@ -1334,6 +1463,12 @@ function getSettings() {
|
|
|
1334
1463
|
function effectiveModalIdleTimeoutSeconds(settings) {
|
|
1335
1464
|
return settings.modalIdleTimeoutSeconds ?? settings.modalTimeoutSeconds;
|
|
1336
1465
|
}
|
|
1466
|
+
function sandboxArchiveCaptureTimeoutMs(settings) {
|
|
1467
|
+
return Math.min(
|
|
1468
|
+
60 * 6e4,
|
|
1469
|
+
Math.max(settings.sandboxSnapshotTimeoutMs + 3e4, settings.sandboxSnapshotTimeoutMs * 2)
|
|
1470
|
+
);
|
|
1471
|
+
}
|
|
1337
1472
|
function collectSandboxEnvironment(settings, source = process.env) {
|
|
1338
1473
|
const out = {};
|
|
1339
1474
|
for (const name of sandboxEnvironmentVariableNames(settings)) {
|
|
@@ -1570,6 +1705,68 @@ function legacyModelCapabilities(settings, input) {
|
|
|
1570
1705
|
latencyModes: [{ id: "standard", upstream: "unknown", runnable: true }]
|
|
1571
1706
|
});
|
|
1572
1707
|
}
|
|
1708
|
+
var GPT56_FAST_BILLING_MULTIPLIER_BPS = 2e4;
|
|
1709
|
+
function productLabelForModelId(modelId) {
|
|
1710
|
+
const slug = modelId.startsWith(CODEX_MODEL_ID_PREFIX) ? modelId.slice(CODEX_MODEL_ID_PREFIX.length) : modelId;
|
|
1711
|
+
const match = /^(gpt-\d+(?:\.\d+)?)(?:-(.+))?$/i.exec(slug);
|
|
1712
|
+
if (!match) {
|
|
1713
|
+
return slug;
|
|
1714
|
+
}
|
|
1715
|
+
const family = match[1].replace(/^gpt/i, "GPT");
|
|
1716
|
+
const rest = match[2];
|
|
1717
|
+
if (!rest) {
|
|
1718
|
+
return family;
|
|
1719
|
+
}
|
|
1720
|
+
const suffix = rest.split("-").filter((part) => part.length > 0).map((part) => part.charAt(0).toUpperCase() + part.slice(1).toLowerCase()).join(" ");
|
|
1721
|
+
return suffix.length > 0 ? `${family} ${suffix}` : family;
|
|
1722
|
+
}
|
|
1723
|
+
function builtinLatencyModesForModel(modelId) {
|
|
1724
|
+
if (modelId === "gpt-5.6-sol" || modelId === "gpt-5.6-terra" || modelId === "gpt-5.6-luna" || modelId.startsWith("codex/gpt-5.6-")) {
|
|
1725
|
+
return [
|
|
1726
|
+
{ id: "standard", upstream: "supported", runnable: true },
|
|
1727
|
+
{
|
|
1728
|
+
id: "fast",
|
|
1729
|
+
upstream: "supported",
|
|
1730
|
+
runnable: true,
|
|
1731
|
+
billingMultiplierBps: GPT56_FAST_BILLING_MULTIPLIER_BPS
|
|
1732
|
+
}
|
|
1733
|
+
];
|
|
1734
|
+
}
|
|
1735
|
+
return [{ id: "standard", upstream: "unknown", runnable: true }];
|
|
1736
|
+
}
|
|
1737
|
+
function serviceTierForLatencyMode(providerId, latencyMode) {
|
|
1738
|
+
if (latencyMode === "standard") {
|
|
1739
|
+
return void 0;
|
|
1740
|
+
}
|
|
1741
|
+
if (providerId === "azure" || providerId === CODEX_PROVIDER_ID) {
|
|
1742
|
+
return "priority";
|
|
1743
|
+
}
|
|
1744
|
+
return "fast";
|
|
1745
|
+
}
|
|
1746
|
+
function responseSatisfiesLatencyMode(requested, responseServiceTier) {
|
|
1747
|
+
if (requested === "standard") {
|
|
1748
|
+
return true;
|
|
1749
|
+
}
|
|
1750
|
+
return responseServiceTier === "priority" || responseServiceTier === "fast";
|
|
1751
|
+
}
|
|
1752
|
+
function runnableLatencyModesForModel(settings, modelId) {
|
|
1753
|
+
const resolved = resolveModelProvider(
|
|
1754
|
+
settingsForTurnExecutionPolicy(settings, modelId),
|
|
1755
|
+
canonicalizeConfiguredModelId(settings, modelId)
|
|
1756
|
+
);
|
|
1757
|
+
if (!resolved) {
|
|
1758
|
+
return ["standard"];
|
|
1759
|
+
}
|
|
1760
|
+
return resolved.model.capabilities.latencyModes.filter((mode) => mode.runnable).map((mode) => LatencyMode.parse(mode.id));
|
|
1761
|
+
}
|
|
1762
|
+
function assertLatencyModeRunnable(settings, modelId, latencyMode) {
|
|
1763
|
+
const runnable = runnableLatencyModesForModel(settings, modelId);
|
|
1764
|
+
if (!runnable.includes(latencyMode)) {
|
|
1765
|
+
throw new Error(
|
|
1766
|
+
`latency mode ${latencyMode} is not runnable for model ${modelId} (allowed: ${runnable.join(", ")})`
|
|
1767
|
+
);
|
|
1768
|
+
}
|
|
1769
|
+
}
|
|
1573
1770
|
function registryCredentialSource(provider) {
|
|
1574
1771
|
return provider.kind === "codex-subscription" ? { kind: "connected_subscription", provider: "codex" } : { kind: "deployment", mechanism: "api_key" };
|
|
1575
1772
|
}
|
|
@@ -1690,21 +1887,31 @@ function withCodexCatalogProvider(settings) {
|
|
|
1690
1887
|
label: "Codex (ChatGPT subscription)",
|
|
1691
1888
|
api: "responses",
|
|
1692
1889
|
baseUrl: CODEX_PROVIDER_BASE_URL,
|
|
1693
|
-
models: CODEX_FALLBACK_MODEL_SLUGS.map((slug) =>
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1890
|
+
models: CODEX_FALLBACK_MODEL_SLUGS.map((slug) => {
|
|
1891
|
+
const capabilities = {
|
|
1892
|
+
...legacyModelCapabilities(settings, {
|
|
1893
|
+
reasoningEffort: true,
|
|
1894
|
+
hostedWebSearch: true
|
|
1895
|
+
}),
|
|
1896
|
+
latencyModes: builtinLatencyModesForModel(`${CODEX_MODEL_ID_PREFIX}${slug}`)
|
|
1897
|
+
};
|
|
1898
|
+
return {
|
|
1899
|
+
id: `${CODEX_MODEL_ID_PREFIX}${slug}`,
|
|
1900
|
+
upstreamModelId: slug,
|
|
1901
|
+
label: productLabelForModelId(slug),
|
|
1902
|
+
reasoningEffort: true,
|
|
1903
|
+
// The ChatGPT/Codex Responses backend accepts the native web_search
|
|
1904
|
+
// hosted tool (unlike hosted apply_patch/computer transports). Declaring
|
|
1905
|
+
// this here makes provider resolution truthful; the worker still applies
|
|
1906
|
+
// the durable session/turn policy gate before attaching it.
|
|
1907
|
+
hostedWebSearch: true,
|
|
1908
|
+
capabilities,
|
|
1909
|
+
contextWindowTokens: CODEX_MODEL_CONTEXT_WINDOW_TOKENS,
|
|
1910
|
+
effectiveContextWindowTokens: CODEX_MODEL_EFFECTIVE_CONTEXT_WINDOW_TOKENS,
|
|
1911
|
+
autoCompactTokenLimit: CODEX_MODEL_AUTO_COMPACT_TOKEN_LIMIT,
|
|
1912
|
+
toolOutputTruncationTokens: CODEX_MODEL_TOOL_OUTPUT_TRUNCATION_TOKENS
|
|
1913
|
+
};
|
|
1914
|
+
})
|
|
1708
1915
|
};
|
|
1709
1916
|
return { ...settings, modelProvidersJson: JSON.stringify([...providers, provider]) };
|
|
1710
1917
|
}
|
|
@@ -1788,14 +1995,17 @@ function configuredModels(settings) {
|
|
|
1788
1995
|
settings.openaiModel,
|
|
1789
1996
|
...splitCsv(settings.openaiAllowedModels)
|
|
1790
1997
|
]).filter((id) => !isRegistryNamespaced(id)).map((id) => {
|
|
1791
|
-
const capabilities =
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1998
|
+
const capabilities = {
|
|
1999
|
+
...legacyModelCapabilities(settings, {
|
|
2000
|
+
reasoningEffort: true,
|
|
2001
|
+
hostedWebSearch: settings.webSearchEnabled
|
|
2002
|
+
}),
|
|
2003
|
+
latencyModes: builtinLatencyModesForModel(id)
|
|
2004
|
+
};
|
|
1795
2005
|
return finalizeConfiguredModel(settings, builtinProvider, {
|
|
1796
2006
|
id,
|
|
1797
2007
|
aliases: [],
|
|
1798
|
-
label: id,
|
|
2008
|
+
label: productLabelForModelId(id),
|
|
1799
2009
|
providerId: builtinId,
|
|
1800
2010
|
providerLabel: builtinLabel,
|
|
1801
2011
|
api: "responses",
|
|
@@ -1827,7 +2037,7 @@ function configuredModels(settings) {
|
|
|
1827
2037
|
finalizeConfiguredModel(settings, resolvedProvider, {
|
|
1828
2038
|
id: model.id,
|
|
1829
2039
|
aliases: [...model.aliases ?? []],
|
|
1830
|
-
label: model.label ?? model.id,
|
|
2040
|
+
label: model.label ?? productLabelForModelId(model.id),
|
|
1831
2041
|
providerId: provider.id,
|
|
1832
2042
|
providerLabel,
|
|
1833
2043
|
api: provider.api,
|
|
@@ -1888,6 +2098,9 @@ function resolveTurnExecutionPolicyV1(settings, input) {
|
|
|
1888
2098
|
if (input.requestedModelId !== null && canonicalizeConfiguredModelId(catalogSettings, input.requestedModelId) !== productModelId) {
|
|
1889
2099
|
throw new Error("Turn execution policy requested model does not canonicalize to its product");
|
|
1890
2100
|
}
|
|
2101
|
+
const latencyMode = LatencyMode.parse(input.latencyMode ?? "standard");
|
|
2102
|
+
const latencyModeSource = input.latencyModeSource ?? "deployment";
|
|
2103
|
+
assertLatencyModeRunnable(catalogSettings, productModelId, latencyMode);
|
|
1891
2104
|
return TurnExecutionPolicyV1.parse({
|
|
1892
2105
|
schemaVersion: 1,
|
|
1893
2106
|
productModelId,
|
|
@@ -1895,6 +2108,8 @@ function resolveTurnExecutionPolicyV1(settings, input) {
|
|
|
1895
2108
|
modelSource: input.modelSource,
|
|
1896
2109
|
reasoningEffort: input.reasoningEffort,
|
|
1897
2110
|
reasoningSource: input.reasoningSource,
|
|
2111
|
+
latencyMode,
|
|
2112
|
+
latencyModeSource,
|
|
1898
2113
|
providerId: resolved.provider.id,
|
|
1899
2114
|
upstreamModelId: resolved.model.upstreamModelId,
|
|
1900
2115
|
wireApi: resolved.model.api,
|
|
@@ -1907,9 +2122,13 @@ function assertTurnExecutionPolicyMatchesConfigV1(settings, policy, expected) {
|
|
|
1907
2122
|
const parsed = TurnExecutionPolicyV1.parse(policy);
|
|
1908
2123
|
const catalogSettings = settingsForTurnExecutionPolicy(settings, parsed.productModelId);
|
|
1909
2124
|
const canonicalExpectedModel = canonicalizeConfiguredModelId(catalogSettings, expected.modelId);
|
|
1910
|
-
|
|
1911
|
-
|
|
2125
|
+
const expectedLatencyMode = expected.latencyMode ?? parsed.latencyMode;
|
|
2126
|
+
if (parsed.productModelId !== canonicalExpectedModel || parsed.reasoningEffort !== expected.reasoningEffort || parsed.latencyMode !== expectedLatencyMode) {
|
|
2127
|
+
throw new Error(
|
|
2128
|
+
"Turn execution policy does not match the accepted turn model/reasoning/latency"
|
|
2129
|
+
);
|
|
1912
2130
|
}
|
|
2131
|
+
assertLatencyModeRunnable(catalogSettings, parsed.productModelId, parsed.latencyMode);
|
|
1913
2132
|
if (parsed.requestedModelId !== null && canonicalizeConfiguredModelId(catalogSettings, parsed.requestedModelId) !== parsed.productModelId) {
|
|
1914
2133
|
throw new Error("Turn execution policy requested model does not match its product model");
|
|
1915
2134
|
}
|
|
@@ -1925,7 +2144,10 @@ function assertTurnExecutionPolicyMatchesConfigV1(settings, policy, expected) {
|
|
|
1925
2144
|
}
|
|
1926
2145
|
function configuredModelPricingSchedules(settings) {
|
|
1927
2146
|
const defaults = Object.fromEntries(
|
|
1928
|
-
Object.entries(defaultModelPricing).map(([model, pricing]) => [
|
|
2147
|
+
Object.entries(defaultModelPricing).map(([model, pricing]) => [
|
|
2148
|
+
model,
|
|
2149
|
+
normalizeModelPricingSchedule(pricing)
|
|
2150
|
+
])
|
|
1929
2151
|
);
|
|
1930
2152
|
const registry = {};
|
|
1931
2153
|
for (const provider of parseModelProvidersJson(settings.modelProvidersJson)) {
|
|
@@ -2007,7 +2229,7 @@ function configuredEntitlements(settings) {
|
|
|
2007
2229
|
...configured
|
|
2008
2230
|
};
|
|
2009
2231
|
}
|
|
2010
|
-
function calculateModelUsageCostMicros(settings, model, usage) {
|
|
2232
|
+
function calculateModelUsageCostMicros(settings, model, usage, options) {
|
|
2011
2233
|
const schedule = configuredModelPricingSchedules(settings)[model];
|
|
2012
2234
|
if (!schedule) {
|
|
2013
2235
|
throw new Error(`Missing model pricing for ${model}`);
|
|
@@ -2026,6 +2248,20 @@ function calculateModelUsageCostMicros(settings, model, usage) {
|
|
|
2026
2248
|
const marginBps = pricing.marginBps ?? 0;
|
|
2027
2249
|
total += Math.ceil(rawCost * (1e4 + marginBps) / 1e4);
|
|
2028
2250
|
}
|
|
2251
|
+
const latencyMode = options?.latencyMode ?? "standard";
|
|
2252
|
+
if (latencyMode !== "standard") {
|
|
2253
|
+
const catalogSettings = settingsForTurnExecutionPolicy(settings, model);
|
|
2254
|
+
const resolved = resolveModelProvider(
|
|
2255
|
+
catalogSettings,
|
|
2256
|
+
canonicalizeConfiguredModelId(catalogSettings, model)
|
|
2257
|
+
);
|
|
2258
|
+
const multiplierBps = resolved?.model.capabilities.latencyModes.find(
|
|
2259
|
+
(mode) => mode.id === latencyMode && mode.runnable
|
|
2260
|
+
)?.billingMultiplierBps;
|
|
2261
|
+
if (multiplierBps && multiplierBps > 0) {
|
|
2262
|
+
total = Math.ceil(total * multiplierBps / 1e4);
|
|
2263
|
+
}
|
|
2264
|
+
}
|
|
2029
2265
|
return total;
|
|
2030
2266
|
}
|
|
2031
2267
|
function configuredAllowedReasoningEfforts(settings) {
|
|
@@ -2589,6 +2825,28 @@ function validateSettings(settings) {
|
|
|
2589
2825
|
);
|
|
2590
2826
|
}
|
|
2591
2827
|
}
|
|
2828
|
+
if (Boolean(settings.googleDriveClientId) !== Boolean(settings.googleDriveClientSecret)) {
|
|
2829
|
+
throw new Error(
|
|
2830
|
+
"OPENGENI_GOOGLE_DRIVE_CLIENT_ID and OPENGENI_GOOGLE_DRIVE_CLIENT_SECRET must be configured together"
|
|
2831
|
+
);
|
|
2832
|
+
}
|
|
2833
|
+
if (settings.googleDriveClientId) {
|
|
2834
|
+
if (!settings.publicBaseUrl) {
|
|
2835
|
+
throw new Error(
|
|
2836
|
+
"OPENGENI_PUBLIC_BASE_URL is required when the Google Drive integration is configured"
|
|
2837
|
+
);
|
|
2838
|
+
}
|
|
2839
|
+
if (!settings.publicBaseUrl.startsWith("https://") && !["local", "test"].includes(settings.environment)) {
|
|
2840
|
+
throw new Error(
|
|
2841
|
+
"OPENGENI_PUBLIC_BASE_URL must use https when the Google Drive integration is configured outside local/test"
|
|
2842
|
+
);
|
|
2843
|
+
}
|
|
2844
|
+
if (!settings.integrationsStateSecret) {
|
|
2845
|
+
throw new Error(
|
|
2846
|
+
"OPENGENI_INTEGRATIONS_STATE_SECRET is required when the Google Drive integration is configured"
|
|
2847
|
+
);
|
|
2848
|
+
}
|
|
2849
|
+
}
|
|
2592
2850
|
parseIntegrationsOauthClientsJson(settings.integrationsOauthClientsJson);
|
|
2593
2851
|
if (settings.productAccessMode === "configured" && !["local", "test"].includes(settings.environment) && !settings.delegationSecret && !settings.authRequired) {
|
|
2594
2852
|
throw new Error(
|
|
@@ -2672,7 +2930,7 @@ function validateSettings(settings) {
|
|
|
2672
2930
|
"OPENGENI_OBJECT_STORAGE_ACCESS_KEY_ID and OPENGENI_OBJECT_STORAGE_SECRET_ACCESS_KEY must both be set or both omitted"
|
|
2673
2931
|
);
|
|
2674
2932
|
}
|
|
2675
|
-
if (settings.objectStorageBackend === "s3-compatible" && (settings.objectStorageEndpoint || settings.objectStorageSandboxEndpoint) && (!settings.objectStorageAccessKeyId || !settings.objectStorageSecretAccessKey)) {
|
|
2933
|
+
if (settings.objectStorageBackend === "s3-compatible" && (settings.objectStorageEndpoint || settings.objectStorageInternalEndpoint || settings.objectStorageSandboxEndpoint) && (!settings.objectStorageAccessKeyId || !settings.objectStorageSecretAccessKey)) {
|
|
2676
2934
|
throw new Error(
|
|
2677
2935
|
"S3-compatible object storage endpoints require OPENGENI_OBJECT_STORAGE_ACCESS_KEY_ID and OPENGENI_OBJECT_STORAGE_SECRET_ACCESS_KEY"
|
|
2678
2936
|
);
|
|
@@ -2688,7 +2946,7 @@ function validateSettings(settings) {
|
|
|
2688
2946
|
);
|
|
2689
2947
|
}
|
|
2690
2948
|
} else if (settings.objectStorageBackend === "azure-blob") {
|
|
2691
|
-
if (settings.objectStorageEndpoint || settings.objectStorageSandboxEndpoint || settings.objectStorageAccessKeyId || settings.objectStorageSecretAccessKey) {
|
|
2949
|
+
if (settings.objectStorageEndpoint || settings.objectStorageInternalEndpoint || settings.objectStorageSandboxEndpoint || settings.objectStorageAccessKeyId || settings.objectStorageSecretAccessKey) {
|
|
2692
2950
|
throw new Error(
|
|
2693
2951
|
"Azure Blob storage uses OPENGENI_OBJECT_STORAGE_AZURE_* settings, not S3-compatible object storage settings"
|
|
2694
2952
|
);
|
|
@@ -2706,7 +2964,7 @@ function validateSettings(settings) {
|
|
|
2706
2964
|
);
|
|
2707
2965
|
}
|
|
2708
2966
|
} else {
|
|
2709
|
-
if (settings.objectStorageEndpoint || settings.objectStorageSandboxEndpoint || settings.objectStorageAccessKeyId || settings.objectStorageSecretAccessKey) {
|
|
2967
|
+
if (settings.objectStorageEndpoint || settings.objectStorageInternalEndpoint || settings.objectStorageSandboxEndpoint || settings.objectStorageAccessKeyId || settings.objectStorageSecretAccessKey) {
|
|
2710
2968
|
throw new Error(
|
|
2711
2969
|
"GCS object storage uses OPENGENI_OBJECT_STORAGE_GCS_* settings, not S3-compatible object storage settings"
|
|
2712
2970
|
);
|
|
@@ -2741,6 +2999,7 @@ function validateSettings(settings) {
|
|
|
2741
2999
|
const viewerTtl = settings.sandboxViewerHolderTtlMs;
|
|
2742
3000
|
const idleGraceMs = settings.sandboxIdleGraceMs;
|
|
2743
3001
|
const providerLifetimeMs = settings.modalTimeoutSeconds * 1e3;
|
|
3002
|
+
const rotationLeadMs = settings.sandboxRotationLeadMs;
|
|
2744
3003
|
const idleTimeoutMs = effectiveModalIdleTimeoutSeconds(settings) * 1e3;
|
|
2745
3004
|
if (!(reaperPeriod < viewerTtl)) {
|
|
2746
3005
|
throw new Error(
|
|
@@ -2752,6 +3011,16 @@ function validateSettings(settings) {
|
|
|
2752
3011
|
`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.`
|
|
2753
3012
|
);
|
|
2754
3013
|
}
|
|
3014
|
+
if (!(rotationLeadMs < providerLifetimeMs)) {
|
|
3015
|
+
throw new Error(
|
|
3016
|
+
`OPENGENI_SANDBOX_ROTATION_LEAD_MS (${rotationLeadMs}) must be strictly less than OPENGENI_MODAL_TIMEOUT_SECONDS*1000 (${providerLifetimeMs}).`
|
|
3017
|
+
);
|
|
3018
|
+
}
|
|
3019
|
+
if (!(rotationLeadMs > settings.sandboxSnapshotTimeoutMs + 2 * reaperPeriod)) {
|
|
3020
|
+
throw new Error(
|
|
3021
|
+
`OPENGENI_SANDBOX_ROTATION_LEAD_MS (${rotationLeadMs}) must exceed the snapshot timeout plus two reaper periods (${settings.sandboxSnapshotTimeoutMs + 2 * reaperPeriod}).`
|
|
3022
|
+
);
|
|
3023
|
+
}
|
|
2755
3024
|
if (!(viewerTtl < idleTimeoutMs)) {
|
|
2756
3025
|
throw new Error(
|
|
2757
3026
|
`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).`
|
|
@@ -2909,6 +3178,7 @@ export {
|
|
|
2909
3178
|
getSettings,
|
|
2910
3179
|
hasGitCredentialRepositorySelection,
|
|
2911
3180
|
hasGitHubRepositorySelection,
|
|
3181
|
+
isUsableVoiceInputSecret,
|
|
2912
3182
|
parseExposedPorts,
|
|
2913
3183
|
parseIntegrationsOauthClientsJson,
|
|
2914
3184
|
parseMcpServers,
|
|
@@ -2918,6 +3188,7 @@ export {
|
|
|
2918
3188
|
parseStaticEntitlementsJson,
|
|
2919
3189
|
parseStaticUsageLimitsJson,
|
|
2920
3190
|
policyProviderIdForModel,
|
|
3191
|
+
productLabelForModelId,
|
|
2921
3192
|
requiredSandboxEnvForBackend,
|
|
2922
3193
|
resolveEnrollmentSigningSecret,
|
|
2923
3194
|
resolveModelProvider,
|
|
@@ -2927,17 +3198,23 @@ export {
|
|
|
2927
3198
|
resolveRelayTokenSecret,
|
|
2928
3199
|
resolveStreamTokenSecret,
|
|
2929
3200
|
resolveTurnExecutionPolicyV1,
|
|
3201
|
+
resolveVoiceInputProviderRegistry,
|
|
3202
|
+
responseSatisfiesLatencyMode,
|
|
2930
3203
|
retryStartupDependency,
|
|
3204
|
+
runnableLatencyModesForModel,
|
|
3205
|
+
sandboxArchiveCaptureTimeoutMs,
|
|
2931
3206
|
sandboxEnvironmentVariableNames,
|
|
2932
3207
|
sandboxLifecycleHookIds,
|
|
2933
3208
|
sandboxPreparationProfiles,
|
|
2934
3209
|
sandboxWarmRateMicrosPerSecond,
|
|
2935
3210
|
selectModelPricing,
|
|
3211
|
+
serviceTierForLatencyMode,
|
|
2936
3212
|
settingsWithResolvedModelContext,
|
|
2937
3213
|
stableSandboxEnvironmentForRun,
|
|
2938
3214
|
startupRetryOptions,
|
|
2939
3215
|
streamTokenDegraded,
|
|
2940
3216
|
temporalConnectionOptions,
|
|
3217
|
+
voiceInputDeploymentConfigured,
|
|
2941
3218
|
withCodexCatalogProvider
|
|
2942
3219
|
};
|
|
2943
3220
|
//# sourceMappingURL=index.js.map
|