omnigateway 0.13.6 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -0
- package/bin/omni.js +905 -111
- package/gateway.js +974 -189
- package/package.json +1 -1
- package/public/assets/{_app.console-facio1_r.js → _app.console-BgA6_yPW.js} +1 -1
- package/public/assets/_app.keys-CY65Ov9F.js +107 -0
- package/public/assets/{_app.plugins._pluginId-cCDDM_D_.js → _app.plugins._pluginId-B825jp0T.js} +1 -1
- package/public/assets/client-CkeRVeTV.js +39 -0
- package/public/assets/{index-BYDVTknB.js → index-Ug93No6D.js} +2 -2
- package/public/assets/limits-iRyDMg04.js +1 -0
- package/public/assets/{login-BhkdR1f4.js → login-BBURVBwb.js} +1 -1
- package/public/index.html +1 -1
- package/public/assets/_app.keys-Dq5O0AG9.js +0 -92
- package/public/assets/client-DEALADtD.js +0 -39
package/bin/omni.js
CHANGED
|
@@ -6025,12 +6025,43 @@ var spendWindows = object({
|
|
|
6025
6025
|
"5h": spendLimit.optional(),
|
|
6026
6026
|
"1w": spendLimit.optional()
|
|
6027
6027
|
}).strict();
|
|
6028
|
-
var
|
|
6028
|
+
var limitConfigBaseSchema = object({
|
|
6029
6029
|
requests: countWindows.optional(),
|
|
6030
6030
|
tokens: countWindows.optional(),
|
|
6031
6031
|
spend: spendWindows.optional(),
|
|
6032
6032
|
concurrency: countLimit.optional()
|
|
6033
6033
|
}).strict();
|
|
6034
|
+
var MAX_MODEL_OVERRIDES = 64;
|
|
6035
|
+
var MAX_MODEL_NAME_LENGTH = 256;
|
|
6036
|
+
function setsAnyLimit(scope) {
|
|
6037
|
+
if (scope.concurrency !== undefined && scope.concurrency !== null)
|
|
6038
|
+
return true;
|
|
6039
|
+
for (const dimension of ["requests", "tokens", "spend"]) {
|
|
6040
|
+
const windows = scope[dimension];
|
|
6041
|
+
if (windows === undefined)
|
|
6042
|
+
continue;
|
|
6043
|
+
for (const window of WINDOWS) {
|
|
6044
|
+
const limit = windows[window];
|
|
6045
|
+
if (limit !== undefined && limit !== null)
|
|
6046
|
+
return true;
|
|
6047
|
+
}
|
|
6048
|
+
}
|
|
6049
|
+
return false;
|
|
6050
|
+
}
|
|
6051
|
+
var modelOverrideSchema = limitConfigBaseSchema.refine(setsAnyLimit, {
|
|
6052
|
+
message: "a model override must set at least one limit; omit the model to remove it"
|
|
6053
|
+
});
|
|
6054
|
+
var limitConfigSchema = limitConfigBaseSchema.extend({
|
|
6055
|
+
models: record(string2().min(1).max(MAX_MODEL_NAME_LENGTH), modelOverrideSchema).refine((models) => Object.keys(models).length <= MAX_MODEL_OVERRIDES, {
|
|
6056
|
+
message: `at most ${MAX_MODEL_OVERRIDES} model overrides per key`
|
|
6057
|
+
}).optional()
|
|
6058
|
+
}).strict();
|
|
6059
|
+
function modelOverride(limits, model) {
|
|
6060
|
+
const models = limits.models;
|
|
6061
|
+
if (models === undefined || !Object.hasOwn(models, model))
|
|
6062
|
+
return;
|
|
6063
|
+
return models[model];
|
|
6064
|
+
}
|
|
6034
6065
|
function assertNoProto(value, path) {
|
|
6035
6066
|
if (typeof value !== "object" || value === null)
|
|
6036
6067
|
return;
|
|
@@ -7695,6 +7726,31 @@ var _005_log_filter_indexes_default = `-- An index for the provider filter on \`
|
|
|
7695
7726
|
CREATE INDEX idx_request_logs_provider ON request_logs (resolved_provider, at DESC, id DESC);
|
|
7696
7727
|
`;
|
|
7697
7728
|
|
|
7729
|
+
// packages/store/src/postgres/migrations/006_model_window_index.sql
|
|
7730
|
+
var _006_model_window_index_default = `-- The per-model long-window reads behind a key's model overrides, on this
|
|
7731
|
+
-- backend: \`usage.sumSince\`, \`sumBuckets\` and \`oldestSince\` narrowed to one
|
|
7732
|
+
-- requested model, read from the log because \`usage_rollup\` has no model
|
|
7733
|
+
-- column. The SQLite copy is \`018_model_window_index.sql\`, same shape.
|
|
7734
|
+
--
|
|
7735
|
+
-- Without it the planner has no prefix to seek and the narrowed sums plan as a
|
|
7736
|
+
-- parallel sequential scan of the table. Measured at 500,000 rows in one key's
|
|
7737
|
+
-- week, a fifth of them the overridden model and a hundred rows of a rare one:
|
|
7738
|
+
--
|
|
7739
|
+
-- read without index with index
|
|
7740
|
+
-- sumSince 1w, rare model 22.6ms 0.22ms
|
|
7741
|
+
-- sumSince 1w, 100k-row model 27.1ms 29.4ms
|
|
7742
|
+
-- sumBuckets 1w hour, rare 78.2ms 0.26ms
|
|
7743
|
+
-- sumBuckets 1w hour, 100k-row 89.4ms 55.5ms
|
|
7744
|
+
-- oldestSince 1w, rare 0.58ms 0.11ms
|
|
7745
|
+
--
|
|
7746
|
+
-- The heavy-model sum is a bitmap heap scan over that model's rows and does not
|
|
7747
|
+
-- beat the sequential scan it replaces: the cost is linear in the model's own
|
|
7748
|
+
-- traffic on either plan, and this index removes the part that was linear in
|
|
7749
|
+
-- everyone else's. 15MB at 500,000 rows, against 19MB for the key/time index.
|
|
7750
|
+
-- Neither partial nor covering, for the reasons the SQLite file records.
|
|
7751
|
+
CREATE INDEX idx_request_logs_key_model_at ON request_logs (api_key_id, requested_model, at);
|
|
7752
|
+
`;
|
|
7753
|
+
|
|
7698
7754
|
// packages/store/src/postgres/db.ts
|
|
7699
7755
|
var num = (v) => Number(v);
|
|
7700
7756
|
var numOrNull = (v) => v === null ? null : Number(v);
|
|
@@ -7704,7 +7760,8 @@ var MIGRATIONS = [
|
|
|
7704
7760
|
{ id: 2, sql: _002_health_measurements_default },
|
|
7705
7761
|
{ id: 3, sql: _003_key_expiry_default },
|
|
7706
7762
|
{ id: 4, sql: _004_log_keyset_default },
|
|
7707
|
-
{ id: 5, sql: _005_log_filter_indexes_default }
|
|
7763
|
+
{ id: 5, sql: _005_log_filter_indexes_default },
|
|
7764
|
+
{ id: 6, sql: _006_model_window_index_default }
|
|
7708
7765
|
];
|
|
7709
7766
|
async function openPg(url) {
|
|
7710
7767
|
const sql = new SQL({ url });
|
|
@@ -9069,6 +9126,24 @@ var sum = (row) => ({
|
|
|
9069
9126
|
tokens: row === undefined ? 0 : num(row.tokens),
|
|
9070
9127
|
costUsd: row === undefined ? 0 : row.cost_usd
|
|
9071
9128
|
});
|
|
9129
|
+
var NARROWED_SQL = {
|
|
9130
|
+
sumSince: `SELECT COUNT(*) AS requests,
|
|
9131
|
+
COALESCE(SUM(input_tokens + output_tokens
|
|
9132
|
+
+ cache_read_tokens + cache_write_tokens), 0) AS tokens,
|
|
9133
|
+
COALESCE(SUM(cost_usd), 0) AS cost_usd
|
|
9134
|
+
FROM request_logs
|
|
9135
|
+
WHERE api_key_id = $1 AND requested_model = $2 AND state = 'done' AND at >= $3`,
|
|
9136
|
+
sumBuckets: `SELECT (at / $1) * $2 AS bucket, COUNT(*) AS requests,
|
|
9137
|
+
COALESCE(SUM(input_tokens + output_tokens
|
|
9138
|
+
+ cache_read_tokens + cache_write_tokens), 0) AS tokens,
|
|
9139
|
+
COALESCE(SUM(cost_usd), 0) AS cost_usd
|
|
9140
|
+
FROM request_logs
|
|
9141
|
+
WHERE api_key_id = $3 AND requested_model = $4 AND state = 'done' AND at >= $5
|
|
9142
|
+
GROUP BY 1`,
|
|
9143
|
+
oldestSince: `SELECT MIN(at) AS at
|
|
9144
|
+
FROM request_logs
|
|
9145
|
+
WHERE api_key_id = $1 AND requested_model = $2 AND state = 'done' AND at >= $3`
|
|
9146
|
+
};
|
|
9072
9147
|
function createUsageRepo(sql, nodeId, dayOffsetMinutes) {
|
|
9073
9148
|
const complete = async (conn, log) => {
|
|
9074
9149
|
const written = (await conn.unsafe(COMPLETE, values(log, "done", nodeId))).count;
|
|
@@ -9126,7 +9201,15 @@ function createUsageRepo(sql, nodeId, dayOffsetMinutes) {
|
|
|
9126
9201
|
ORDER BY at, id LIMIT $3`, [cursor.at, cursor.id, limit]);
|
|
9127
9202
|
return rows.map(toLog);
|
|
9128
9203
|
},
|
|
9129
|
-
async sumSince(apiKeyId, sinceMs) {
|
|
9204
|
+
async sumSince(apiKeyId, sinceMs, requestedModel) {
|
|
9205
|
+
if (requestedModel !== undefined) {
|
|
9206
|
+
const narrowed = await sql.unsafe(NARROWED_SQL.sumSince, [
|
|
9207
|
+
apiKeyId,
|
|
9208
|
+
requestedModel,
|
|
9209
|
+
sinceMs
|
|
9210
|
+
]);
|
|
9211
|
+
return sum(narrowed[0]);
|
|
9212
|
+
}
|
|
9130
9213
|
const boundary = hourOf(sinceMs);
|
|
9131
9214
|
const whole = await sql.unsafe(`SELECT COALESCE(SUM(requests), 0) AS requests,
|
|
9132
9215
|
COALESCE(SUM(input_tokens + output_tokens
|
|
@@ -9148,7 +9231,18 @@ function createUsageRepo(sql, nodeId, dayOffsetMinutes) {
|
|
|
9148
9231
|
costUsd: a.costUsd + b.costUsd
|
|
9149
9232
|
};
|
|
9150
9233
|
},
|
|
9151
|
-
async sumBuckets(apiKeyId, sinceMs, grainMs) {
|
|
9234
|
+
async sumBuckets(apiKeyId, sinceMs, grainMs, requestedModel) {
|
|
9235
|
+
if (requestedModel !== undefined) {
|
|
9236
|
+
const from = grainMs === HOUR_MS ? hourOf(sinceMs) * HOUR_MS : sinceMs;
|
|
9237
|
+
const narrowed = await sql.unsafe(NARROWED_SQL.sumBuckets, [
|
|
9238
|
+
grainMs,
|
|
9239
|
+
grainMs,
|
|
9240
|
+
apiKeyId,
|
|
9241
|
+
requestedModel,
|
|
9242
|
+
from
|
|
9243
|
+
]);
|
|
9244
|
+
return narrowed.map((row) => [num(row.bucket), sum(row)]);
|
|
9245
|
+
}
|
|
9152
9246
|
const rows = grainMs === HOUR_MS ? await sql.unsafe(`SELECT hour * ${HOUR_MS} AS bucket, requests,
|
|
9153
9247
|
input_tokens + output_tokens + cache_read_tokens + cache_write_tokens
|
|
9154
9248
|
AS tokens,
|
|
@@ -9169,10 +9263,14 @@ function createUsageRepo(sql, nodeId, dayOffsetMinutes) {
|
|
|
9169
9263
|
async auditRollup() {
|
|
9170
9264
|
return auditRollup2(sql);
|
|
9171
9265
|
},
|
|
9172
|
-
async oldestSince(apiKeyId, sinceMs) {
|
|
9173
|
-
const row = (await sql.unsafe(`SELECT MIN(at) AS at
|
|
9174
|
-
|
|
9175
|
-
|
|
9266
|
+
async oldestSince(apiKeyId, sinceMs, requestedModel) {
|
|
9267
|
+
const row = (requestedModel === undefined ? await sql.unsafe(`SELECT MIN(at) AS at
|
|
9268
|
+
FROM request_logs
|
|
9269
|
+
WHERE api_key_id = $1 AND state = 'done' AND at >= $2`, [apiKeyId, sinceMs]) : await sql.unsafe(NARROWED_SQL.oldestSince, [
|
|
9270
|
+
apiKeyId,
|
|
9271
|
+
requestedModel,
|
|
9272
|
+
sinceMs
|
|
9273
|
+
]))[0];
|
|
9176
9274
|
return row === undefined ? null : numOrNull(row.at);
|
|
9177
9275
|
},
|
|
9178
9276
|
async aggregate(q) {
|
|
@@ -10454,6 +10552,42 @@ CREATE INDEX idx_request_logs_at
|
|
|
10454
10552
|
ON request_logs (at DESC, id DESC, requested_model, resolved_model, error_code);
|
|
10455
10553
|
`;
|
|
10456
10554
|
|
|
10555
|
+
// packages/store/src/sqlite/migrations/018_model_window_index.sql
|
|
10556
|
+
var _018_model_window_index_default = `-- An index for the per-model long-window reads behind a key's model overrides:
|
|
10557
|
+
-- \`usage.sumSince\`, \`sumBuckets\` and \`oldestSince\` narrowed to one requested
|
|
10558
|
+
-- model. \`usage_rollup\` has no model column, so those reads come from the log,
|
|
10559
|
+
-- and without this they walk \`idx_request_logs_key_at\` across the key's whole
|
|
10560
|
+
-- window and reject every other model's row one at a time. \`bun:sqlite\` is
|
|
10561
|
+
-- synchronous, so that walk is the event loop \u2014 and the console's key listing
|
|
10562
|
+
-- repeats it once per override per window on every fetch.
|
|
10563
|
+
--
|
|
10564
|
+
-- Key, then model, then time: one seek lands on one key's one model, and the
|
|
10565
|
+
-- window is a range from there. Measured at 500,000 rows in one key's week, a
|
|
10566
|
+
-- fifth of them the overridden model and a hundred rows of a rare one:
|
|
10567
|
+
--
|
|
10568
|
+
-- read without index with index
|
|
10569
|
+
-- sumSince 1w, rare model 45.9ms 0.03ms
|
|
10570
|
+
-- sumSince 1w, 100k-row model 56.1ms 38.1ms
|
|
10571
|
+
-- sumBuckets 1w hour, rare 46.1ms 0.07ms
|
|
10572
|
+
-- sumBuckets 1w hour, 100k-row 76.8ms 58.9ms
|
|
10573
|
+
-- oldestSince 1w, rare 0.57ms 0.00ms
|
|
10574
|
+
--
|
|
10575
|
+
-- So the read stops costing the key's traffic and starts costing the model's
|
|
10576
|
+
-- own, which is still linear: a model that carries most of a key's traffic pays
|
|
10577
|
+
-- most of what it did before. That remaining cost is the design's, stated in
|
|
10578
|
+
-- \`ARCHITECTURE.md#rate-limiting\`, not something this index claims to remove.
|
|
10579
|
+
--
|
|
10580
|
+
-- Not partial on \`state = 'done'\`, and not covering, both measured. Partial
|
|
10581
|
+
-- bought nothing (37.4ms against 38.1ms) and, once \`ANALYZE\` had run, the
|
|
10582
|
+
-- planner moved the key-wide edge read onto it as a skip-scan over every model \u2014
|
|
10583
|
+
-- a plan that degrades with the number of models a key calls. Covering the four
|
|
10584
|
+
-- token columns and cost cut the heavy case to 9.0ms, for an index nearly three
|
|
10585
|
+
-- times as wide written on every completed request, and took the same key-wide
|
|
10586
|
+
-- plan. This shape leaves every key-wide read on \`idx_request_logs_key_at\`,
|
|
10587
|
+
-- which \`migrations.test.ts\` asserts beside the narrowed plans.
|
|
10588
|
+
CREATE INDEX idx_request_logs_key_model_at ON request_logs (api_key_id, requested_model, at);
|
|
10589
|
+
`;
|
|
10590
|
+
|
|
10457
10591
|
// packages/store/src/sqlite/db.ts
|
|
10458
10592
|
var MIGRATIONS2 = [
|
|
10459
10593
|
{ id: 1, sql: _001_init_default2 },
|
|
@@ -10472,7 +10606,8 @@ var MIGRATIONS2 = [
|
|
|
10472
10606
|
{ id: 14, sql: _014_key_expiry_default },
|
|
10473
10607
|
{ id: 15, sql: _015_log_keyset_default },
|
|
10474
10608
|
{ id: 16, sql: _016_log_filter_indexes_default },
|
|
10475
|
-
{ id: 17, sql: _017_log_text_search_default }
|
|
10609
|
+
{ id: 17, sql: _017_log_text_search_default },
|
|
10610
|
+
{ id: 18, sql: _018_model_window_index_default }
|
|
10476
10611
|
];
|
|
10477
10612
|
function statements(sql) {
|
|
10478
10613
|
return sql.split(`
|
|
@@ -10916,6 +11051,24 @@ var COMPLETE2 = `INSERT INTO request_logs ${COLUMNS2} VALUES ${PLACEHOLDERS2}
|
|
|
10916
11051
|
rtk_estimated_tokens_saved = excluded.rtk_estimated_tokens_saved,
|
|
10917
11052
|
rtk_filters = excluded.rtk_filters
|
|
10918
11053
|
WHERE request_logs.state = 'pending'`;
|
|
11054
|
+
var NARROWED_SQL2 = {
|
|
11055
|
+
sumSince: `SELECT COUNT(*) AS requests,
|
|
11056
|
+
COALESCE(SUM(input_tokens + output_tokens
|
|
11057
|
+
+ cache_read_tokens + cache_write_tokens), 0) AS tokens,
|
|
11058
|
+
COALESCE(SUM(cost_usd), 0) AS cost_usd
|
|
11059
|
+
FROM request_logs
|
|
11060
|
+
WHERE api_key_id = ? AND requested_model = ? AND state = 'done' AND at >= ?`,
|
|
11061
|
+
sumBuckets: `SELECT (at / ?) * ? AS bucket, COUNT(*) AS requests,
|
|
11062
|
+
COALESCE(SUM(input_tokens + output_tokens
|
|
11063
|
+
+ cache_read_tokens + cache_write_tokens), 0) AS tokens,
|
|
11064
|
+
COALESCE(SUM(cost_usd), 0) AS cost_usd
|
|
11065
|
+
FROM request_logs
|
|
11066
|
+
WHERE api_key_id = ? AND requested_model = ? AND state = 'done' AND at >= ?
|
|
11067
|
+
GROUP BY bucket`,
|
|
11068
|
+
oldestSince: `SELECT MIN(at) AS at
|
|
11069
|
+
FROM request_logs
|
|
11070
|
+
WHERE api_key_id = ? AND requested_model = ? AND state = 'done' AND at >= ?`
|
|
11071
|
+
};
|
|
10919
11072
|
function createUsageRepo2(db, nodeId, dayOffsetMinutes = hostDayOffsetMinutes()) {
|
|
10920
11073
|
const complete = db.transaction((log) => {
|
|
10921
11074
|
if (db.run(COMPLETE2, values2(log, "done", nodeId)).changes === 0)
|
|
@@ -10988,7 +11141,15 @@ function createUsageRepo2(db, nodeId, dayOffsetMinutes = hostDayOffsetMinutes())
|
|
|
10988
11141
|
ORDER BY at, id LIMIT ?`).all(cursor.at, cursor.at, cursor.id, limit);
|
|
10989
11142
|
return rows.map(toLog2);
|
|
10990
11143
|
},
|
|
10991
|
-
async sumSince(apiKeyId, sinceMs) {
|
|
11144
|
+
async sumSince(apiKeyId, sinceMs, requestedModel) {
|
|
11145
|
+
if (requestedModel !== undefined) {
|
|
11146
|
+
const row = db.query(NARROWED_SQL2.sumSince).get(apiKeyId, requestedModel, sinceMs);
|
|
11147
|
+
return {
|
|
11148
|
+
requests: row?.requests ?? 0,
|
|
11149
|
+
tokens: row?.tokens ?? 0,
|
|
11150
|
+
costUsd: row?.cost_usd ?? 0
|
|
11151
|
+
};
|
|
11152
|
+
}
|
|
10992
11153
|
const boundary = hourOf(sinceMs);
|
|
10993
11154
|
const whole = db.query(`SELECT COALESCE(SUM(requests), 0) AS requests,
|
|
10994
11155
|
COALESCE(SUM(input_tokens + output_tokens
|
|
@@ -11008,7 +11169,15 @@ function createUsageRepo2(db, nodeId, dayOffsetMinutes = hostDayOffsetMinutes())
|
|
|
11008
11169
|
costUsd: (whole?.cost_usd ?? 0) + (edge?.cost_usd ?? 0)
|
|
11009
11170
|
};
|
|
11010
11171
|
},
|
|
11011
|
-
async sumBuckets(apiKeyId, sinceMs, grainMs) {
|
|
11172
|
+
async sumBuckets(apiKeyId, sinceMs, grainMs, requestedModel) {
|
|
11173
|
+
if (requestedModel !== undefined) {
|
|
11174
|
+
const from = grainMs === HOUR_MS ? hourOf(sinceMs) * HOUR_MS : sinceMs;
|
|
11175
|
+
const narrowed = db.query(NARROWED_SQL2.sumBuckets).all(grainMs, grainMs, apiKeyId, requestedModel, from);
|
|
11176
|
+
return narrowed.map((row) => [
|
|
11177
|
+
row.bucket,
|
|
11178
|
+
{ requests: row.requests, tokens: row.tokens, costUsd: row.cost_usd }
|
|
11179
|
+
]);
|
|
11180
|
+
}
|
|
11012
11181
|
const rows = grainMs === HOUR_MS ? db.query(`SELECT hour * ${HOUR_MS} AS bucket, requests,
|
|
11013
11182
|
input_tokens + output_tokens + cache_read_tokens + cache_write_tokens
|
|
11014
11183
|
AS tokens,
|
|
@@ -11032,10 +11201,10 @@ function createUsageRepo2(db, nodeId, dayOffsetMinutes = hostDayOffsetMinutes())
|
|
|
11032
11201
|
async auditRollup() {
|
|
11033
11202
|
return auditRollup(db);
|
|
11034
11203
|
},
|
|
11035
|
-
async oldestSince(apiKeyId, sinceMs) {
|
|
11036
|
-
const row = db.query(`SELECT MIN(at) AS at
|
|
11037
|
-
|
|
11038
|
-
|
|
11204
|
+
async oldestSince(apiKeyId, sinceMs, requestedModel) {
|
|
11205
|
+
const row = requestedModel === undefined ? db.query(`SELECT MIN(at) AS at
|
|
11206
|
+
FROM request_logs
|
|
11207
|
+
WHERE api_key_id = ? AND state = 'done' AND at >= ?`).get(apiKeyId, sinceMs) : db.query(NARROWED_SQL2.oldestSince).get(apiKeyId, requestedModel, sinceMs);
|
|
11039
11208
|
return row?.at ?? null;
|
|
11040
11209
|
},
|
|
11041
11210
|
async aggregate(q) {
|
|
@@ -11176,9 +11345,9 @@ async function createStore(opts) {
|
|
|
11176
11345
|
lastUsedByCredential: (credentialId) => handle.usage.lastUsedByCredential(credentialId),
|
|
11177
11346
|
scan: (cursor, limit) => handle.usage.scan(cursor, limit),
|
|
11178
11347
|
aggregate: (q) => handle.usage.aggregate(q),
|
|
11179
|
-
sumSince: (apiKeyId, sinceMs) => handle.usage.sumSince(apiKeyId, sinceMs),
|
|
11180
|
-
sumBuckets: (apiKeyId, sinceMs, grainMs) => handle.usage.sumBuckets(apiKeyId, sinceMs, grainMs),
|
|
11181
|
-
oldestSince: (apiKeyId, sinceMs) => handle.usage.oldestSince(apiKeyId, sinceMs),
|
|
11348
|
+
sumSince: (apiKeyId, sinceMs, requestedModel) => handle.usage.sumSince(apiKeyId, sinceMs, requestedModel),
|
|
11349
|
+
sumBuckets: (apiKeyId, sinceMs, grainMs, requestedModel) => handle.usage.sumBuckets(apiKeyId, sinceMs, grainMs, requestedModel),
|
|
11350
|
+
oldestSince: (apiKeyId, sinceMs, requestedModel) => handle.usage.oldestSince(apiKeyId, sinceMs, requestedModel),
|
|
11182
11351
|
rebuildRollup: () => handle.usage.rebuildRollup(),
|
|
11183
11352
|
auditRollup: () => handle.usage.auditRollup(),
|
|
11184
11353
|
prune: (olderThan) => handle.usage.prune(olderThan),
|
|
@@ -12289,6 +12458,22 @@ function codecFailure(id, hook, what) {
|
|
|
12289
12458
|
gatewayAuthored: true
|
|
12290
12459
|
});
|
|
12291
12460
|
}
|
|
12461
|
+
function framingAgrees(headers, body, bodyEncoding) {
|
|
12462
|
+
const named = (name) => headers.filter(([n]) => n.toLowerCase() === name);
|
|
12463
|
+
const lengths = named("content-length");
|
|
12464
|
+
const encodings = named("content-encoding");
|
|
12465
|
+
if (bodyEncoding !== undefined) {
|
|
12466
|
+
return lengths.length === 0 && encodings.length === 1 && encodings[0]?.[1].trim().toLowerCase() === bodyEncoding;
|
|
12467
|
+
}
|
|
12468
|
+
if (lengths.length > 1 || encodings.length > 1)
|
|
12469
|
+
return false;
|
|
12470
|
+
const length = lengths[0]?.[1];
|
|
12471
|
+
const bytes = new TextEncoder().encode(body).byteLength;
|
|
12472
|
+
if (length !== undefined && length !== String(bytes))
|
|
12473
|
+
return false;
|
|
12474
|
+
const encoding = encodings[0]?.[1];
|
|
12475
|
+
return encoding === undefined || encoding.trim().toLowerCase() === "identity";
|
|
12476
|
+
}
|
|
12292
12477
|
function boundedDegradations(entries) {
|
|
12293
12478
|
if (!Array.isArray(entries))
|
|
12294
12479
|
return [];
|
|
@@ -12345,23 +12530,26 @@ function codecAdapter(id, capabilities, codec, origins) {
|
|
|
12345
12530
|
fail,
|
|
12346
12531
|
...req.requestId === undefined ? {} : { requestId: req.requestId },
|
|
12347
12532
|
...req.autoCache === undefined ? {} : { autoCacheEnabled: req.autoCache },
|
|
12348
|
-
...req.maxOutputTokens === undefined ? {} : { maxOutputTokens: req.maxOutputTokens }
|
|
12533
|
+
...req.maxOutputTokens === undefined ? {} : { maxOutputTokens: req.maxOutputTokens },
|
|
12534
|
+
...req.requestStartedAt === undefined ? {} : { requestStartedAt: req.requestStartedAt }
|
|
12349
12535
|
}));
|
|
12350
12536
|
const described = built?.request ?? {};
|
|
12351
12537
|
const request = {
|
|
12352
12538
|
url: described.url,
|
|
12353
12539
|
method: described.method,
|
|
12354
12540
|
headers: Array.isArray(described.headers) ? [...described.headers] : described.headers,
|
|
12355
|
-
body: described.body
|
|
12541
|
+
body: described.body,
|
|
12542
|
+
bodyEncoding: described.bodyEncoding
|
|
12356
12543
|
};
|
|
12357
|
-
if (typeof request.body !== "string" || !isSendableUrl(request.url) || !isHttpMethod(request.method) || !Array.isArray(request.headers) || !request.headers.every((pair) => Array.isArray(pair) && pair.length === 2 && typeof pair[0] === "string" && typeof pair[1] === "string")) {
|
|
12544
|
+
if (typeof request.body !== "string" || request.bodyEncoding !== undefined && request.bodyEncoding !== "zstd" || !isSendableUrl(request.url) || !isHttpMethod(request.method) || !Array.isArray(request.headers) || !request.headers.every((pair) => Array.isArray(pair) && pair.length === 2 && typeof pair[0] === "string" && typeof pair[1] === "string") || !framingAgrees(request.headers, request.body, request.bodyEncoding)) {
|
|
12358
12545
|
throw codecFailure(id, "buildRequest", "did not return a usable request");
|
|
12359
12546
|
}
|
|
12360
12547
|
const sendable = {
|
|
12361
12548
|
url: request.url,
|
|
12362
12549
|
method: request.method,
|
|
12363
12550
|
headers: request.headers,
|
|
12364
|
-
body: request.body
|
|
12551
|
+
body: request.body,
|
|
12552
|
+
...request.bodyEncoding === "zstd" ? { bodyEncoding: request.bodyEncoding } : {}
|
|
12365
12553
|
};
|
|
12366
12554
|
if (origins !== undefined && !withinOrigins(request.url, origins)) {
|
|
12367
12555
|
throw codecFailure(id, "buildRequest", "described a request to an origin its manifest does not declare");
|
|
@@ -12719,53 +12907,61 @@ var museBodyOrder = [
|
|
|
12719
12907
|
];
|
|
12720
12908
|
|
|
12721
12909
|
// packages/providers/src/openai/profile.ts
|
|
12722
|
-
var OPENAI_CLI_VERSION = env("OMNI_OPENAI_CLI_VERSION", "0.
|
|
12910
|
+
var OPENAI_CLI_VERSION = env("OMNI_OPENAI_CLI_VERSION", "0.163.0");
|
|
12723
12911
|
var OPENAI_UA_PLATFORM = env("OMNI_OPENAI_UA_PLATFORM", "Windows 10.0.26200");
|
|
12724
12912
|
var OPENAI_UA_ARCH = env("OMNI_OPENAI_UA_ARCH", "x86_64");
|
|
12725
12913
|
var OPENAI_UA_TERMINAL = env("OMNI_OPENAI_UA_TERMINAL", "WindowsTerminal");
|
|
12726
|
-
var
|
|
12914
|
+
var OPENAI_ORIGINATOR = env("OMNI_OPENAI_ORIGINATOR", "codex-tui");
|
|
12915
|
+
var OPENAI_SANDBOX = OPENAI_UA_PLATFORM.startsWith("Windows") ? "windows_elevated" : /^(Mac OS|macOS)/.test(OPENAI_UA_PLATFORM) ? "seatbelt" : "seccomp";
|
|
12727
12916
|
var openaiProfile = {
|
|
12728
12917
|
headers: [
|
|
12729
12918
|
[
|
|
12730
12919
|
"user-agent",
|
|
12731
|
-
env("OMNI_UA_OPENAI", `${
|
|
12920
|
+
env("OMNI_UA_OPENAI", `${OPENAI_ORIGINATOR}/${OPENAI_CLI_VERSION} (${OPENAI_UA_PLATFORM}; ${OPENAI_UA_ARCH}) ` + `${OPENAI_UA_TERMINAL} (${OPENAI_ORIGINATOR}; ${OPENAI_CLI_VERSION})`)
|
|
12732
12921
|
],
|
|
12733
|
-
["originator",
|
|
12922
|
+
["originator", OPENAI_ORIGINATOR],
|
|
12734
12923
|
["version", OPENAI_CLI_VERSION],
|
|
12735
12924
|
["x-codex-beta-features", "remote_compaction_v2"],
|
|
12736
12925
|
["accept", "text/event-stream"]
|
|
12737
12926
|
],
|
|
12738
12927
|
order: envOrder("OMNI_ORDER_OPENAI", [
|
|
12739
|
-
"host",
|
|
12740
12928
|
"version",
|
|
12741
12929
|
"x-codex-beta-features",
|
|
12742
|
-
"
|
|
12930
|
+
"x-codex-window-id",
|
|
12931
|
+
"x-codex-turn-metadata",
|
|
12932
|
+
"x-openai-internal-codex-responses-lite",
|
|
12933
|
+
"x-codex-routing-hint",
|
|
12743
12934
|
"x-client-request-id",
|
|
12744
12935
|
"session-id",
|
|
12745
12936
|
"thread-id",
|
|
12746
12937
|
"accept",
|
|
12938
|
+
"content-encoding",
|
|
12747
12939
|
"content-type",
|
|
12748
12940
|
"authorization",
|
|
12749
12941
|
"chatgpt-account-id",
|
|
12942
|
+
"originator",
|
|
12750
12943
|
"user-agent",
|
|
12751
|
-
"
|
|
12944
|
+
"host",
|
|
12752
12945
|
"content-length"
|
|
12753
12946
|
])
|
|
12754
12947
|
};
|
|
12755
12948
|
var openaiBodyOrder = [
|
|
12756
12949
|
"model",
|
|
12757
12950
|
"stream",
|
|
12951
|
+
"service_tier",
|
|
12758
12952
|
"input",
|
|
12759
12953
|
"instructions",
|
|
12760
|
-
"store",
|
|
12761
|
-
"reasoning",
|
|
12762
|
-
"prompt_cache_key",
|
|
12763
12954
|
"tools",
|
|
12764
12955
|
"tool_choice",
|
|
12956
|
+
"parallel_tool_calls",
|
|
12957
|
+
"reasoning",
|
|
12958
|
+
"store",
|
|
12959
|
+
"stream_options",
|
|
12765
12960
|
"include",
|
|
12766
|
-
"
|
|
12961
|
+
"prompt_cache_key",
|
|
12962
|
+
"text",
|
|
12767
12963
|
"client_metadata",
|
|
12768
|
-
"
|
|
12964
|
+
"access_programs",
|
|
12769
12965
|
"metadata"
|
|
12770
12966
|
];
|
|
12771
12967
|
|
|
@@ -16561,6 +16757,81 @@ function parseMuseUsage(value, now) {
|
|
|
16561
16757
|
]);
|
|
16562
16758
|
}
|
|
16563
16759
|
|
|
16760
|
+
// packages/providers/src/openai/codexIdentity.ts
|
|
16761
|
+
import { createHash as createHash3, createHmac } from "crypto";
|
|
16762
|
+
var NAMESPACE_OID = "6ba7b812-9dad-11d1-80b4-00c04fd430c8";
|
|
16763
|
+
var UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
16764
|
+
var WINDOW_END_MS = Date.UTC(2026, 9, 1);
|
|
16765
|
+
var WINDOW_MS2 = 365 * 24 * 60 * 60 * 1000;
|
|
16766
|
+
function isUuid(value) {
|
|
16767
|
+
return UUID.test(value);
|
|
16768
|
+
}
|
|
16769
|
+
function format(bytes) {
|
|
16770
|
+
const hex = Buffer.from(bytes).toString("hex");
|
|
16771
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20, 32)}`;
|
|
16772
|
+
}
|
|
16773
|
+
function withVersion(bytes, version) {
|
|
16774
|
+
const out = Uint8Array.from(bytes.subarray(0, 16));
|
|
16775
|
+
out[6] = (out[6] ?? 0) & 15 | version << 4;
|
|
16776
|
+
out[8] = (out[8] ?? 0) & 63 | 128;
|
|
16777
|
+
return out;
|
|
16778
|
+
}
|
|
16779
|
+
var sha256 = (seed) => createHash3("sha256").update(seed).digest();
|
|
16780
|
+
function uuidV5(namespace, name) {
|
|
16781
|
+
const digest = createHash3("sha1").update(Buffer.from(namespace.replace(/-/g, ""), "hex")).update(name, "utf8").digest();
|
|
16782
|
+
return format(withVersion(digest, 5));
|
|
16783
|
+
}
|
|
16784
|
+
function uuidV4From(seed) {
|
|
16785
|
+
return format(withVersion(sha256(seed), 4));
|
|
16786
|
+
}
|
|
16787
|
+
function uuidV7From(seed, timestampMs) {
|
|
16788
|
+
const bytes = Uint8Array.from(sha256(seed).subarray(0, 16));
|
|
16789
|
+
let ts = Math.max(0, Math.floor(timestampMs));
|
|
16790
|
+
for (let i = 5;i >= 0; i--) {
|
|
16791
|
+
bytes[i] = ts % 256;
|
|
16792
|
+
ts = Math.floor(ts / 256);
|
|
16793
|
+
}
|
|
16794
|
+
return format(withVersion(bytes, 7));
|
|
16795
|
+
}
|
|
16796
|
+
function threadIdFrom(seed) {
|
|
16797
|
+
const digest = sha256(`thread:${seed}`);
|
|
16798
|
+
const offset = digest.readUIntBE(0, 6) % WINDOW_MS2;
|
|
16799
|
+
return uuidV7From(seed, WINDOW_END_MS - WINDOW_MS2 + offset);
|
|
16800
|
+
}
|
|
16801
|
+
function threadInstant(threadId, now) {
|
|
16802
|
+
const hex = threadId.replace(/-/g, "");
|
|
16803
|
+
const version = hex[12];
|
|
16804
|
+
const ts = Number.parseInt(hex.slice(0, 12), 16);
|
|
16805
|
+
if (version === "7" && Number.isFinite(ts) && ts <= now)
|
|
16806
|
+
return ts;
|
|
16807
|
+
const digest = sha256(`thread:${threadId}`);
|
|
16808
|
+
return WINDOW_END_MS - WINDOW_MS2 + digest.readUIntBE(0, 6) % WINDOW_MS2;
|
|
16809
|
+
}
|
|
16810
|
+
var ID_SALT = /^[0-9a-f]{64}$/;
|
|
16811
|
+
function codexSecretsFrom(providerData) {
|
|
16812
|
+
const { installationId, idSalt } = providerData;
|
|
16813
|
+
return {
|
|
16814
|
+
...typeof installationId === "string" && isUuid(installationId) ? { installationId } : {},
|
|
16815
|
+
...typeof idSalt === "string" && ID_SALT.test(idSalt) ? { idSalt } : {}
|
|
16816
|
+
};
|
|
16817
|
+
}
|
|
16818
|
+
function ensureCodexSecrets(providerData) {
|
|
16819
|
+
const stored = codexSecretsFrom(providerData);
|
|
16820
|
+
const salt = () => Buffer.from(globalThis.crypto.getRandomValues(new Uint8Array(32))).toString("hex");
|
|
16821
|
+
return {
|
|
16822
|
+
installationId: stored.installationId ?? globalThis.crypto.randomUUID(),
|
|
16823
|
+
idSalt: stored.idSalt ?? salt()
|
|
16824
|
+
};
|
|
16825
|
+
}
|
|
16826
|
+
function keyed(salt, seed) {
|
|
16827
|
+
if (salt === undefined)
|
|
16828
|
+
return seed;
|
|
16829
|
+
return createHmac("sha256", salt).update(seed).digest("hex");
|
|
16830
|
+
}
|
|
16831
|
+
function prefixNamespace(threadId) {
|
|
16832
|
+
return uuidV5(NAMESPACE_OID, threadId);
|
|
16833
|
+
}
|
|
16834
|
+
|
|
16564
16835
|
// packages/providers/src/openai/oauth.ts
|
|
16565
16836
|
var CLIENT_ID6 = "app_EMoamEEZ73f0CkXaXp7hrann";
|
|
16566
16837
|
var AUTHORIZE_URL3 = "https://auth.openai.com/oauth/authorize";
|
|
@@ -16621,7 +16892,7 @@ async function* postToken4(body, fail) {
|
|
|
16621
16892
|
}
|
|
16622
16893
|
return token;
|
|
16623
16894
|
}
|
|
16624
|
-
function toResult6(token, fallbackRefresh, now, fallbackAccountId
|
|
16895
|
+
function toResult6(token, fallbackRefresh, now, fallbackAccountId, prior) {
|
|
16625
16896
|
const claims = decodeClaims(token.idToken);
|
|
16626
16897
|
return {
|
|
16627
16898
|
secrets: {
|
|
@@ -16632,7 +16903,10 @@ function toResult6(token, fallbackRefresh, now, fallbackAccountId = null) {
|
|
|
16632
16903
|
},
|
|
16633
16904
|
expiresAt: token.expiresIn === null ? null : now() + token.expiresIn * 1000,
|
|
16634
16905
|
accountEmail: claims.email,
|
|
16635
|
-
providerData: {
|
|
16906
|
+
providerData: {
|
|
16907
|
+
accountId: claims.accountId ?? fallbackAccountId,
|
|
16908
|
+
...ensureCodexSecrets(prior)
|
|
16909
|
+
}
|
|
16636
16910
|
};
|
|
16637
16911
|
}
|
|
16638
16912
|
function windowSecondsOf(value) {
|
|
@@ -16689,11 +16963,11 @@ var openaiOAuthFlow = {
|
|
|
16689
16963
|
url.searchParams.set("id_token_add_organizations", "true");
|
|
16690
16964
|
url.searchParams.set("codex_cli_simplified_flow", "true");
|
|
16691
16965
|
url.searchParams.set("state", state);
|
|
16692
|
-
url.searchParams.set("originator",
|
|
16966
|
+
url.searchParams.set("originator", OPENAI_ORIGINATOR);
|
|
16693
16967
|
url.searchParams.set("prompt", "login");
|
|
16694
16968
|
return { authorizeUrl: url.toString(), pending: { verifier, challenge, state, redirectUri } };
|
|
16695
16969
|
},
|
|
16696
|
-
async* exchange({ code, pending, fail, now }) {
|
|
16970
|
+
async* exchange({ code, pending, providerData, fail, now }) {
|
|
16697
16971
|
const [rawCode, pastedState] = code.split("#");
|
|
16698
16972
|
if (pastedState !== undefined && pastedState !== pending.state) {
|
|
16699
16973
|
throw fail("AUTH", "authorization state mismatch");
|
|
@@ -16704,11 +16978,11 @@ var openaiOAuthFlow = {
|
|
|
16704
16978
|
redirect_uri: pending.redirectUri,
|
|
16705
16979
|
code_verifier: pending.verifier
|
|
16706
16980
|
}, fail);
|
|
16707
|
-
return toResult6(token, null, now);
|
|
16981
|
+
return toResult6(token, null, now, null, providerData ?? {});
|
|
16708
16982
|
},
|
|
16709
16983
|
async* refresh({ refreshToken, providerData, fail, now }) {
|
|
16710
16984
|
const token = yield* postToken4({ grant_type: "refresh_token", refresh_token: refreshToken, scope: SCOPES4 }, fail);
|
|
16711
|
-
return toResult6(token, refreshToken, now, accountIdFromProviderData(providerData));
|
|
16985
|
+
return toResult6(token, refreshToken, now, accountIdFromProviderData(providerData), providerData);
|
|
16712
16986
|
},
|
|
16713
16987
|
async* usage({ secrets, providerData, now }) {
|
|
16714
16988
|
if (secrets.accessToken === null)
|
|
@@ -17458,7 +17732,7 @@ var customCodec = {
|
|
|
17458
17732
|
// packages/providers/src/custom/index.ts
|
|
17459
17733
|
var customAdapter = codecAdapter("custom", customDescriptor.capabilities, customCodec);
|
|
17460
17734
|
// packages/providers/src/grok/codec.ts
|
|
17461
|
-
import { createHash as
|
|
17735
|
+
import { createHash as createHash5 } from "crypto";
|
|
17462
17736
|
|
|
17463
17737
|
// packages/providers/src/grok/decode.ts
|
|
17464
17738
|
var ERROR_CODE2 = {
|
|
@@ -17657,7 +17931,7 @@ async function* decodeGrokResponses(messages) {
|
|
|
17657
17931
|
}
|
|
17658
17932
|
|
|
17659
17933
|
// packages/providers/src/grok/wire.ts
|
|
17660
|
-
import { createHash as
|
|
17934
|
+
import { createHash as createHash4 } from "crypto";
|
|
17661
17935
|
var MAX_TOOLS = 200;
|
|
17662
17936
|
function encodeToolChoice3(c) {
|
|
17663
17937
|
switch (c.type) {
|
|
@@ -17673,7 +17947,7 @@ function encodeToolChoice3(c) {
|
|
|
17673
17947
|
}
|
|
17674
17948
|
function promptCacheKey(instructions, firstInput) {
|
|
17675
17949
|
const stable = JSON.stringify({ instructions, firstInput });
|
|
17676
|
-
return
|
|
17950
|
+
return createHash4("sha256").update(stable).digest("hex").slice(0, 32);
|
|
17677
17951
|
}
|
|
17678
17952
|
function toGrokWire(req, model) {
|
|
17679
17953
|
const degradations = [];
|
|
@@ -17813,7 +18087,7 @@ var PROXY_URL = "https://cli-chat-proxy.grok.com/v1/responses";
|
|
|
17813
18087
|
var API_URL = "https://api.x.ai/v1/responses";
|
|
17814
18088
|
var CONV_NAMESPACE = "omni-grok-conv";
|
|
17815
18089
|
function derivedUuid(value) {
|
|
17816
|
-
const h =
|
|
18090
|
+
const h = createHash5("sha256").update(`${CONV_NAMESPACE}:${value}`).digest("hex");
|
|
17817
18091
|
const variant = (Number.parseInt(h[16], 16) & 3 | 8).toString(16);
|
|
17818
18092
|
return [
|
|
17819
18093
|
h.slice(0, 8),
|
|
@@ -17873,11 +18147,12 @@ var grokAdapter = codecAdapter("grok", grokDescriptor.capabilities, grokCodec);
|
|
|
17873
18147
|
import { request as httpRequest } from "http";
|
|
17874
18148
|
import { request as httpsRequest } from "https";
|
|
17875
18149
|
import { Readable } from "stream";
|
|
18150
|
+
import * as zlib from "zlib";
|
|
17876
18151
|
var CONNECT_ATTEMPT_TIMEOUT_MS = 3000;
|
|
17877
18152
|
function nodeHttpClient(options = {}) {
|
|
17878
18153
|
const logger = options.logger ?? noopLogger;
|
|
17879
18154
|
const now = options.now ?? (() => Date.now());
|
|
17880
|
-
return (req) => new Promise((resolve, reject) => {
|
|
18155
|
+
return (req) => encodeBody(req).then((bodyBytes) => new Promise((resolve, reject) => {
|
|
17881
18156
|
const url = new URL(req.url);
|
|
17882
18157
|
const startedAt = now();
|
|
17883
18158
|
let traced = false;
|
|
@@ -17908,7 +18183,6 @@ function nodeHttpClient(options = {}) {
|
|
|
17908
18183
|
});
|
|
17909
18184
|
};
|
|
17910
18185
|
const send = url.protocol === "https:" ? httpsRequest : httpRequest;
|
|
17911
|
-
const bodyBytes = Buffer.from(req.body, "utf8");
|
|
17912
18186
|
const headers = {};
|
|
17913
18187
|
for (const [name, value] of req.headers)
|
|
17914
18188
|
headers[name] = value;
|
|
@@ -17966,6 +18240,22 @@ function nodeHttpClient(options = {}) {
|
|
|
17966
18240
|
if (bodyBytes.byteLength > 0)
|
|
17967
18241
|
outgoing.write(bodyBytes);
|
|
17968
18242
|
outgoing.end();
|
|
18243
|
+
}));
|
|
18244
|
+
}
|
|
18245
|
+
function encodeBody(req) {
|
|
18246
|
+
const raw = Buffer.from(req.body, "utf8");
|
|
18247
|
+
if (req.bodyEncoding !== "zstd")
|
|
18248
|
+
return Promise.resolve(raw);
|
|
18249
|
+
return zstdEncode(raw);
|
|
18250
|
+
}
|
|
18251
|
+
function zstdEncode(raw, create = (options) => zlib.createZstdCompress(options)) {
|
|
18252
|
+
return new Promise((resolve, reject) => {
|
|
18253
|
+
const stream = create({ params: { [zlib.constants.ZSTD_c_compressionLevel]: 3 } });
|
|
18254
|
+
const chunks = [];
|
|
18255
|
+
stream.on("data", (chunk) => chunks.push(chunk));
|
|
18256
|
+
stream.on("end", () => resolve(Buffer.concat(chunks)));
|
|
18257
|
+
stream.on("error", reject);
|
|
18258
|
+
stream.end(raw);
|
|
17969
18259
|
});
|
|
17970
18260
|
}
|
|
17971
18261
|
function hasHeader(req, lowerName) {
|
|
@@ -18808,7 +19098,7 @@ async function* decodeMuseResponses(messages, options = {}) {
|
|
|
18808
19098
|
}
|
|
18809
19099
|
|
|
18810
19100
|
// packages/providers/src/muse/wire.ts
|
|
18811
|
-
import { createHash as
|
|
19101
|
+
import { createHash as createHash6 } from "crypto";
|
|
18812
19102
|
function suppliedKey(req) {
|
|
18813
19103
|
const vendor = req.vendor?.openai;
|
|
18814
19104
|
for (const name of ["prompt_cache_key", "session_id"]) {
|
|
@@ -18820,7 +19110,7 @@ function suppliedKey(req) {
|
|
|
18820
19110
|
}
|
|
18821
19111
|
function cacheKey(req, instructions, firstInput) {
|
|
18822
19112
|
const stable = req.conversationId !== undefined && req.conversationId.length > 0 ? JSON.stringify({ conversation: req.conversationId }) : JSON.stringify({ instructions, firstInput: firstInput ?? null });
|
|
18823
|
-
return
|
|
19113
|
+
return createHash6("sha256").update(stable).digest("hex").slice(0, 32);
|
|
18824
19114
|
}
|
|
18825
19115
|
function repairOrphanedCalls(input) {
|
|
18826
19116
|
const answered = new Set;
|
|
@@ -19020,6 +19310,49 @@ var museCodec = {
|
|
|
19020
19310
|
|
|
19021
19311
|
// packages/providers/src/muse/index.ts
|
|
19022
19312
|
var museAdapter = codecAdapter("muse", museDescriptor.capabilities, museCodec);
|
|
19313
|
+
// packages/providers/src/openai/codexCatalog.ts
|
|
19314
|
+
var CODEX_EFFORTS = new Set([
|
|
19315
|
+
"none",
|
|
19316
|
+
"minimal",
|
|
19317
|
+
"low",
|
|
19318
|
+
"medium",
|
|
19319
|
+
"high",
|
|
19320
|
+
"xhigh",
|
|
19321
|
+
"max",
|
|
19322
|
+
"ultra",
|
|
19323
|
+
"persistent"
|
|
19324
|
+
]);
|
|
19325
|
+
function isCodexEffort(value) {
|
|
19326
|
+
return typeof value === "string" && CODEX_EFFORTS.has(value);
|
|
19327
|
+
}
|
|
19328
|
+
var entry = (defaultEffort, nodeReplAutoReviewRequired, over = {}) => ({
|
|
19329
|
+
lite: true,
|
|
19330
|
+
verbosity: "low",
|
|
19331
|
+
defaultEffort,
|
|
19332
|
+
lowestEffort: "low",
|
|
19333
|
+
nodeReplAutoReviewRequired,
|
|
19334
|
+
nodeReplDisabled: false,
|
|
19335
|
+
supportsParallelToolCalls: true,
|
|
19336
|
+
...over
|
|
19337
|
+
});
|
|
19338
|
+
var UNLISTED = entry("low", false);
|
|
19339
|
+
var MODELS = Object.assign(Object.create(null), {
|
|
19340
|
+
"gpt-6-astra": entry("low", true),
|
|
19341
|
+
"gpt-6.1-sol": entry("low", true),
|
|
19342
|
+
"gpt-6-sol": entry("medium", true),
|
|
19343
|
+
"gpt-6-luna": entry("medium", false),
|
|
19344
|
+
"gpt-5.6-sol": entry("low", false),
|
|
19345
|
+
"gpt-5.6-terra": entry("medium", false),
|
|
19346
|
+
"gpt-5.6-luna": entry("medium", false),
|
|
19347
|
+
"gpt-daybreak-blue-latest": entry("low", false),
|
|
19348
|
+
"gpt-daybreak-red-latest": entry("medium", false, { verbosity: "high" }),
|
|
19349
|
+
"gpt-5.5": entry("medium", false, { lite: false }),
|
|
19350
|
+
"codex-auto-review": entry("medium", false)
|
|
19351
|
+
});
|
|
19352
|
+
function codexModel(model) {
|
|
19353
|
+
return MODELS[model] ?? UNLISTED;
|
|
19354
|
+
}
|
|
19355
|
+
|
|
19023
19356
|
// packages/providers/src/openai/decode.ts
|
|
19024
19357
|
var ERROR_CODE4 = {
|
|
19025
19358
|
rate_limit_exceeded: "RATE_LIMIT",
|
|
@@ -19051,6 +19384,7 @@ var KNOWN_EVENTS4 = new Set([
|
|
|
19051
19384
|
"response.reasoning_text.done",
|
|
19052
19385
|
"response.function_call_arguments.delta",
|
|
19053
19386
|
"response.function_call_arguments.done",
|
|
19387
|
+
"response.metadata",
|
|
19054
19388
|
"response.completed",
|
|
19055
19389
|
"response.incomplete",
|
|
19056
19390
|
"response.failed",
|
|
@@ -19236,8 +19570,170 @@ async function* decodeResponses(messages, options = {}) {
|
|
|
19236
19570
|
}
|
|
19237
19571
|
}
|
|
19238
19572
|
|
|
19573
|
+
// packages/providers/src/openai/codexLite.ts
|
|
19574
|
+
var PASSTHROUGH = "internal_chat_message_metadata_passthrough";
|
|
19575
|
+
function turnId(threadId, index, threadAt, salt) {
|
|
19576
|
+
return uuidV7From(keyed(salt, `${threadId}:turn:${index}`), threadAt + index);
|
|
19577
|
+
}
|
|
19578
|
+
function contentKinds(content, role) {
|
|
19579
|
+
if (!Array.isArray(content))
|
|
19580
|
+
return;
|
|
19581
|
+
if (role === "assistant")
|
|
19582
|
+
return content.map(() => "unknown");
|
|
19583
|
+
const kinds = [];
|
|
19584
|
+
for (const part of content) {
|
|
19585
|
+
const type = part.type;
|
|
19586
|
+
if (type === "input_text")
|
|
19587
|
+
kinds.push("user.text");
|
|
19588
|
+
else if (type === "input_image")
|
|
19589
|
+
kinds.push("user.image");
|
|
19590
|
+
else
|
|
19591
|
+
return;
|
|
19592
|
+
}
|
|
19593
|
+
return kinds;
|
|
19594
|
+
}
|
|
19595
|
+
function toLiteInput(args) {
|
|
19596
|
+
const { threadId } = args;
|
|
19597
|
+
const threadAt = threadInstant(threadId, args.startedAt ?? Number.POSITIVE_INFINITY);
|
|
19598
|
+
const startedAt = args.startedAt ?? threadAt;
|
|
19599
|
+
const historySeed = args.requestId ?? threadId;
|
|
19600
|
+
const salt = args.idSalt;
|
|
19601
|
+
const namespace = prefixNamespace(threadId);
|
|
19602
|
+
const prefix = [];
|
|
19603
|
+
if (args.tools !== undefined && args.tools.length > 0) {
|
|
19604
|
+
const tools = [{ type: "namespace", name: "functions", description: "", tools: args.tools }];
|
|
19605
|
+
prefix.push({
|
|
19606
|
+
type: "additional_tools",
|
|
19607
|
+
id: `at_${uuidV5(namespace, JSON.stringify(tools))}`,
|
|
19608
|
+
role: "developer",
|
|
19609
|
+
tools
|
|
19610
|
+
});
|
|
19611
|
+
}
|
|
19612
|
+
if (args.instructions !== undefined && args.instructions.length > 0) {
|
|
19613
|
+
prefix.push({
|
|
19614
|
+
type: "message",
|
|
19615
|
+
id: `msg_${uuidV5(namespace, args.instructions)}`,
|
|
19616
|
+
role: "developer",
|
|
19617
|
+
content: [{ type: "input_text", text: args.instructions }],
|
|
19618
|
+
[PASSTHROUGH]: { content_item_kinds: ["model.base_instructions"] }
|
|
19619
|
+
});
|
|
19620
|
+
}
|
|
19621
|
+
let turn = 0;
|
|
19622
|
+
const history = args.input.map((raw, index) => {
|
|
19623
|
+
const item = raw;
|
|
19624
|
+
turn = args.turnOf.get(item) ?? turn;
|
|
19625
|
+
const turn_id = turnId(threadId, turn, threadAt, salt);
|
|
19626
|
+
const fresh = (prefixName) => `${prefixName}_${uuidV7From(keyed(salt, `${historySeed}:item:${index}`), startedAt)}`;
|
|
19627
|
+
const create_time = (startedAt * 1000 + index) / 1e6;
|
|
19628
|
+
switch (item.type) {
|
|
19629
|
+
case "message": {
|
|
19630
|
+
const role = String(item.role);
|
|
19631
|
+
const kinds = contentKinds(item.content, role);
|
|
19632
|
+
if (role === "assistant") {
|
|
19633
|
+
return {
|
|
19634
|
+
type: "message",
|
|
19635
|
+
status: "completed",
|
|
19636
|
+
id: fresh("msg"),
|
|
19637
|
+
role,
|
|
19638
|
+
content: Array.isArray(item.content) ? item.content.map((part) => part.type === "output_text" ? { ...part, annotations: [] } : part) : item.content,
|
|
19639
|
+
[PASSTHROUGH]: {
|
|
19640
|
+
turn_id,
|
|
19641
|
+
...kinds === undefined ? {} : { content_item_kinds: kinds }
|
|
19642
|
+
}
|
|
19643
|
+
};
|
|
19644
|
+
}
|
|
19645
|
+
return {
|
|
19646
|
+
type: "message",
|
|
19647
|
+
id: fresh("msg"),
|
|
19648
|
+
role,
|
|
19649
|
+
content: item.content,
|
|
19650
|
+
[PASSTHROUGH]: {
|
|
19651
|
+
turn_id,
|
|
19652
|
+
create_time,
|
|
19653
|
+
...kinds === undefined ? {} : { content_item_kinds: kinds }
|
|
19654
|
+
}
|
|
19655
|
+
};
|
|
19656
|
+
}
|
|
19657
|
+
case "function_call":
|
|
19658
|
+
return {
|
|
19659
|
+
type: "function_call",
|
|
19660
|
+
status: "completed",
|
|
19661
|
+
id: fresh("fc"),
|
|
19662
|
+
name: item.name,
|
|
19663
|
+
arguments: item.arguments,
|
|
19664
|
+
call_id: item.call_id,
|
|
19665
|
+
[PASSTHROUGH]: { turn_id }
|
|
19666
|
+
};
|
|
19667
|
+
case "function_call_output":
|
|
19668
|
+
return {
|
|
19669
|
+
type: "function_call_output",
|
|
19670
|
+
id: fresh("fco"),
|
|
19671
|
+
call_id: item.call_id,
|
|
19672
|
+
output: item.output,
|
|
19673
|
+
[PASSTHROUGH]: { turn_id, create_time }
|
|
19674
|
+
};
|
|
19675
|
+
case "reasoning": {
|
|
19676
|
+
const { [PASSTHROUGH]: _client, ...rest } = item;
|
|
19677
|
+
return { ...rest, [PASSTHROUGH]: { turn_id } };
|
|
19678
|
+
}
|
|
19679
|
+
default:
|
|
19680
|
+
return item;
|
|
19681
|
+
}
|
|
19682
|
+
});
|
|
19683
|
+
return {
|
|
19684
|
+
input: [...prefix, ...history],
|
|
19685
|
+
turnId: turnId(threadId, turn, threadAt, salt),
|
|
19686
|
+
startedAt
|
|
19687
|
+
};
|
|
19688
|
+
}
|
|
19689
|
+
function asciiJson(value) {
|
|
19690
|
+
return JSON.stringify(value).replace(/[^\x20-\x7e]/g, (c) => `\\u${c.charCodeAt(0).toString(16).padStart(4, "0")}`);
|
|
19691
|
+
}
|
|
19692
|
+
function codexTurn(args) {
|
|
19693
|
+
const { threadId, turnId: turn } = args;
|
|
19694
|
+
const windowId = `${threadId}:0`;
|
|
19695
|
+
const installationId = args.installationId ?? uuidV4From(`installation:${args.installationSeed}`);
|
|
19696
|
+
const threadAt = threadInstant(threadId, args.startedAt);
|
|
19697
|
+
const catalog = codexModel(args.model);
|
|
19698
|
+
const turnMetadata = asciiJson({
|
|
19699
|
+
installation_id: installationId,
|
|
19700
|
+
session_id: threadId,
|
|
19701
|
+
thread_id: threadId,
|
|
19702
|
+
agent_name: "/root",
|
|
19703
|
+
turn_id: turn,
|
|
19704
|
+
window_id: windowId,
|
|
19705
|
+
window_number: 0,
|
|
19706
|
+
context_window_id: uuidV7From(keyed(args.idSalt, `${threadId}:context-window`), threadAt),
|
|
19707
|
+
request_kind: "turn",
|
|
19708
|
+
history_initialization: "new",
|
|
19709
|
+
root_turn_id: turn,
|
|
19710
|
+
thread_source: "user",
|
|
19711
|
+
turn_trigger: "user",
|
|
19712
|
+
sandbox: OPENAI_SANDBOX,
|
|
19713
|
+
sandbox_mode: "workspace-write",
|
|
19714
|
+
auto_review_enabled: false,
|
|
19715
|
+
node_repl_auto_review_required: catalog.nodeReplAutoReviewRequired,
|
|
19716
|
+
node_repl_disabled: catalog.nodeReplDisabled,
|
|
19717
|
+
turn_started_at_unix_ms: args.startedAt,
|
|
19718
|
+
analytics_enabled: false,
|
|
19719
|
+
model: args.model,
|
|
19720
|
+
reasoning_effort: args.effort
|
|
19721
|
+
});
|
|
19722
|
+
const clientMetadata = {
|
|
19723
|
+
turn_id: turn,
|
|
19724
|
+
thread_id: threadId,
|
|
19725
|
+
"x-codex-turn-metadata": turnMetadata,
|
|
19726
|
+
mcp_attribution: '{"status":"none"}',
|
|
19727
|
+
guardian_credits_requested: "true",
|
|
19728
|
+
"x-codex-installation-id": installationId,
|
|
19729
|
+
"x-codex-window-id": windowId,
|
|
19730
|
+
root_turn_id: turn,
|
|
19731
|
+
session_id: threadId
|
|
19732
|
+
};
|
|
19733
|
+
return { windowId, turnMetadata, clientMetadata };
|
|
19734
|
+
}
|
|
19735
|
+
|
|
19239
19736
|
// packages/providers/src/openai/wire.ts
|
|
19240
|
-
import { createHash as createHash6 } from "crypto";
|
|
19241
19737
|
var MAX_CACHE_KEY_LENGTH = 1024;
|
|
19242
19738
|
var HEADER_SAFE_KEY = /^[\x21-\x7e]+$/;
|
|
19243
19739
|
function suppliedKey2(req) {
|
|
@@ -19249,9 +19745,16 @@ function suppliedKey2(req) {
|
|
|
19249
19745
|
}
|
|
19250
19746
|
return;
|
|
19251
19747
|
}
|
|
19252
|
-
function cacheKey2(req, instructions, firstInput) {
|
|
19253
|
-
const
|
|
19254
|
-
|
|
19748
|
+
function cacheKey2(req, instructions, firstInput, salt) {
|
|
19749
|
+
const supplied = suppliedKey2(req);
|
|
19750
|
+
if (supplied !== undefined) {
|
|
19751
|
+
if (isUuid(supplied))
|
|
19752
|
+
return { key: supplied, seed: supplied };
|
|
19753
|
+
const seed = JSON.stringify({ key: supplied });
|
|
19754
|
+
return { key: threadIdFrom(keyed(salt, seed)), seed };
|
|
19755
|
+
}
|
|
19756
|
+
const seed = req.conversationId !== undefined && req.conversationId.length > 0 ? JSON.stringify({ conversation: req.conversationId }) : JSON.stringify({ instructions, firstInput: firstInput ?? null });
|
|
19757
|
+
return { key: threadIdFrom(keyed(salt, seed)), seed };
|
|
19255
19758
|
}
|
|
19256
19759
|
function repairOrphanedCalls2(input) {
|
|
19257
19760
|
const answered = new Set;
|
|
@@ -19282,6 +19785,44 @@ function encodeToolChoice7(c) {
|
|
|
19282
19785
|
return { type: "function", name: c.name };
|
|
19283
19786
|
}
|
|
19284
19787
|
}
|
|
19788
|
+
function codexReasoning(req, model, note) {
|
|
19789
|
+
const catalog = codexModel(model);
|
|
19790
|
+
const context = catalog.lite ? { context: "all_turns" } : {};
|
|
19791
|
+
const r = req.reasoning;
|
|
19792
|
+
if (r === undefined)
|
|
19793
|
+
return { effort: catalog.defaultEffort, ...context };
|
|
19794
|
+
if (r.mode === "off") {
|
|
19795
|
+
note("openai:reasoning-off-as-lowest");
|
|
19796
|
+
return { effort: catalog.lowestEffort, ...context };
|
|
19797
|
+
}
|
|
19798
|
+
if (r.mode === "betweenTools")
|
|
19799
|
+
return { effort: catalog.lowestEffort, ...context };
|
|
19800
|
+
if (r.mode === "budget") {
|
|
19801
|
+
note("openai:reasoning-budget-dropped");
|
|
19802
|
+
return { effort: catalog.defaultEffort, ...context };
|
|
19803
|
+
}
|
|
19804
|
+
return {
|
|
19805
|
+
effort: r.effort ?? catalog.defaultEffort,
|
|
19806
|
+
...r.display === "omitted" || r.display === "updates" ? {} : { summary: "auto" },
|
|
19807
|
+
...context
|
|
19808
|
+
};
|
|
19809
|
+
}
|
|
19810
|
+
var isRecord8 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
19811
|
+
function mergeCodexVendor(body, vendor, note) {
|
|
19812
|
+
const target = body;
|
|
19813
|
+
for (const [name, value] of Object.entries(vendor)) {
|
|
19814
|
+
if (name === "client_metadata") {
|
|
19815
|
+
note("openai:client-metadata-replaced");
|
|
19816
|
+
continue;
|
|
19817
|
+
}
|
|
19818
|
+
const ours = target[name];
|
|
19819
|
+
if ((name === "text" || name === "reasoning") && isRecord8(ours) && isRecord8(value)) {
|
|
19820
|
+
target[name] = { ...ours, ...value };
|
|
19821
|
+
continue;
|
|
19822
|
+
}
|
|
19823
|
+
target[name] = value;
|
|
19824
|
+
}
|
|
19825
|
+
}
|
|
19285
19826
|
function toResponsesWire(req, model, opts = { oauth: false }) {
|
|
19286
19827
|
const degradations = [];
|
|
19287
19828
|
const input = [];
|
|
@@ -19289,6 +19830,12 @@ function toResponsesWire(req, model, opts = { oauth: false }) {
|
|
|
19289
19830
|
if (!degradations.includes(d))
|
|
19290
19831
|
degradations.push(d);
|
|
19291
19832
|
};
|
|
19833
|
+
const turnOf = new WeakMap;
|
|
19834
|
+
let turn = -1;
|
|
19835
|
+
const push = (item) => {
|
|
19836
|
+
input.push(item);
|
|
19837
|
+
turnOf.set(item, Math.max(0, turn));
|
|
19838
|
+
};
|
|
19292
19839
|
if (req.betas?.includes(CONTEXT_1M_BETA))
|
|
19293
19840
|
note("openai:context-1m-dropped");
|
|
19294
19841
|
for (const message of req.messages) {
|
|
@@ -19300,10 +19847,15 @@ function toResponsesWire(req, model, opts = { oauth: false }) {
|
|
|
19300
19847
|
const flush = () => {
|
|
19301
19848
|
if (parts.length === 0)
|
|
19302
19849
|
return;
|
|
19303
|
-
|
|
19850
|
+
push({ type: "message", role, content: [...parts] });
|
|
19304
19851
|
parts.length = 0;
|
|
19305
19852
|
};
|
|
19853
|
+
let opened = false;
|
|
19306
19854
|
for (const block of message.content) {
|
|
19855
|
+
if (message.role === "user" && !opened && block.type !== "toolResult") {
|
|
19856
|
+
opened = true;
|
|
19857
|
+
turn++;
|
|
19858
|
+
}
|
|
19307
19859
|
switch (block.type) {
|
|
19308
19860
|
case "text":
|
|
19309
19861
|
parts.push({
|
|
@@ -19324,7 +19876,7 @@ function toResponsesWire(req, model, opts = { oauth: false }) {
|
|
|
19324
19876
|
break;
|
|
19325
19877
|
case "toolUse":
|
|
19326
19878
|
flush();
|
|
19327
|
-
|
|
19879
|
+
push({
|
|
19328
19880
|
type: "function_call",
|
|
19329
19881
|
call_id: block.id,
|
|
19330
19882
|
name: block.name,
|
|
@@ -19336,7 +19888,7 @@ function toResponsesWire(req, model, opts = { oauth: false }) {
|
|
|
19336
19888
|
note("openai:anthropic-native-block-dropped");
|
|
19337
19889
|
flush();
|
|
19338
19890
|
const files = responsesFiles(block.files ?? []);
|
|
19339
|
-
|
|
19891
|
+
push({
|
|
19340
19892
|
type: "function_call_output",
|
|
19341
19893
|
call_id: block.toolUseId,
|
|
19342
19894
|
output: withFileText(block.content, files.text)
|
|
@@ -19364,7 +19916,7 @@ function toResponsesWire(req, model, opts = { oauth: false }) {
|
|
|
19364
19916
|
if (block.provider === "openai") {
|
|
19365
19917
|
flush();
|
|
19366
19918
|
const { id: _id, ...rest } = block.data;
|
|
19367
|
-
|
|
19919
|
+
push({ type: block.blockType, ...rest });
|
|
19368
19920
|
break;
|
|
19369
19921
|
}
|
|
19370
19922
|
note("openai:anthropic-native-block-dropped");
|
|
@@ -19376,8 +19928,10 @@ function toResponsesWire(req, model, opts = { oauth: false }) {
|
|
|
19376
19928
|
repairOrphanedCalls2(input);
|
|
19377
19929
|
const body = { model, input, stream: req.stream, store: false };
|
|
19378
19930
|
const instructions = systemText(req.system, "openai", note);
|
|
19379
|
-
|
|
19931
|
+
const lite = opts.oauth && codexModel(model).lite;
|
|
19932
|
+
if (instructions !== undefined && instructions.length > 0 && !opts.oauth) {
|
|
19380
19933
|
body.instructions = instructions;
|
|
19934
|
+
}
|
|
19381
19935
|
if (req.maxTokens !== undefined) {
|
|
19382
19936
|
if (opts.oauth)
|
|
19383
19937
|
note("openai:max-tokens-dropped");
|
|
@@ -19390,22 +19944,45 @@ function toResponsesWire(req, model, opts = { oauth: false }) {
|
|
|
19390
19944
|
else
|
|
19391
19945
|
body.temperature = req.temperature;
|
|
19392
19946
|
}
|
|
19947
|
+
let liteTools;
|
|
19393
19948
|
if (req.tools !== undefined) {
|
|
19394
19949
|
const custom = req.tools.filter((t) => t.kind === "portable");
|
|
19395
19950
|
if (custom.length !== req.tools.length)
|
|
19396
19951
|
note("openai:anthropic-tool-dropped");
|
|
19397
|
-
|
|
19398
|
-
|
|
19399
|
-
|
|
19400
|
-
|
|
19401
|
-
|
|
19402
|
-
|
|
19952
|
+
if (opts.oauth) {
|
|
19953
|
+
const tools = custom.map((t) => ({
|
|
19954
|
+
type: "function",
|
|
19955
|
+
name: t.name,
|
|
19956
|
+
description: t.description,
|
|
19957
|
+
strict: false,
|
|
19958
|
+
parameters: t.inputSchema
|
|
19959
|
+
}));
|
|
19960
|
+
if (lite)
|
|
19961
|
+
liteTools = tools;
|
|
19962
|
+
else
|
|
19963
|
+
body.tools = tools;
|
|
19964
|
+
} else {
|
|
19965
|
+
body.tools = custom.map((t) => ({
|
|
19966
|
+
type: "function",
|
|
19967
|
+
name: t.name,
|
|
19968
|
+
description: t.description,
|
|
19969
|
+
parameters: t.inputSchema
|
|
19970
|
+
}));
|
|
19971
|
+
}
|
|
19403
19972
|
}
|
|
19404
19973
|
if (req.toolChoice !== undefined)
|
|
19405
19974
|
body.tool_choice = encodeToolChoice7(req.toolChoice);
|
|
19406
|
-
if (
|
|
19407
|
-
|
|
19408
|
-
if (
|
|
19975
|
+
else if (opts.oauth)
|
|
19976
|
+
body.tool_choice = "auto";
|
|
19977
|
+
if (opts.oauth) {
|
|
19978
|
+
body.parallel_tool_calls = !lite && codexModel(model).supportsParallelToolCalls;
|
|
19979
|
+
}
|
|
19980
|
+
if (req.reasoning?.mode === "betweenTools") {
|
|
19981
|
+
note(opts.oauth ? "openai:reasoning-between-tools-as-lowest" : "openai:reasoning-between-tools-as-off");
|
|
19982
|
+
}
|
|
19983
|
+
if (opts.oauth) {
|
|
19984
|
+
body.reasoning = codexReasoning(req, model, note);
|
|
19985
|
+
} else if (req.reasoning !== undefined && req.reasoning.mode !== "off" && req.reasoning.mode !== "betweenTools") {
|
|
19409
19986
|
if (req.reasoning.mode === "budget") {
|
|
19410
19987
|
degradations.push("openai:reasoning-budget-dropped");
|
|
19411
19988
|
} else {
|
|
@@ -19416,36 +19993,98 @@ function toResponsesWire(req, model, opts = { oauth: false }) {
|
|
|
19416
19993
|
};
|
|
19417
19994
|
}
|
|
19418
19995
|
}
|
|
19419
|
-
const
|
|
19420
|
-
|
|
19996
|
+
const salt = opts.oauth ? opts.idSalt : undefined;
|
|
19997
|
+
const { key, seed } = cacheKey2(req, instructions ?? "", input[0], salt);
|
|
19998
|
+
if (opts.oauth) {
|
|
19999
|
+
body.include = ["reasoning.encrypted_content"];
|
|
20000
|
+
body.text = { verbosity: codexModel(model).verbosity };
|
|
20001
|
+
mergeCodexVendor(body, req.vendor?.openai ?? {}, note);
|
|
20002
|
+
} else {
|
|
20003
|
+
Object.assign(body, req.vendor?.openai ?? {});
|
|
20004
|
+
}
|
|
19421
20005
|
const moved = moveToResponsesText(req, body, "openai");
|
|
19422
20006
|
if (moved !== undefined)
|
|
19423
20007
|
note(moved);
|
|
19424
20008
|
body.prompt_cache_key = key;
|
|
19425
|
-
|
|
20009
|
+
if (!opts.oauth)
|
|
20010
|
+
return { body, degradations, cacheKey: key };
|
|
20011
|
+
const built = toLiteInput({
|
|
20012
|
+
threadId: key,
|
|
20013
|
+
input,
|
|
20014
|
+
turnOf,
|
|
20015
|
+
tools: liteTools,
|
|
20016
|
+
instructions,
|
|
20017
|
+
requestId: opts.requestId,
|
|
20018
|
+
startedAt: opts.startedAt,
|
|
20019
|
+
idSalt: salt
|
|
20020
|
+
});
|
|
20021
|
+
body.input = built.input;
|
|
20022
|
+
const sent = body.reasoning?.effort;
|
|
20023
|
+
const codex = codexTurn({
|
|
20024
|
+
threadId: key,
|
|
20025
|
+
turnId: built.turnId,
|
|
20026
|
+
installationId: opts.installationId,
|
|
20027
|
+
installationSeed: opts.accountId ?? seed,
|
|
20028
|
+
idSalt: salt,
|
|
20029
|
+
model,
|
|
20030
|
+
effort: isCodexEffort(sent) ? sent : codexModel(model).defaultEffort,
|
|
20031
|
+
startedAt: built.startedAt
|
|
20032
|
+
});
|
|
20033
|
+
body.client_metadata = codex.clientMetadata;
|
|
20034
|
+
return { body, degradations, cacheKey: key, codex };
|
|
19426
20035
|
}
|
|
19427
20036
|
|
|
19428
20037
|
// packages/providers/src/openai/codec.ts
|
|
19429
20038
|
var OAUTH_URL2 = "https://chatgpt.com/backend-api/codex/responses";
|
|
19430
20039
|
var API_URL3 = "https://api.openai.com/v1/responses";
|
|
20040
|
+
var HEADER_SAFE = /^[\x21-\x7e]+$/;
|
|
20041
|
+
function routingHint(model, tier) {
|
|
20042
|
+
if (!HEADER_SAFE.test(model))
|
|
20043
|
+
return;
|
|
20044
|
+
if (typeof tier === "string" && HEADER_SAFE.test(tier) && !tier.includes(";")) {
|
|
20045
|
+
return `model=${model};tier=${tier}`;
|
|
20046
|
+
}
|
|
20047
|
+
return `model=${model}`;
|
|
20048
|
+
}
|
|
19431
20049
|
var openaiCodec = {
|
|
19432
20050
|
buildRequest(input) {
|
|
19433
20051
|
const oauth = input.credentials.accessToken !== null;
|
|
19434
|
-
const
|
|
19435
|
-
const
|
|
20052
|
+
const accountId = input.credentials.providerData.accountId;
|
|
20053
|
+
const secrets = oauth ? codexSecretsFrom(input.credentials.providerData) : {};
|
|
20054
|
+
const { body, degradations, cacheKey, codex } = toResponsesWire(input.request, input.model, {
|
|
20055
|
+
oauth,
|
|
20056
|
+
requestId: input.requestId,
|
|
20057
|
+
startedAt: input.requestStartedAt,
|
|
20058
|
+
accountId: typeof accountId === "string" ? accountId : undefined,
|
|
20059
|
+
installationId: secrets.installationId,
|
|
20060
|
+
idSalt: secrets.idSalt
|
|
20061
|
+
});
|
|
20062
|
+
const protocol = [];
|
|
20063
|
+
if (codex !== undefined) {
|
|
20064
|
+
protocol.push(["x-codex-window-id", codex.windowId], ["x-codex-turn-metadata", codex.turnMetadata]);
|
|
20065
|
+
if (codexModel(input.model).lite) {
|
|
20066
|
+
protocol.push(["x-openai-internal-codex-responses-lite", "true"]);
|
|
20067
|
+
}
|
|
20068
|
+
const hint = routingHint(input.model, body.service_tier);
|
|
20069
|
+
if (hint !== undefined)
|
|
20070
|
+
protocol.push(["x-codex-routing-hint", hint]);
|
|
20071
|
+
}
|
|
20072
|
+
protocol.push(["x-client-request-id", cacheKey], ["session-id", cacheKey], ["thread-id", cacheKey]);
|
|
20073
|
+
if (oauth)
|
|
20074
|
+
protocol.push(["content-encoding", "zstd"]);
|
|
20075
|
+
protocol.push(["content-type", "application/json"]);
|
|
19436
20076
|
if (oauth) {
|
|
19437
20077
|
protocol.push(["authorization", `Bearer ${input.credentials.accessToken}`]);
|
|
19438
|
-
const accountId = input.credentials.providerData.accountId;
|
|
19439
20078
|
if (typeof accountId === "string")
|
|
19440
20079
|
protocol.push(["chatgpt-account-id", accountId]);
|
|
20080
|
+
protocol.push(["host", "chatgpt.com"]);
|
|
19441
20081
|
} else if (input.credentials.apiKey !== null) {
|
|
19442
20082
|
protocol.push(["authorization", `Bearer ${input.credentials.apiKey}`]);
|
|
19443
20083
|
} else {
|
|
19444
20084
|
throw input.fail("AUTH", "openai credential has no token");
|
|
19445
20085
|
}
|
|
19446
|
-
protocol.push(["x-client-request-id", cacheKey], ["session-id", cacheKey], ["thread-id", cacheKey]);
|
|
19447
20086
|
const headers = orderHeaders(mergeHeaders(openaiProfile.headers, protocol), openaiProfile.order);
|
|
19448
|
-
const include =
|
|
20087
|
+
const include = input.request.vendor?.openai?.include;
|
|
19449
20088
|
const nativeReasoning = Array.isArray(include) && include.includes("reasoning.encrypted_content");
|
|
19450
20089
|
return {
|
|
19451
20090
|
decodeState: { nativeReasoning },
|
|
@@ -19453,7 +20092,8 @@ var openaiCodec = {
|
|
|
19453
20092
|
url: oauth ? OAUTH_URL2 : API_URL3,
|
|
19454
20093
|
method: "POST",
|
|
19455
20094
|
headers,
|
|
19456
|
-
body: JSON.stringify(orderFields({ ...body, stream: true }, openaiBodyOrder))
|
|
20095
|
+
body: JSON.stringify(orderFields({ ...body, stream: true }, openaiBodyOrder)),
|
|
20096
|
+
...oauth ? { bodyEncoding: "zstd" } : {}
|
|
19457
20097
|
},
|
|
19458
20098
|
degradations
|
|
19459
20099
|
};
|
|
@@ -19545,11 +20185,17 @@ function createConnectFlows(deps) {
|
|
|
19545
20185
|
const provider = deps.providers[flow.provider];
|
|
19546
20186
|
if (provider === undefined)
|
|
19547
20187
|
throw new GatewayError("BAD_REQUEST", "provider does not support OAuth");
|
|
19548
|
-
const
|
|
20188
|
+
const existing = flow.credentialId === undefined ? null : await deps.store.credentials.get(flow.credentialId);
|
|
20189
|
+
if (flow.credentialId !== undefined && existing === null) {
|
|
20190
|
+
throw new GatewayError("BAD_REQUEST", "credential not found");
|
|
20191
|
+
}
|
|
20192
|
+
const prior = existing !== null && existing.provider === flow.provider ? existing.providerData : undefined;
|
|
20193
|
+
const result = await provider.exchange({
|
|
20194
|
+
code,
|
|
20195
|
+
pending: flow.pending,
|
|
20196
|
+
...prior === undefined ? {} : { providerData: prior }
|
|
20197
|
+
}, { http: deps.http, now: deps.now });
|
|
19549
20198
|
if (flow.credentialId !== undefined) {
|
|
19550
|
-
const existing = await deps.store.credentials.get(flow.credentialId);
|
|
19551
|
-
if (existing === null)
|
|
19552
|
-
throw new GatewayError("BAD_REQUEST", "credential not found");
|
|
19553
20199
|
await deps.store.credentials.updateSecrets(flow.credentialId, result.secrets, result.expiresAt);
|
|
19554
20200
|
await deps.store.credentials.update(flow.credentialId, {
|
|
19555
20201
|
enabled: true,
|
|
@@ -20821,9 +21467,20 @@ function configuredWindows(limits) {
|
|
|
20821
21467
|
async function readLimitUsage(store, id, limits, now) {
|
|
20822
21468
|
if (limits === null)
|
|
20823
21469
|
return [];
|
|
21470
|
+
const readings = await readScope(store, id, limits, null, now);
|
|
21471
|
+
for (const model of Object.keys(limits.models ?? {})) {
|
|
21472
|
+
const override = modelOverride(limits, model);
|
|
21473
|
+
if (override === undefined)
|
|
21474
|
+
continue;
|
|
21475
|
+
readings.push(...await readScope(store, id, override, model, now));
|
|
21476
|
+
}
|
|
21477
|
+
return readings;
|
|
21478
|
+
}
|
|
21479
|
+
async function readScope(store, id, limits, model, now) {
|
|
20824
21480
|
const sums = new Map;
|
|
20825
21481
|
for (const window of configuredWindows(limits)) {
|
|
20826
|
-
|
|
21482
|
+
const since = now - WINDOW_MS[window];
|
|
21483
|
+
sums.set(window, await store.usage.sumSince(id, since, model ?? undefined));
|
|
20827
21484
|
}
|
|
20828
21485
|
const readings = [];
|
|
20829
21486
|
for (const dimension of ["requests", "tokens", "spend"]) {
|
|
@@ -20836,7 +21493,7 @@ async function readLimitUsage(store, id, limits, now) {
|
|
|
20836
21493
|
continue;
|
|
20837
21494
|
const sum = sums.get(window);
|
|
20838
21495
|
const used = sum === undefined ? null : dimension === "requests" ? sum.requests : dimension === "tokens" ? sum.tokens : sum.costUsd;
|
|
20839
|
-
readings.push({ dimension, window, limit, used });
|
|
21496
|
+
readings.push({ dimension, window, limit, used, model });
|
|
20840
21497
|
}
|
|
20841
21498
|
}
|
|
20842
21499
|
if (limits.concurrency !== undefined && limits.concurrency !== null) {
|
|
@@ -20844,7 +21501,8 @@ async function readLimitUsage(store, id, limits, now) {
|
|
|
20844
21501
|
dimension: "concurrency",
|
|
20845
21502
|
window: null,
|
|
20846
21503
|
limit: limits.concurrency,
|
|
20847
|
-
used: null
|
|
21504
|
+
used: null,
|
|
21505
|
+
model
|
|
20848
21506
|
});
|
|
20849
21507
|
}
|
|
20850
21508
|
return readings;
|
|
@@ -22397,7 +23055,7 @@ function createRefresher(deps) {
|
|
|
22397
23055
|
}
|
|
22398
23056
|
// packages/control/src/pluginProviders.ts
|
|
22399
23057
|
import { join as join5 } from "path";
|
|
22400
|
-
function
|
|
23058
|
+
function isRecord9(value) {
|
|
22401
23059
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
22402
23060
|
}
|
|
22403
23061
|
function isNumber(value) {
|
|
@@ -22408,7 +23066,7 @@ function checkAt(ok, id, path, expected) {
|
|
|
22408
23066
|
throw new Error(`provider ${id} descriptor: ${path} must be ${expected}`);
|
|
22409
23067
|
}
|
|
22410
23068
|
function checkLimits(id, path, value) {
|
|
22411
|
-
checkAt(
|
|
23069
|
+
checkAt(isRecord9(value), id, path, "an object");
|
|
22412
23070
|
const limits = value;
|
|
22413
23071
|
checkAt(isNumber(limits.contextWindow), id, `${path}.contextWindow`, "a finite number");
|
|
22414
23072
|
checkAt(isNumber(limits.maxOutputTokens), id, `${path}.maxOutputTokens`, "a finite number");
|
|
@@ -22426,19 +23084,19 @@ function checkDescriptor(id, record) {
|
|
|
22426
23084
|
}
|
|
22427
23085
|
}
|
|
22428
23086
|
const capabilities = record.capabilities;
|
|
22429
|
-
checkAt(
|
|
23087
|
+
checkAt(isRecord9(capabilities), id, "capabilities", "an object");
|
|
22430
23088
|
for (const flag of ["tools", "images", "reasoning"]) {
|
|
22431
23089
|
const value = capabilities[flag];
|
|
22432
23090
|
checkAt(typeof value === "boolean", id, `capabilities.${flag}`, "a boolean");
|
|
22433
23091
|
}
|
|
22434
23092
|
const write = record.writeOverInput;
|
|
22435
|
-
checkAt(
|
|
23093
|
+
checkAt(isRecord9(write), id, "writeOverInput", "an object");
|
|
22436
23094
|
for (const ttl of ["fiveMinute", "oneHour"]) {
|
|
22437
23095
|
const value = write[ttl];
|
|
22438
23096
|
checkAt(isNumber(value), id, `writeOverInput.${ttl}`, "a finite number");
|
|
22439
23097
|
}
|
|
22440
23098
|
const catalog = record.catalog;
|
|
22441
|
-
checkAt(
|
|
23099
|
+
checkAt(isRecord9(catalog), id, "catalog", "an object");
|
|
22442
23100
|
const entry = catalog;
|
|
22443
23101
|
checkAt(typeof entry.defaultModel === "string", id, "catalog.defaultModel", "a string");
|
|
22444
23102
|
checkAt(Array.isArray(entry.authTypes), id, "catalog.authTypes", "an array");
|
|
@@ -22449,11 +23107,11 @@ function checkDescriptor(id, record) {
|
|
|
22449
23107
|
checkAt(Array.isArray(entry.models), id, "catalog.models", "an array");
|
|
22450
23108
|
for (const [index, model] of entry.models.entries()) {
|
|
22451
23109
|
const at = `catalog.models[${index}]`;
|
|
22452
|
-
checkAt(
|
|
23110
|
+
checkAt(isRecord9(model), id, at, "an object");
|
|
22453
23111
|
const choice = model;
|
|
22454
23112
|
checkAt(typeof choice.id === "string", id, `${at}.id`, "a string");
|
|
22455
23113
|
checkAt(typeof choice.label === "string", id, `${at}.label`, "a string");
|
|
22456
|
-
checkAt(
|
|
23114
|
+
checkAt(isRecord9(choice.pricing), id, `${at}.pricing`, "an object");
|
|
22457
23115
|
const pricing = choice.pricing;
|
|
22458
23116
|
for (const field of ["input", "output", "cacheRead", "cacheWrite5m", "cacheWrite1h"]) {
|
|
22459
23117
|
checkAt(isNumber(pricing[field]), id, `${at}.pricing.${field}`, "a finite number");
|
|
@@ -22469,13 +23127,13 @@ function checkDescriptor(id, record) {
|
|
|
22469
23127
|
checkAt(typeof prefix === "string", id, `modelPrefixes[${index}]`, "a string");
|
|
22470
23128
|
}
|
|
22471
23129
|
const presentation = record.presentation;
|
|
22472
|
-
checkAt(
|
|
23130
|
+
checkAt(isRecord9(presentation), id, "presentation", "an object");
|
|
22473
23131
|
const shown = presentation;
|
|
22474
23132
|
for (const field of ["label", "tone"]) {
|
|
22475
23133
|
checkAt(typeof shown[field] === "string", id, `presentation.${field}`, "a string");
|
|
22476
23134
|
}
|
|
22477
23135
|
checkAt(isNumber(shown.order), id, "presentation.order", "a finite number");
|
|
22478
|
-
checkAt(
|
|
23136
|
+
checkAt(isRecord9(shown.colour), id, "presentation.colour", "an object");
|
|
22479
23137
|
for (const scheme of ["light", "dark"]) {
|
|
22480
23138
|
const value = shown.colour[scheme];
|
|
22481
23139
|
checkAt(typeof value === "string", id, `presentation.colour.${scheme}`, "a string");
|
|
@@ -22487,7 +23145,7 @@ function checkDescriptor(id, record) {
|
|
|
22487
23145
|
function validateOAuthFlow(id, value) {
|
|
22488
23146
|
if (value === undefined)
|
|
22489
23147
|
return;
|
|
22490
|
-
if (!
|
|
23148
|
+
if (!isRecord9(value))
|
|
22491
23149
|
throw new Error(`provider ${id} oauth must be an object`);
|
|
22492
23150
|
const kind = value.kind;
|
|
22493
23151
|
if (kind !== "pkce" && kind !== "device") {
|
|
@@ -23159,7 +23817,7 @@ function requirePositional(parsed, index, name) {
|
|
|
23159
23817
|
return value;
|
|
23160
23818
|
}
|
|
23161
23819
|
// packages/plugin-api/src/events.ts
|
|
23162
|
-
var
|
|
23820
|
+
var WINDOW_MS3 = {
|
|
23163
23821
|
"1m": 60000,
|
|
23164
23822
|
"5h": 5 * 60 * 60 * 1000,
|
|
23165
23823
|
"1w": 7 * 24 * 60 * 60 * 1000
|
|
@@ -24399,6 +25057,23 @@ var dbRestore = {
|
|
|
24399
25057
|
|
|
24400
25058
|
// apps/cli/src/commands/keys.ts
|
|
24401
25059
|
var WINDOWED = new Set(["requests", "tokens", "spend"]);
|
|
25060
|
+
var GAUGE = "concurrency";
|
|
25061
|
+
var MODELS2 = "models";
|
|
25062
|
+
function emptyLimits() {
|
|
25063
|
+
return { scope: {}, models: Object.create(null) };
|
|
25064
|
+
}
|
|
25065
|
+
function toWire2(limits) {
|
|
25066
|
+
const models = Object.keys(limits.models);
|
|
25067
|
+
if (models.length === 0)
|
|
25068
|
+
return { ...limits.scope };
|
|
25069
|
+
const out = {};
|
|
25070
|
+
for (const model of models) {
|
|
25071
|
+
const scope = limits.models[model];
|
|
25072
|
+
if (scope !== undefined)
|
|
25073
|
+
out[model] = scope;
|
|
25074
|
+
}
|
|
25075
|
+
return { ...limits.scope, [MODELS2]: out };
|
|
25076
|
+
}
|
|
24402
25077
|
var PROTO = "__proto__";
|
|
24403
25078
|
function rejectProto(flag, name) {
|
|
24404
25079
|
if (name === PROTO)
|
|
@@ -24423,6 +25098,7 @@ function parseLimitFlags(entries, flag, into = {}) {
|
|
|
24423
25098
|
throw new UsageError(`${flag} ${pair} needs a window, e.g. ${flag} ${pair}:1m=60`);
|
|
24424
25099
|
}
|
|
24425
25100
|
rejectProto(flag, pair);
|
|
25101
|
+
rejectModels(flag, pair);
|
|
24426
25102
|
limits[pair] = value;
|
|
24427
25103
|
continue;
|
|
24428
25104
|
}
|
|
@@ -24433,14 +25109,109 @@ function parseLimitFlags(entries, flag, into = {}) {
|
|
|
24433
25109
|
}
|
|
24434
25110
|
rejectProto(flag, dimension);
|
|
24435
25111
|
rejectProto(flag, window);
|
|
24436
|
-
|
|
24437
|
-
|
|
24438
|
-
windows[window] = value;
|
|
24439
|
-
limits[dimension] = windows;
|
|
25112
|
+
rejectModels(flag, dimension);
|
|
25113
|
+
setSlot(limits, dimension, window, value);
|
|
24440
25114
|
}
|
|
24441
25115
|
return limits;
|
|
24442
25116
|
}
|
|
25117
|
+
function rejectModels(flag, name) {
|
|
25118
|
+
if (name === MODELS2) {
|
|
25119
|
+
throw new UsageError(`${flag} cannot name "${MODELS2}"; per-model limits use --model-limit`);
|
|
25120
|
+
}
|
|
25121
|
+
}
|
|
25122
|
+
function setSlot(scope, dimension, window, value) {
|
|
25123
|
+
const existing = scope[dimension];
|
|
25124
|
+
const windows = typeof existing === "object" ? existing : {};
|
|
25125
|
+
windows[window] = value;
|
|
25126
|
+
scope[dimension] = windows;
|
|
25127
|
+
}
|
|
25128
|
+
function splitModelSlot(spec, flag, shape) {
|
|
25129
|
+
const malformed = () => new UsageError(`${flag} must be ${shape}, got "${spec}"`);
|
|
25130
|
+
const last = spec.lastIndexOf(":");
|
|
25131
|
+
if (last < 0)
|
|
25132
|
+
throw malformed();
|
|
25133
|
+
const tail = spec.slice(last + 1);
|
|
25134
|
+
if (tail === GAUGE) {
|
|
25135
|
+
const model = spec.slice(0, last);
|
|
25136
|
+
if (model.length === 0)
|
|
25137
|
+
throw malformed();
|
|
25138
|
+
rejectProto(flag, model);
|
|
25139
|
+
return { model, dimension: GAUGE, window: null };
|
|
25140
|
+
}
|
|
25141
|
+
if (WINDOWED.has(tail)) {
|
|
25142
|
+
throw new UsageError(`${flag} ${spec} needs a window, e.g. ${flag} ${spec}:1m`);
|
|
25143
|
+
}
|
|
25144
|
+
const head = spec.slice(0, last);
|
|
25145
|
+
const second = head.lastIndexOf(":");
|
|
25146
|
+
if (second < 0)
|
|
25147
|
+
throw malformed();
|
|
25148
|
+
const model = head.slice(0, second);
|
|
25149
|
+
const dimension = head.slice(second + 1);
|
|
25150
|
+
if (model.length === 0 || dimension.length === 0 || tail.length === 0)
|
|
25151
|
+
throw malformed();
|
|
25152
|
+
rejectProto(flag, model);
|
|
25153
|
+
rejectProto(flag, dimension);
|
|
25154
|
+
rejectProto(flag, tail);
|
|
25155
|
+
return { model, dimension, window: tail };
|
|
25156
|
+
}
|
|
25157
|
+
var MODEL_LIMIT_SHAPE = "model:dimension:window=value or model:concurrency=value";
|
|
25158
|
+
function parseModelLimitFlags(entries, into) {
|
|
25159
|
+
const flag = "--model-limit";
|
|
25160
|
+
for (const entry of entries) {
|
|
25161
|
+
const equals = entry.lastIndexOf("=");
|
|
25162
|
+
if (equals <= 0)
|
|
25163
|
+
throw new UsageError(`${flag} must be ${MODEL_LIMIT_SHAPE}, got "${entry}"`);
|
|
25164
|
+
const spec = entry.slice(0, equals);
|
|
25165
|
+
const raw = entry.slice(equals + 1);
|
|
25166
|
+
const value = Number(raw);
|
|
25167
|
+
if (raw.trim().length === 0 || !Number.isFinite(value)) {
|
|
25168
|
+
throw new UsageError(`${flag} ${spec} must be a number, got "${raw}"`);
|
|
25169
|
+
}
|
|
25170
|
+
const { model, dimension, window } = splitModelSlot(spec, flag, MODEL_LIMIT_SHAPE);
|
|
25171
|
+
const scope = into.models[model] ?? {};
|
|
25172
|
+
if (window === null)
|
|
25173
|
+
scope[dimension] = value;
|
|
25174
|
+
else
|
|
25175
|
+
setSlot(scope, dimension, window, value);
|
|
25176
|
+
into.models[model] = scope;
|
|
25177
|
+
}
|
|
25178
|
+
}
|
|
25179
|
+
function applyModelUnsetFlags(limits, entries) {
|
|
25180
|
+
const flag = "--unset-model-limit";
|
|
25181
|
+
for (const entry of entries) {
|
|
25182
|
+
if (entry.includes("=")) {
|
|
25183
|
+
throw new UsageError(`${flag} names a limit to remove, not a value, got "${entry}"`);
|
|
25184
|
+
}
|
|
25185
|
+
const { model, dimension, window } = splitModelSlot(entry, flag, "model:dimension:window or model:concurrency");
|
|
25186
|
+
const scope = limits.models[model];
|
|
25187
|
+
const missing = () => new UsageError(`this key has no ${entry} limit`);
|
|
25188
|
+
if (scope === undefined || !Object.hasOwn(scope, dimension))
|
|
25189
|
+
throw missing();
|
|
25190
|
+
if (window === null) {
|
|
25191
|
+
delete scope[dimension];
|
|
25192
|
+
} else {
|
|
25193
|
+
const windows = scope[dimension];
|
|
25194
|
+
if (typeof windows !== "object" || !Object.hasOwn(windows, window))
|
|
25195
|
+
throw missing();
|
|
25196
|
+
delete windows[window];
|
|
25197
|
+
if (Object.keys(windows).length === 0)
|
|
25198
|
+
delete scope[dimension];
|
|
25199
|
+
}
|
|
25200
|
+
if (Object.keys(scope).length === 0)
|
|
25201
|
+
delete limits.models[model];
|
|
25202
|
+
}
|
|
25203
|
+
}
|
|
24443
25204
|
function toLoose(limits) {
|
|
25205
|
+
const loose = emptyLimits();
|
|
25206
|
+
loose.scope = toLooseScope(limits);
|
|
25207
|
+
for (const [model, override] of Object.entries(limits.models ?? {})) {
|
|
25208
|
+
const scope = toLooseScope(override);
|
|
25209
|
+
if (Object.keys(scope).length > 0)
|
|
25210
|
+
loose.models[model] = scope;
|
|
25211
|
+
}
|
|
25212
|
+
return loose;
|
|
25213
|
+
}
|
|
25214
|
+
function toLooseScope(limits) {
|
|
24444
25215
|
const loose = {};
|
|
24445
25216
|
for (const dimension of ["requests", "tokens", "spend"]) {
|
|
24446
25217
|
const windows = limits[dimension];
|
|
@@ -24496,6 +25267,7 @@ function limitRow(reading) {
|
|
|
24496
25267
|
const money = reading.dimension === "spend";
|
|
24497
25268
|
const amount = (value) => money ? formatUsd(value) : String(value);
|
|
24498
25269
|
return [
|
|
25270
|
+
reading.model ?? "key",
|
|
24499
25271
|
reading.dimension,
|
|
24500
25272
|
reading.window ?? "\u2014",
|
|
24501
25273
|
amount(reading.limit),
|
|
@@ -24504,6 +25276,13 @@ function limitRow(reading) {
|
|
|
24504
25276
|
];
|
|
24505
25277
|
}
|
|
24506
25278
|
function summarizeLimits(limits) {
|
|
25279
|
+
const parts = summarizeScope(limits, "");
|
|
25280
|
+
for (const [model, override] of Object.entries(limits.models ?? {})) {
|
|
25281
|
+
parts.push(...summarizeScope(override, `${model}:`));
|
|
25282
|
+
}
|
|
25283
|
+
return parts.length === 0 ? "\u2014" : parts.join(" ");
|
|
25284
|
+
}
|
|
25285
|
+
function summarizeScope(limits, prefix) {
|
|
24507
25286
|
const parts = [];
|
|
24508
25287
|
for (const dimension of ["requests", "tokens", "spend"]) {
|
|
24509
25288
|
const windows = limits[dimension];
|
|
@@ -24512,13 +25291,13 @@ function summarizeLimits(limits) {
|
|
|
24512
25291
|
for (const [window, value] of Object.entries(windows)) {
|
|
24513
25292
|
if (value === null || value === undefined)
|
|
24514
25293
|
continue;
|
|
24515
|
-
parts.push(`${dimension}:${window}=${value}`);
|
|
25294
|
+
parts.push(`${prefix}${dimension}:${window}=${value}`);
|
|
24516
25295
|
}
|
|
24517
25296
|
}
|
|
24518
25297
|
if (limits.concurrency !== undefined && limits.concurrency !== null) {
|
|
24519
|
-
parts.push(
|
|
25298
|
+
parts.push(`${prefix}concurrency=${limits.concurrency}`);
|
|
24520
25299
|
}
|
|
24521
|
-
return parts
|
|
25300
|
+
return parts;
|
|
24522
25301
|
}
|
|
24523
25302
|
function parseWhen(flag, raw) {
|
|
24524
25303
|
const at = Date.parse(raw);
|
|
@@ -24566,22 +25345,26 @@ var keysList = {
|
|
|
24566
25345
|
}
|
|
24567
25346
|
};
|
|
24568
25347
|
var keysCreate = {
|
|
24569
|
-
usage: "keys create [--label L] [--allow <model> ...] [--limit <d>:<w>=N ...] [--expires <when>] [--no-bodies]",
|
|
25348
|
+
usage: "keys create [--label L] [--allow <model> ...] [--limit <d>:<w>=N ...] [--model-limit <model>:<d>:<w>=N ...] [--expires <when>] [--no-bodies]",
|
|
24570
25349
|
summary: "Mint a gateway API key, printed once",
|
|
24571
25350
|
options: {
|
|
24572
25351
|
label: { type: "string" },
|
|
24573
25352
|
allow: { type: "string", multiple: true },
|
|
24574
25353
|
limit: { type: "string", multiple: true },
|
|
25354
|
+
"model-limit": { type: "string", multiple: true },
|
|
24575
25355
|
expires: { type: "string" },
|
|
24576
25356
|
"no-bodies": { type: "boolean" }
|
|
24577
25357
|
},
|
|
24578
25358
|
async run(args, { ctx, writer }) {
|
|
24579
25359
|
const allow = listFlag(args.values, "allow");
|
|
24580
25360
|
const expires = stringFlag(args.values, "expires");
|
|
25361
|
+
const limits = emptyLimits();
|
|
25362
|
+
parseLimitFlags(listFlag(args.values, "limit") ?? [], "--limit", limits.scope);
|
|
25363
|
+
parseModelLimitFlags(listFlag(args.values, "model-limit") ?? [], limits);
|
|
24581
25364
|
const created = await createKey(await ctx.store(), {
|
|
24582
25365
|
...stringFlag(args.values, "label") === undefined ? {} : { label: stringFlag(args.values, "label") },
|
|
24583
25366
|
modelAllowlist: allow ?? null,
|
|
24584
|
-
limits:
|
|
25367
|
+
limits: toWire2(limits),
|
|
24585
25368
|
bodyLoggingOptOut: boolFlag(args.values, "no-bodies"),
|
|
24586
25369
|
expiresAt: expires === undefined ? null : parseWhen("--expires", expires)
|
|
24587
25370
|
});
|
|
@@ -24592,16 +25375,24 @@ var keysCreate = {
|
|
|
24592
25375
|
}
|
|
24593
25376
|
};
|
|
24594
25377
|
var keysLimits = {
|
|
24595
|
-
usage: "keys limits <id> [--set <d>:<w>=N ...] [--unset <d>:<w> ...]",
|
|
25378
|
+
usage: "keys limits <id> [--set <d>:<w>=N ...] [--unset <d>:<w> ...] [--model-limit <model>:<d>:<w>=N ...] [--unset-model-limit <model>:<d>[:<w>] ...]",
|
|
24596
25379
|
summary: "Show or edit one key's limits, with what has been used against them",
|
|
24597
25380
|
options: {
|
|
24598
25381
|
set: { type: "string", multiple: true },
|
|
24599
|
-
unset: { type: "string", multiple: true }
|
|
25382
|
+
unset: { type: "string", multiple: true },
|
|
25383
|
+
"model-limit": { type: "string", multiple: true },
|
|
25384
|
+
"unset-model-limit": { type: "string", multiple: true }
|
|
24600
25385
|
},
|
|
24601
25386
|
async run(args, { ctx, writer }) {
|
|
24602
25387
|
const id = requirePositional(args, 0, "key id");
|
|
24603
|
-
const sets =
|
|
24604
|
-
|
|
25388
|
+
const sets = [
|
|
25389
|
+
...listFlag(args.values, "set") ?? [],
|
|
25390
|
+
...listFlag(args.values, "model-limit") ?? []
|
|
25391
|
+
];
|
|
25392
|
+
const unsets = [
|
|
25393
|
+
...listFlag(args.values, "unset") ?? [],
|
|
25394
|
+
...listFlag(args.values, "unset-model-limit") ?? []
|
|
25395
|
+
];
|
|
24605
25396
|
const store = await ctx.store();
|
|
24606
25397
|
const existing = (await listKeys(store)).find((entry) => entry.id === id);
|
|
24607
25398
|
if (existing === undefined)
|
|
@@ -24616,10 +25407,12 @@ var keysLimits = {
|
|
|
24616
25407
|
replaced = true;
|
|
24617
25408
|
note(ctx, writer, paint(ctx, "yellow", "the stored limits could not be read and are being replaced"));
|
|
24618
25409
|
}
|
|
24619
|
-
const next = existing.limits === null ?
|
|
24620
|
-
applyUnsetFlags(next,
|
|
24621
|
-
|
|
24622
|
-
|
|
25410
|
+
const next = existing.limits === null ? emptyLimits() : toLoose(existing.limits);
|
|
25411
|
+
applyUnsetFlags(next.scope, listFlag(args.values, "unset") ?? []);
|
|
25412
|
+
applyModelUnsetFlags(next, listFlag(args.values, "unset-model-limit") ?? []);
|
|
25413
|
+
parseLimitFlags(listFlag(args.values, "set") ?? [], "--set", next.scope);
|
|
25414
|
+
parseModelLimitFlags(listFlag(args.values, "model-limit") ?? [], next);
|
|
25415
|
+
key = await setKeyLimits(store, id, { limits: toWire2(next) });
|
|
24623
25416
|
}
|
|
24624
25417
|
emit(ctx, writer, replaced ? { ...key, limitsReplaced: true } : key, () => {
|
|
24625
25418
|
const head = fields([
|
|
@@ -24640,6 +25433,7 @@ no limits configured; this key is unlimited`;
|
|
|
24640
25433
|
return `${head}
|
|
24641
25434
|
|
|
24642
25435
|
${table([
|
|
25436
|
+
{ header: "SCOPE" },
|
|
24643
25437
|
{ header: "DIMENSION" },
|
|
24644
25438
|
{ header: "WINDOW" },
|
|
24645
25439
|
{ header: "LIMIT", align: "right" },
|
|
@@ -25287,7 +26081,7 @@ function createServiceDeps(input) {
|
|
|
25287
26081
|
}
|
|
25288
26082
|
|
|
25289
26083
|
// apps/cli/src/version.ts
|
|
25290
|
-
var VERSION = "0.
|
|
26084
|
+
var VERSION = "0.14.0";
|
|
25291
26085
|
|
|
25292
26086
|
// apps/cli/src/commands/service.ts
|
|
25293
26087
|
function gatewayArgv(root) {
|