omnigateway 0.13.7 → 0.14.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -0
- package/bin/omni.js +904 -111
- package/gateway.js +973 -189
- package/package.json +1 -1
- package/public/assets/{Field-DG1317zr.js → Field-D9V9N-vb.js} +12 -13
- package/public/assets/{RequestTable-i3TtCB9m.js → RequestTable-BF5-R27W.js} +1 -1
- package/public/assets/{_app.accounts-2syjcpos.js → _app.accounts-C-Y0w4PQ.js} +1 -1
- package/public/assets/{_app.console-facio1_r.js → _app.console-BplICGMp.js} +1 -1
- package/public/assets/{_app.database-CqSoTi2l.js → _app.database-CKoZXAHB.js} +1 -1
- package/public/assets/_app.keys-TIg-A16v.js +107 -0
- package/public/assets/{_app.logs-PjRK5wqy.js → _app.logs-CdF9dkSn.js} +1 -1
- package/public/assets/{_app.models-BfmeeB5e.js → _app.models-CPb0eKBe.js} +1 -1
- package/public/assets/{_app.plugins._pluginId-cCDDM_D_.js → _app.plugins._pluginId-BduNUc7R.js} +1 -1
- package/public/assets/{_app.settings-CdSQyweb.js → _app.settings-dGgsENlT.js} +1 -1
- package/public/assets/client-Bx3ttI-9.js +39 -0
- package/public/assets/{index-BYDVTknB.js → index-Rh35FNqC.js} +2 -2
- package/public/assets/limits-iRyDMg04.js +1 -0
- package/public/assets/{login-BhkdR1f4.js → login-Dj7MQcIc.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",
|
|
@@ -19237,8 +19570,170 @@ async function* decodeResponses(messages, options = {}) {
|
|
|
19237
19570
|
}
|
|
19238
19571
|
}
|
|
19239
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
|
+
|
|
19240
19736
|
// packages/providers/src/openai/wire.ts
|
|
19241
|
-
import { createHash as createHash6 } from "crypto";
|
|
19242
19737
|
var MAX_CACHE_KEY_LENGTH = 1024;
|
|
19243
19738
|
var HEADER_SAFE_KEY = /^[\x21-\x7e]+$/;
|
|
19244
19739
|
function suppliedKey2(req) {
|
|
@@ -19250,9 +19745,16 @@ function suppliedKey2(req) {
|
|
|
19250
19745
|
}
|
|
19251
19746
|
return;
|
|
19252
19747
|
}
|
|
19253
|
-
function cacheKey2(req, instructions, firstInput) {
|
|
19254
|
-
const
|
|
19255
|
-
|
|
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 };
|
|
19256
19758
|
}
|
|
19257
19759
|
function repairOrphanedCalls2(input) {
|
|
19258
19760
|
const answered = new Set;
|
|
@@ -19283,6 +19785,44 @@ function encodeToolChoice7(c) {
|
|
|
19283
19785
|
return { type: "function", name: c.name };
|
|
19284
19786
|
}
|
|
19285
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
|
+
}
|
|
19286
19826
|
function toResponsesWire(req, model, opts = { oauth: false }) {
|
|
19287
19827
|
const degradations = [];
|
|
19288
19828
|
const input = [];
|
|
@@ -19290,6 +19830,12 @@ function toResponsesWire(req, model, opts = { oauth: false }) {
|
|
|
19290
19830
|
if (!degradations.includes(d))
|
|
19291
19831
|
degradations.push(d);
|
|
19292
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
|
+
};
|
|
19293
19839
|
if (req.betas?.includes(CONTEXT_1M_BETA))
|
|
19294
19840
|
note("openai:context-1m-dropped");
|
|
19295
19841
|
for (const message of req.messages) {
|
|
@@ -19301,10 +19847,15 @@ function toResponsesWire(req, model, opts = { oauth: false }) {
|
|
|
19301
19847
|
const flush = () => {
|
|
19302
19848
|
if (parts.length === 0)
|
|
19303
19849
|
return;
|
|
19304
|
-
|
|
19850
|
+
push({ type: "message", role, content: [...parts] });
|
|
19305
19851
|
parts.length = 0;
|
|
19306
19852
|
};
|
|
19853
|
+
let opened = false;
|
|
19307
19854
|
for (const block of message.content) {
|
|
19855
|
+
if (message.role === "user" && !opened && block.type !== "toolResult") {
|
|
19856
|
+
opened = true;
|
|
19857
|
+
turn++;
|
|
19858
|
+
}
|
|
19308
19859
|
switch (block.type) {
|
|
19309
19860
|
case "text":
|
|
19310
19861
|
parts.push({
|
|
@@ -19325,7 +19876,7 @@ function toResponsesWire(req, model, opts = { oauth: false }) {
|
|
|
19325
19876
|
break;
|
|
19326
19877
|
case "toolUse":
|
|
19327
19878
|
flush();
|
|
19328
|
-
|
|
19879
|
+
push({
|
|
19329
19880
|
type: "function_call",
|
|
19330
19881
|
call_id: block.id,
|
|
19331
19882
|
name: block.name,
|
|
@@ -19337,7 +19888,7 @@ function toResponsesWire(req, model, opts = { oauth: false }) {
|
|
|
19337
19888
|
note("openai:anthropic-native-block-dropped");
|
|
19338
19889
|
flush();
|
|
19339
19890
|
const files = responsesFiles(block.files ?? []);
|
|
19340
|
-
|
|
19891
|
+
push({
|
|
19341
19892
|
type: "function_call_output",
|
|
19342
19893
|
call_id: block.toolUseId,
|
|
19343
19894
|
output: withFileText(block.content, files.text)
|
|
@@ -19365,7 +19916,7 @@ function toResponsesWire(req, model, opts = { oauth: false }) {
|
|
|
19365
19916
|
if (block.provider === "openai") {
|
|
19366
19917
|
flush();
|
|
19367
19918
|
const { id: _id, ...rest } = block.data;
|
|
19368
|
-
|
|
19919
|
+
push({ type: block.blockType, ...rest });
|
|
19369
19920
|
break;
|
|
19370
19921
|
}
|
|
19371
19922
|
note("openai:anthropic-native-block-dropped");
|
|
@@ -19377,8 +19928,10 @@ function toResponsesWire(req, model, opts = { oauth: false }) {
|
|
|
19377
19928
|
repairOrphanedCalls2(input);
|
|
19378
19929
|
const body = { model, input, stream: req.stream, store: false };
|
|
19379
19930
|
const instructions = systemText(req.system, "openai", note);
|
|
19380
|
-
|
|
19931
|
+
const lite = opts.oauth && codexModel(model).lite;
|
|
19932
|
+
if (instructions !== undefined && instructions.length > 0 && !opts.oauth) {
|
|
19381
19933
|
body.instructions = instructions;
|
|
19934
|
+
}
|
|
19382
19935
|
if (req.maxTokens !== undefined) {
|
|
19383
19936
|
if (opts.oauth)
|
|
19384
19937
|
note("openai:max-tokens-dropped");
|
|
@@ -19391,22 +19944,45 @@ function toResponsesWire(req, model, opts = { oauth: false }) {
|
|
|
19391
19944
|
else
|
|
19392
19945
|
body.temperature = req.temperature;
|
|
19393
19946
|
}
|
|
19947
|
+
let liteTools;
|
|
19394
19948
|
if (req.tools !== undefined) {
|
|
19395
19949
|
const custom = req.tools.filter((t) => t.kind === "portable");
|
|
19396
19950
|
if (custom.length !== req.tools.length)
|
|
19397
19951
|
note("openai:anthropic-tool-dropped");
|
|
19398
|
-
|
|
19399
|
-
|
|
19400
|
-
|
|
19401
|
-
|
|
19402
|
-
|
|
19403
|
-
|
|
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
|
+
}
|
|
19404
19972
|
}
|
|
19405
19973
|
if (req.toolChoice !== undefined)
|
|
19406
19974
|
body.tool_choice = encodeToolChoice7(req.toolChoice);
|
|
19407
|
-
if (
|
|
19408
|
-
|
|
19409
|
-
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") {
|
|
19410
19986
|
if (req.reasoning.mode === "budget") {
|
|
19411
19987
|
degradations.push("openai:reasoning-budget-dropped");
|
|
19412
19988
|
} else {
|
|
@@ -19417,36 +19993,98 @@ function toResponsesWire(req, model, opts = { oauth: false }) {
|
|
|
19417
19993
|
};
|
|
19418
19994
|
}
|
|
19419
19995
|
}
|
|
19420
|
-
const
|
|
19421
|
-
|
|
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
|
+
}
|
|
19422
20005
|
const moved = moveToResponsesText(req, body, "openai");
|
|
19423
20006
|
if (moved !== undefined)
|
|
19424
20007
|
note(moved);
|
|
19425
20008
|
body.prompt_cache_key = key;
|
|
19426
|
-
|
|
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 };
|
|
19427
20035
|
}
|
|
19428
20036
|
|
|
19429
20037
|
// packages/providers/src/openai/codec.ts
|
|
19430
20038
|
var OAUTH_URL2 = "https://chatgpt.com/backend-api/codex/responses";
|
|
19431
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
|
+
}
|
|
19432
20049
|
var openaiCodec = {
|
|
19433
20050
|
buildRequest(input) {
|
|
19434
20051
|
const oauth = input.credentials.accessToken !== null;
|
|
19435
|
-
const
|
|
19436
|
-
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"]);
|
|
19437
20076
|
if (oauth) {
|
|
19438
20077
|
protocol.push(["authorization", `Bearer ${input.credentials.accessToken}`]);
|
|
19439
|
-
const accountId = input.credentials.providerData.accountId;
|
|
19440
20078
|
if (typeof accountId === "string")
|
|
19441
20079
|
protocol.push(["chatgpt-account-id", accountId]);
|
|
20080
|
+
protocol.push(["host", "chatgpt.com"]);
|
|
19442
20081
|
} else if (input.credentials.apiKey !== null) {
|
|
19443
20082
|
protocol.push(["authorization", `Bearer ${input.credentials.apiKey}`]);
|
|
19444
20083
|
} else {
|
|
19445
20084
|
throw input.fail("AUTH", "openai credential has no token");
|
|
19446
20085
|
}
|
|
19447
|
-
protocol.push(["x-client-request-id", cacheKey], ["session-id", cacheKey], ["thread-id", cacheKey]);
|
|
19448
20086
|
const headers = orderHeaders(mergeHeaders(openaiProfile.headers, protocol), openaiProfile.order);
|
|
19449
|
-
const include =
|
|
20087
|
+
const include = input.request.vendor?.openai?.include;
|
|
19450
20088
|
const nativeReasoning = Array.isArray(include) && include.includes("reasoning.encrypted_content");
|
|
19451
20089
|
return {
|
|
19452
20090
|
decodeState: { nativeReasoning },
|
|
@@ -19454,7 +20092,8 @@ var openaiCodec = {
|
|
|
19454
20092
|
url: oauth ? OAUTH_URL2 : API_URL3,
|
|
19455
20093
|
method: "POST",
|
|
19456
20094
|
headers,
|
|
19457
|
-
body: JSON.stringify(orderFields({ ...body, stream: true }, openaiBodyOrder))
|
|
20095
|
+
body: JSON.stringify(orderFields({ ...body, stream: true }, openaiBodyOrder)),
|
|
20096
|
+
...oauth ? { bodyEncoding: "zstd" } : {}
|
|
19458
20097
|
},
|
|
19459
20098
|
degradations
|
|
19460
20099
|
};
|
|
@@ -19546,11 +20185,17 @@ function createConnectFlows(deps) {
|
|
|
19546
20185
|
const provider = deps.providers[flow.provider];
|
|
19547
20186
|
if (provider === undefined)
|
|
19548
20187
|
throw new GatewayError("BAD_REQUEST", "provider does not support OAuth");
|
|
19549
|
-
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 });
|
|
19550
20198
|
if (flow.credentialId !== undefined) {
|
|
19551
|
-
const existing = await deps.store.credentials.get(flow.credentialId);
|
|
19552
|
-
if (existing === null)
|
|
19553
|
-
throw new GatewayError("BAD_REQUEST", "credential not found");
|
|
19554
20199
|
await deps.store.credentials.updateSecrets(flow.credentialId, result.secrets, result.expiresAt);
|
|
19555
20200
|
await deps.store.credentials.update(flow.credentialId, {
|
|
19556
20201
|
enabled: true,
|
|
@@ -20822,9 +21467,20 @@ function configuredWindows(limits) {
|
|
|
20822
21467
|
async function readLimitUsage(store, id, limits, now) {
|
|
20823
21468
|
if (limits === null)
|
|
20824
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) {
|
|
20825
21480
|
const sums = new Map;
|
|
20826
21481
|
for (const window of configuredWindows(limits)) {
|
|
20827
|
-
|
|
21482
|
+
const since = now - WINDOW_MS[window];
|
|
21483
|
+
sums.set(window, await store.usage.sumSince(id, since, model ?? undefined));
|
|
20828
21484
|
}
|
|
20829
21485
|
const readings = [];
|
|
20830
21486
|
for (const dimension of ["requests", "tokens", "spend"]) {
|
|
@@ -20837,7 +21493,7 @@ async function readLimitUsage(store, id, limits, now) {
|
|
|
20837
21493
|
continue;
|
|
20838
21494
|
const sum = sums.get(window);
|
|
20839
21495
|
const used = sum === undefined ? null : dimension === "requests" ? sum.requests : dimension === "tokens" ? sum.tokens : sum.costUsd;
|
|
20840
|
-
readings.push({ dimension, window, limit, used });
|
|
21496
|
+
readings.push({ dimension, window, limit, used, model });
|
|
20841
21497
|
}
|
|
20842
21498
|
}
|
|
20843
21499
|
if (limits.concurrency !== undefined && limits.concurrency !== null) {
|
|
@@ -20845,7 +21501,8 @@ async function readLimitUsage(store, id, limits, now) {
|
|
|
20845
21501
|
dimension: "concurrency",
|
|
20846
21502
|
window: null,
|
|
20847
21503
|
limit: limits.concurrency,
|
|
20848
|
-
used: null
|
|
21504
|
+
used: null,
|
|
21505
|
+
model
|
|
20849
21506
|
});
|
|
20850
21507
|
}
|
|
20851
21508
|
return readings;
|
|
@@ -22398,7 +23055,7 @@ function createRefresher(deps) {
|
|
|
22398
23055
|
}
|
|
22399
23056
|
// packages/control/src/pluginProviders.ts
|
|
22400
23057
|
import { join as join5 } from "path";
|
|
22401
|
-
function
|
|
23058
|
+
function isRecord9(value) {
|
|
22402
23059
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
22403
23060
|
}
|
|
22404
23061
|
function isNumber(value) {
|
|
@@ -22409,7 +23066,7 @@ function checkAt(ok, id, path, expected) {
|
|
|
22409
23066
|
throw new Error(`provider ${id} descriptor: ${path} must be ${expected}`);
|
|
22410
23067
|
}
|
|
22411
23068
|
function checkLimits(id, path, value) {
|
|
22412
|
-
checkAt(
|
|
23069
|
+
checkAt(isRecord9(value), id, path, "an object");
|
|
22413
23070
|
const limits = value;
|
|
22414
23071
|
checkAt(isNumber(limits.contextWindow), id, `${path}.contextWindow`, "a finite number");
|
|
22415
23072
|
checkAt(isNumber(limits.maxOutputTokens), id, `${path}.maxOutputTokens`, "a finite number");
|
|
@@ -22427,19 +23084,19 @@ function checkDescriptor(id, record) {
|
|
|
22427
23084
|
}
|
|
22428
23085
|
}
|
|
22429
23086
|
const capabilities = record.capabilities;
|
|
22430
|
-
checkAt(
|
|
23087
|
+
checkAt(isRecord9(capabilities), id, "capabilities", "an object");
|
|
22431
23088
|
for (const flag of ["tools", "images", "reasoning"]) {
|
|
22432
23089
|
const value = capabilities[flag];
|
|
22433
23090
|
checkAt(typeof value === "boolean", id, `capabilities.${flag}`, "a boolean");
|
|
22434
23091
|
}
|
|
22435
23092
|
const write = record.writeOverInput;
|
|
22436
|
-
checkAt(
|
|
23093
|
+
checkAt(isRecord9(write), id, "writeOverInput", "an object");
|
|
22437
23094
|
for (const ttl of ["fiveMinute", "oneHour"]) {
|
|
22438
23095
|
const value = write[ttl];
|
|
22439
23096
|
checkAt(isNumber(value), id, `writeOverInput.${ttl}`, "a finite number");
|
|
22440
23097
|
}
|
|
22441
23098
|
const catalog = record.catalog;
|
|
22442
|
-
checkAt(
|
|
23099
|
+
checkAt(isRecord9(catalog), id, "catalog", "an object");
|
|
22443
23100
|
const entry = catalog;
|
|
22444
23101
|
checkAt(typeof entry.defaultModel === "string", id, "catalog.defaultModel", "a string");
|
|
22445
23102
|
checkAt(Array.isArray(entry.authTypes), id, "catalog.authTypes", "an array");
|
|
@@ -22450,11 +23107,11 @@ function checkDescriptor(id, record) {
|
|
|
22450
23107
|
checkAt(Array.isArray(entry.models), id, "catalog.models", "an array");
|
|
22451
23108
|
for (const [index, model] of entry.models.entries()) {
|
|
22452
23109
|
const at = `catalog.models[${index}]`;
|
|
22453
|
-
checkAt(
|
|
23110
|
+
checkAt(isRecord9(model), id, at, "an object");
|
|
22454
23111
|
const choice = model;
|
|
22455
23112
|
checkAt(typeof choice.id === "string", id, `${at}.id`, "a string");
|
|
22456
23113
|
checkAt(typeof choice.label === "string", id, `${at}.label`, "a string");
|
|
22457
|
-
checkAt(
|
|
23114
|
+
checkAt(isRecord9(choice.pricing), id, `${at}.pricing`, "an object");
|
|
22458
23115
|
const pricing = choice.pricing;
|
|
22459
23116
|
for (const field of ["input", "output", "cacheRead", "cacheWrite5m", "cacheWrite1h"]) {
|
|
22460
23117
|
checkAt(isNumber(pricing[field]), id, `${at}.pricing.${field}`, "a finite number");
|
|
@@ -22470,13 +23127,13 @@ function checkDescriptor(id, record) {
|
|
|
22470
23127
|
checkAt(typeof prefix === "string", id, `modelPrefixes[${index}]`, "a string");
|
|
22471
23128
|
}
|
|
22472
23129
|
const presentation = record.presentation;
|
|
22473
|
-
checkAt(
|
|
23130
|
+
checkAt(isRecord9(presentation), id, "presentation", "an object");
|
|
22474
23131
|
const shown = presentation;
|
|
22475
23132
|
for (const field of ["label", "tone"]) {
|
|
22476
23133
|
checkAt(typeof shown[field] === "string", id, `presentation.${field}`, "a string");
|
|
22477
23134
|
}
|
|
22478
23135
|
checkAt(isNumber(shown.order), id, "presentation.order", "a finite number");
|
|
22479
|
-
checkAt(
|
|
23136
|
+
checkAt(isRecord9(shown.colour), id, "presentation.colour", "an object");
|
|
22480
23137
|
for (const scheme of ["light", "dark"]) {
|
|
22481
23138
|
const value = shown.colour[scheme];
|
|
22482
23139
|
checkAt(typeof value === "string", id, `presentation.colour.${scheme}`, "a string");
|
|
@@ -22488,7 +23145,7 @@ function checkDescriptor(id, record) {
|
|
|
22488
23145
|
function validateOAuthFlow(id, value) {
|
|
22489
23146
|
if (value === undefined)
|
|
22490
23147
|
return;
|
|
22491
|
-
if (!
|
|
23148
|
+
if (!isRecord9(value))
|
|
22492
23149
|
throw new Error(`provider ${id} oauth must be an object`);
|
|
22493
23150
|
const kind = value.kind;
|
|
22494
23151
|
if (kind !== "pkce" && kind !== "device") {
|
|
@@ -23160,7 +23817,7 @@ function requirePositional(parsed, index, name) {
|
|
|
23160
23817
|
return value;
|
|
23161
23818
|
}
|
|
23162
23819
|
// packages/plugin-api/src/events.ts
|
|
23163
|
-
var
|
|
23820
|
+
var WINDOW_MS3 = {
|
|
23164
23821
|
"1m": 60000,
|
|
23165
23822
|
"5h": 5 * 60 * 60 * 1000,
|
|
23166
23823
|
"1w": 7 * 24 * 60 * 60 * 1000
|
|
@@ -24400,6 +25057,23 @@ var dbRestore = {
|
|
|
24400
25057
|
|
|
24401
25058
|
// apps/cli/src/commands/keys.ts
|
|
24402
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
|
+
}
|
|
24403
25077
|
var PROTO = "__proto__";
|
|
24404
25078
|
function rejectProto(flag, name) {
|
|
24405
25079
|
if (name === PROTO)
|
|
@@ -24424,6 +25098,7 @@ function parseLimitFlags(entries, flag, into = {}) {
|
|
|
24424
25098
|
throw new UsageError(`${flag} ${pair} needs a window, e.g. ${flag} ${pair}:1m=60`);
|
|
24425
25099
|
}
|
|
24426
25100
|
rejectProto(flag, pair);
|
|
25101
|
+
rejectModels(flag, pair);
|
|
24427
25102
|
limits[pair] = value;
|
|
24428
25103
|
continue;
|
|
24429
25104
|
}
|
|
@@ -24434,14 +25109,109 @@ function parseLimitFlags(entries, flag, into = {}) {
|
|
|
24434
25109
|
}
|
|
24435
25110
|
rejectProto(flag, dimension);
|
|
24436
25111
|
rejectProto(flag, window);
|
|
24437
|
-
|
|
24438
|
-
|
|
24439
|
-
windows[window] = value;
|
|
24440
|
-
limits[dimension] = windows;
|
|
25112
|
+
rejectModels(flag, dimension);
|
|
25113
|
+
setSlot(limits, dimension, window, value);
|
|
24441
25114
|
}
|
|
24442
25115
|
return limits;
|
|
24443
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
|
+
}
|
|
24444
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) {
|
|
24445
25215
|
const loose = {};
|
|
24446
25216
|
for (const dimension of ["requests", "tokens", "spend"]) {
|
|
24447
25217
|
const windows = limits[dimension];
|
|
@@ -24497,6 +25267,7 @@ function limitRow(reading) {
|
|
|
24497
25267
|
const money = reading.dimension === "spend";
|
|
24498
25268
|
const amount = (value) => money ? formatUsd(value) : String(value);
|
|
24499
25269
|
return [
|
|
25270
|
+
reading.model ?? "key",
|
|
24500
25271
|
reading.dimension,
|
|
24501
25272
|
reading.window ?? "\u2014",
|
|
24502
25273
|
amount(reading.limit),
|
|
@@ -24505,6 +25276,13 @@ function limitRow(reading) {
|
|
|
24505
25276
|
];
|
|
24506
25277
|
}
|
|
24507
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) {
|
|
24508
25286
|
const parts = [];
|
|
24509
25287
|
for (const dimension of ["requests", "tokens", "spend"]) {
|
|
24510
25288
|
const windows = limits[dimension];
|
|
@@ -24513,13 +25291,13 @@ function summarizeLimits(limits) {
|
|
|
24513
25291
|
for (const [window, value] of Object.entries(windows)) {
|
|
24514
25292
|
if (value === null || value === undefined)
|
|
24515
25293
|
continue;
|
|
24516
|
-
parts.push(`${dimension}:${window}=${value}`);
|
|
25294
|
+
parts.push(`${prefix}${dimension}:${window}=${value}`);
|
|
24517
25295
|
}
|
|
24518
25296
|
}
|
|
24519
25297
|
if (limits.concurrency !== undefined && limits.concurrency !== null) {
|
|
24520
|
-
parts.push(
|
|
25298
|
+
parts.push(`${prefix}concurrency=${limits.concurrency}`);
|
|
24521
25299
|
}
|
|
24522
|
-
return parts
|
|
25300
|
+
return parts;
|
|
24523
25301
|
}
|
|
24524
25302
|
function parseWhen(flag, raw) {
|
|
24525
25303
|
const at = Date.parse(raw);
|
|
@@ -24567,22 +25345,26 @@ var keysList = {
|
|
|
24567
25345
|
}
|
|
24568
25346
|
};
|
|
24569
25347
|
var keysCreate = {
|
|
24570
|
-
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]",
|
|
24571
25349
|
summary: "Mint a gateway API key, printed once",
|
|
24572
25350
|
options: {
|
|
24573
25351
|
label: { type: "string" },
|
|
24574
25352
|
allow: { type: "string", multiple: true },
|
|
24575
25353
|
limit: { type: "string", multiple: true },
|
|
25354
|
+
"model-limit": { type: "string", multiple: true },
|
|
24576
25355
|
expires: { type: "string" },
|
|
24577
25356
|
"no-bodies": { type: "boolean" }
|
|
24578
25357
|
},
|
|
24579
25358
|
async run(args, { ctx, writer }) {
|
|
24580
25359
|
const allow = listFlag(args.values, "allow");
|
|
24581
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);
|
|
24582
25364
|
const created = await createKey(await ctx.store(), {
|
|
24583
25365
|
...stringFlag(args.values, "label") === undefined ? {} : { label: stringFlag(args.values, "label") },
|
|
24584
25366
|
modelAllowlist: allow ?? null,
|
|
24585
|
-
limits:
|
|
25367
|
+
limits: toWire2(limits),
|
|
24586
25368
|
bodyLoggingOptOut: boolFlag(args.values, "no-bodies"),
|
|
24587
25369
|
expiresAt: expires === undefined ? null : parseWhen("--expires", expires)
|
|
24588
25370
|
});
|
|
@@ -24593,16 +25375,24 @@ var keysCreate = {
|
|
|
24593
25375
|
}
|
|
24594
25376
|
};
|
|
24595
25377
|
var keysLimits = {
|
|
24596
|
-
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>] ...]",
|
|
24597
25379
|
summary: "Show or edit one key's limits, with what has been used against them",
|
|
24598
25380
|
options: {
|
|
24599
25381
|
set: { type: "string", multiple: true },
|
|
24600
|
-
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 }
|
|
24601
25385
|
},
|
|
24602
25386
|
async run(args, { ctx, writer }) {
|
|
24603
25387
|
const id = requirePositional(args, 0, "key id");
|
|
24604
|
-
const sets =
|
|
24605
|
-
|
|
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
|
+
];
|
|
24606
25396
|
const store = await ctx.store();
|
|
24607
25397
|
const existing = (await listKeys(store)).find((entry) => entry.id === id);
|
|
24608
25398
|
if (existing === undefined)
|
|
@@ -24617,10 +25407,12 @@ var keysLimits = {
|
|
|
24617
25407
|
replaced = true;
|
|
24618
25408
|
note(ctx, writer, paint(ctx, "yellow", "the stored limits could not be read and are being replaced"));
|
|
24619
25409
|
}
|
|
24620
|
-
const next = existing.limits === null ?
|
|
24621
|
-
applyUnsetFlags(next,
|
|
24622
|
-
|
|
24623
|
-
|
|
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) });
|
|
24624
25416
|
}
|
|
24625
25417
|
emit(ctx, writer, replaced ? { ...key, limitsReplaced: true } : key, () => {
|
|
24626
25418
|
const head = fields([
|
|
@@ -24641,6 +25433,7 @@ no limits configured; this key is unlimited`;
|
|
|
24641
25433
|
return `${head}
|
|
24642
25434
|
|
|
24643
25435
|
${table([
|
|
25436
|
+
{ header: "SCOPE" },
|
|
24644
25437
|
{ header: "DIMENSION" },
|
|
24645
25438
|
{ header: "WINDOW" },
|
|
24646
25439
|
{ header: "LIMIT", align: "right" },
|
|
@@ -25288,7 +26081,7 @@ function createServiceDeps(input) {
|
|
|
25288
26081
|
}
|
|
25289
26082
|
|
|
25290
26083
|
// apps/cli/src/version.ts
|
|
25291
|
-
var VERSION = "0.
|
|
26084
|
+
var VERSION = "0.14.1";
|
|
25292
26085
|
|
|
25293
26086
|
// apps/cli/src/commands/service.ts
|
|
25294
26087
|
function gatewayArgv(root) {
|