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/gateway.js
CHANGED
|
@@ -11250,12 +11250,43 @@ var spendWindows = object({
|
|
|
11250
11250
|
"5h": spendLimit.optional(),
|
|
11251
11251
|
"1w": spendLimit.optional()
|
|
11252
11252
|
}).strict();
|
|
11253
|
-
var
|
|
11253
|
+
var limitConfigBaseSchema = object({
|
|
11254
11254
|
requests: countWindows.optional(),
|
|
11255
11255
|
tokens: countWindows.optional(),
|
|
11256
11256
|
spend: spendWindows.optional(),
|
|
11257
11257
|
concurrency: countLimit.optional()
|
|
11258
11258
|
}).strict();
|
|
11259
|
+
var MAX_MODEL_OVERRIDES = 64;
|
|
11260
|
+
var MAX_MODEL_NAME_LENGTH = 256;
|
|
11261
|
+
function setsAnyLimit(scope) {
|
|
11262
|
+
if (scope.concurrency !== undefined && scope.concurrency !== null)
|
|
11263
|
+
return true;
|
|
11264
|
+
for (const dimension of ["requests", "tokens", "spend"]) {
|
|
11265
|
+
const windows = scope[dimension];
|
|
11266
|
+
if (windows === undefined)
|
|
11267
|
+
continue;
|
|
11268
|
+
for (const window2 of WINDOWS) {
|
|
11269
|
+
const limit = windows[window2];
|
|
11270
|
+
if (limit !== undefined && limit !== null)
|
|
11271
|
+
return true;
|
|
11272
|
+
}
|
|
11273
|
+
}
|
|
11274
|
+
return false;
|
|
11275
|
+
}
|
|
11276
|
+
var modelOverrideSchema = limitConfigBaseSchema.refine(setsAnyLimit, {
|
|
11277
|
+
message: "a model override must set at least one limit; omit the model to remove it"
|
|
11278
|
+
});
|
|
11279
|
+
var limitConfigSchema = limitConfigBaseSchema.extend({
|
|
11280
|
+
models: record(string2().min(1).max(MAX_MODEL_NAME_LENGTH), modelOverrideSchema).refine((models) => Object.keys(models).length <= MAX_MODEL_OVERRIDES, {
|
|
11281
|
+
message: `at most ${MAX_MODEL_OVERRIDES} model overrides per key`
|
|
11282
|
+
}).optional()
|
|
11283
|
+
}).strict();
|
|
11284
|
+
function modelOverride(limits, model) {
|
|
11285
|
+
const models = limits.models;
|
|
11286
|
+
if (models === undefined || !Object.hasOwn(models, model))
|
|
11287
|
+
return;
|
|
11288
|
+
return models[model];
|
|
11289
|
+
}
|
|
11259
11290
|
function assertNoProto(value, path) {
|
|
11260
11291
|
if (typeof value !== "object" || value === null)
|
|
11261
11292
|
return;
|
|
@@ -11372,7 +11403,22 @@ function evaluate(config, counters, now) {
|
|
|
11372
11403
|
}
|
|
11373
11404
|
}
|
|
11374
11405
|
const violation = lastToClear(violations);
|
|
11375
|
-
return { allowed: violation === null, violation, headroom };
|
|
11406
|
+
return { allowed: violation === null, violation, violations, headroom };
|
|
11407
|
+
}
|
|
11408
|
+
function combine(key, model) {
|
|
11409
|
+
const violations = [...key.violations, ...model.violations];
|
|
11410
|
+
const violation = lastToClear(violations);
|
|
11411
|
+
const headroom = { ...key.headroom };
|
|
11412
|
+
for (const dimension of WINDOWED) {
|
|
11413
|
+
const candidate = model.headroom[dimension];
|
|
11414
|
+
if (candidate === undefined)
|
|
11415
|
+
continue;
|
|
11416
|
+
const current = headroom[dimension];
|
|
11417
|
+
if (current === undefined || candidate.used / candidate.limit > current.used / current.limit) {
|
|
11418
|
+
headroom[dimension] = candidate;
|
|
11419
|
+
}
|
|
11420
|
+
}
|
|
11421
|
+
return { allowed: violation === null, violation, violations, headroom };
|
|
11376
11422
|
}
|
|
11377
11423
|
function retryAfterMs(violation, now) {
|
|
11378
11424
|
return violation === null ? 0 : Math.max(0, violation.resetAt - now);
|
|
@@ -13242,6 +13288,31 @@ var _005_log_filter_indexes_default = `-- An index for the provider filter on \`
|
|
|
13242
13288
|
CREATE INDEX idx_request_logs_provider ON request_logs (resolved_provider, at DESC, id DESC);
|
|
13243
13289
|
`;
|
|
13244
13290
|
|
|
13291
|
+
// packages/store/src/postgres/migrations/006_model_window_index.sql
|
|
13292
|
+
var _006_model_window_index_default = `-- The per-model long-window reads behind a key's model overrides, on this
|
|
13293
|
+
-- backend: \`usage.sumSince\`, \`sumBuckets\` and \`oldestSince\` narrowed to one
|
|
13294
|
+
-- requested model, read from the log because \`usage_rollup\` has no model
|
|
13295
|
+
-- column. The SQLite copy is \`018_model_window_index.sql\`, same shape.
|
|
13296
|
+
--
|
|
13297
|
+
-- Without it the planner has no prefix to seek and the narrowed sums plan as a
|
|
13298
|
+
-- parallel sequential scan of the table. Measured at 500,000 rows in one key's
|
|
13299
|
+
-- week, a fifth of them the overridden model and a hundred rows of a rare one:
|
|
13300
|
+
--
|
|
13301
|
+
-- read without index with index
|
|
13302
|
+
-- sumSince 1w, rare model 22.6ms 0.22ms
|
|
13303
|
+
-- sumSince 1w, 100k-row model 27.1ms 29.4ms
|
|
13304
|
+
-- sumBuckets 1w hour, rare 78.2ms 0.26ms
|
|
13305
|
+
-- sumBuckets 1w hour, 100k-row 89.4ms 55.5ms
|
|
13306
|
+
-- oldestSince 1w, rare 0.58ms 0.11ms
|
|
13307
|
+
--
|
|
13308
|
+
-- The heavy-model sum is a bitmap heap scan over that model's rows and does not
|
|
13309
|
+
-- beat the sequential scan it replaces: the cost is linear in the model's own
|
|
13310
|
+
-- traffic on either plan, and this index removes the part that was linear in
|
|
13311
|
+
-- everyone else's. 15MB at 500,000 rows, against 19MB for the key/time index.
|
|
13312
|
+
-- Neither partial nor covering, for the reasons the SQLite file records.
|
|
13313
|
+
CREATE INDEX idx_request_logs_key_model_at ON request_logs (api_key_id, requested_model, at);
|
|
13314
|
+
`;
|
|
13315
|
+
|
|
13245
13316
|
// packages/store/src/postgres/db.ts
|
|
13246
13317
|
var num = (v) => Number(v);
|
|
13247
13318
|
var numOrNull = (v) => v === null ? null : Number(v);
|
|
@@ -13251,7 +13322,8 @@ var MIGRATIONS = [
|
|
|
13251
13322
|
{ id: 2, sql: _002_health_measurements_default },
|
|
13252
13323
|
{ id: 3, sql: _003_key_expiry_default },
|
|
13253
13324
|
{ id: 4, sql: _004_log_keyset_default },
|
|
13254
|
-
{ id: 5, sql: _005_log_filter_indexes_default }
|
|
13325
|
+
{ id: 5, sql: _005_log_filter_indexes_default },
|
|
13326
|
+
{ id: 6, sql: _006_model_window_index_default }
|
|
13255
13327
|
];
|
|
13256
13328
|
async function openPg(url) {
|
|
13257
13329
|
const sql = new SQL({ url });
|
|
@@ -14604,6 +14676,24 @@ var sum = (row) => ({
|
|
|
14604
14676
|
tokens: row === undefined ? 0 : num(row.tokens),
|
|
14605
14677
|
costUsd: row === undefined ? 0 : row.cost_usd
|
|
14606
14678
|
});
|
|
14679
|
+
var NARROWED_SQL = {
|
|
14680
|
+
sumSince: `SELECT COUNT(*) AS requests,
|
|
14681
|
+
COALESCE(SUM(input_tokens + output_tokens
|
|
14682
|
+
+ cache_read_tokens + cache_write_tokens), 0) AS tokens,
|
|
14683
|
+
COALESCE(SUM(cost_usd), 0) AS cost_usd
|
|
14684
|
+
FROM request_logs
|
|
14685
|
+
WHERE api_key_id = $1 AND requested_model = $2 AND state = 'done' AND at >= $3`,
|
|
14686
|
+
sumBuckets: `SELECT (at / $1) * $2 AS bucket, COUNT(*) AS requests,
|
|
14687
|
+
COALESCE(SUM(input_tokens + output_tokens
|
|
14688
|
+
+ cache_read_tokens + cache_write_tokens), 0) AS tokens,
|
|
14689
|
+
COALESCE(SUM(cost_usd), 0) AS cost_usd
|
|
14690
|
+
FROM request_logs
|
|
14691
|
+
WHERE api_key_id = $3 AND requested_model = $4 AND state = 'done' AND at >= $5
|
|
14692
|
+
GROUP BY 1`,
|
|
14693
|
+
oldestSince: `SELECT MIN(at) AS at
|
|
14694
|
+
FROM request_logs
|
|
14695
|
+
WHERE api_key_id = $1 AND requested_model = $2 AND state = 'done' AND at >= $3`
|
|
14696
|
+
};
|
|
14607
14697
|
function createUsageRepo(sql, nodeId, dayOffsetMinutes) {
|
|
14608
14698
|
const complete = async (conn, log) => {
|
|
14609
14699
|
const written = (await conn.unsafe(COMPLETE, values(log, "done", nodeId))).count;
|
|
@@ -14661,7 +14751,15 @@ function createUsageRepo(sql, nodeId, dayOffsetMinutes) {
|
|
|
14661
14751
|
ORDER BY at, id LIMIT $3`, [cursor.at, cursor.id, limit]);
|
|
14662
14752
|
return rows.map(toLog);
|
|
14663
14753
|
},
|
|
14664
|
-
async sumSince(apiKeyId, sinceMs) {
|
|
14754
|
+
async sumSince(apiKeyId, sinceMs, requestedModel) {
|
|
14755
|
+
if (requestedModel !== undefined) {
|
|
14756
|
+
const narrowed = await sql.unsafe(NARROWED_SQL.sumSince, [
|
|
14757
|
+
apiKeyId,
|
|
14758
|
+
requestedModel,
|
|
14759
|
+
sinceMs
|
|
14760
|
+
]);
|
|
14761
|
+
return sum(narrowed[0]);
|
|
14762
|
+
}
|
|
14665
14763
|
const boundary = hourOf(sinceMs);
|
|
14666
14764
|
const whole = await sql.unsafe(`SELECT COALESCE(SUM(requests), 0) AS requests,
|
|
14667
14765
|
COALESCE(SUM(input_tokens + output_tokens
|
|
@@ -14683,7 +14781,18 @@ function createUsageRepo(sql, nodeId, dayOffsetMinutes) {
|
|
|
14683
14781
|
costUsd: a.costUsd + b.costUsd
|
|
14684
14782
|
};
|
|
14685
14783
|
},
|
|
14686
|
-
async sumBuckets(apiKeyId, sinceMs, grainMs) {
|
|
14784
|
+
async sumBuckets(apiKeyId, sinceMs, grainMs, requestedModel) {
|
|
14785
|
+
if (requestedModel !== undefined) {
|
|
14786
|
+
const from = grainMs === HOUR_MS ? hourOf(sinceMs) * HOUR_MS : sinceMs;
|
|
14787
|
+
const narrowed = await sql.unsafe(NARROWED_SQL.sumBuckets, [
|
|
14788
|
+
grainMs,
|
|
14789
|
+
grainMs,
|
|
14790
|
+
apiKeyId,
|
|
14791
|
+
requestedModel,
|
|
14792
|
+
from
|
|
14793
|
+
]);
|
|
14794
|
+
return narrowed.map((row) => [num(row.bucket), sum(row)]);
|
|
14795
|
+
}
|
|
14687
14796
|
const rows = grainMs === HOUR_MS ? await sql.unsafe(`SELECT hour * ${HOUR_MS} AS bucket, requests,
|
|
14688
14797
|
input_tokens + output_tokens + cache_read_tokens + cache_write_tokens
|
|
14689
14798
|
AS tokens,
|
|
@@ -14704,10 +14813,14 @@ function createUsageRepo(sql, nodeId, dayOffsetMinutes) {
|
|
|
14704
14813
|
async auditRollup() {
|
|
14705
14814
|
return auditRollup2(sql);
|
|
14706
14815
|
},
|
|
14707
|
-
async oldestSince(apiKeyId, sinceMs) {
|
|
14708
|
-
const row = (await sql.unsafe(`SELECT MIN(at) AS at
|
|
14709
|
-
|
|
14710
|
-
|
|
14816
|
+
async oldestSince(apiKeyId, sinceMs, requestedModel) {
|
|
14817
|
+
const row = (requestedModel === undefined ? await sql.unsafe(`SELECT MIN(at) AS at
|
|
14818
|
+
FROM request_logs
|
|
14819
|
+
WHERE api_key_id = $1 AND state = 'done' AND at >= $2`, [apiKeyId, sinceMs]) : await sql.unsafe(NARROWED_SQL.oldestSince, [
|
|
14820
|
+
apiKeyId,
|
|
14821
|
+
requestedModel,
|
|
14822
|
+
sinceMs
|
|
14823
|
+
]))[0];
|
|
14711
14824
|
return row === undefined ? null : numOrNull(row.at);
|
|
14712
14825
|
},
|
|
14713
14826
|
async aggregate(q) {
|
|
@@ -15989,6 +16102,42 @@ CREATE INDEX idx_request_logs_at
|
|
|
15989
16102
|
ON request_logs (at DESC, id DESC, requested_model, resolved_model, error_code);
|
|
15990
16103
|
`;
|
|
15991
16104
|
|
|
16105
|
+
// packages/store/src/sqlite/migrations/018_model_window_index.sql
|
|
16106
|
+
var _018_model_window_index_default = `-- An index for the per-model long-window reads behind a key's model overrides:
|
|
16107
|
+
-- \`usage.sumSince\`, \`sumBuckets\` and \`oldestSince\` narrowed to one requested
|
|
16108
|
+
-- model. \`usage_rollup\` has no model column, so those reads come from the log,
|
|
16109
|
+
-- and without this they walk \`idx_request_logs_key_at\` across the key's whole
|
|
16110
|
+
-- window and reject every other model's row one at a time. \`bun:sqlite\` is
|
|
16111
|
+
-- synchronous, so that walk is the event loop \u2014 and the console's key listing
|
|
16112
|
+
-- repeats it once per override per window on every fetch.
|
|
16113
|
+
--
|
|
16114
|
+
-- Key, then model, then time: one seek lands on one key's one model, and the
|
|
16115
|
+
-- window is a range from there. Measured at 500,000 rows in one key's week, a
|
|
16116
|
+
-- fifth of them the overridden model and a hundred rows of a rare one:
|
|
16117
|
+
--
|
|
16118
|
+
-- read without index with index
|
|
16119
|
+
-- sumSince 1w, rare model 45.9ms 0.03ms
|
|
16120
|
+
-- sumSince 1w, 100k-row model 56.1ms 38.1ms
|
|
16121
|
+
-- sumBuckets 1w hour, rare 46.1ms 0.07ms
|
|
16122
|
+
-- sumBuckets 1w hour, 100k-row 76.8ms 58.9ms
|
|
16123
|
+
-- oldestSince 1w, rare 0.57ms 0.00ms
|
|
16124
|
+
--
|
|
16125
|
+
-- So the read stops costing the key's traffic and starts costing the model's
|
|
16126
|
+
-- own, which is still linear: a model that carries most of a key's traffic pays
|
|
16127
|
+
-- most of what it did before. That remaining cost is the design's, stated in
|
|
16128
|
+
-- \`ARCHITECTURE.md#rate-limiting\`, not something this index claims to remove.
|
|
16129
|
+
--
|
|
16130
|
+
-- Not partial on \`state = 'done'\`, and not covering, both measured. Partial
|
|
16131
|
+
-- bought nothing (37.4ms against 38.1ms) and, once \`ANALYZE\` had run, the
|
|
16132
|
+
-- planner moved the key-wide edge read onto it as a skip-scan over every model \u2014
|
|
16133
|
+
-- a plan that degrades with the number of models a key calls. Covering the four
|
|
16134
|
+
-- token columns and cost cut the heavy case to 9.0ms, for an index nearly three
|
|
16135
|
+
-- times as wide written on every completed request, and took the same key-wide
|
|
16136
|
+
-- plan. This shape leaves every key-wide read on \`idx_request_logs_key_at\`,
|
|
16137
|
+
-- which \`migrations.test.ts\` asserts beside the narrowed plans.
|
|
16138
|
+
CREATE INDEX idx_request_logs_key_model_at ON request_logs (api_key_id, requested_model, at);
|
|
16139
|
+
`;
|
|
16140
|
+
|
|
15992
16141
|
// packages/store/src/sqlite/db.ts
|
|
15993
16142
|
var MIGRATIONS2 = [
|
|
15994
16143
|
{ id: 1, sql: _001_init_default2 },
|
|
@@ -16007,7 +16156,8 @@ var MIGRATIONS2 = [
|
|
|
16007
16156
|
{ id: 14, sql: _014_key_expiry_default },
|
|
16008
16157
|
{ id: 15, sql: _015_log_keyset_default },
|
|
16009
16158
|
{ id: 16, sql: _016_log_filter_indexes_default },
|
|
16010
|
-
{ id: 17, sql: _017_log_text_search_default }
|
|
16159
|
+
{ id: 17, sql: _017_log_text_search_default },
|
|
16160
|
+
{ id: 18, sql: _018_model_window_index_default }
|
|
16011
16161
|
];
|
|
16012
16162
|
function statements(sql) {
|
|
16013
16163
|
return sql.split(`
|
|
@@ -16451,6 +16601,24 @@ var COMPLETE2 = `INSERT INTO request_logs ${COLUMNS2} VALUES ${PLACEHOLDERS2}
|
|
|
16451
16601
|
rtk_estimated_tokens_saved = excluded.rtk_estimated_tokens_saved,
|
|
16452
16602
|
rtk_filters = excluded.rtk_filters
|
|
16453
16603
|
WHERE request_logs.state = 'pending'`;
|
|
16604
|
+
var NARROWED_SQL2 = {
|
|
16605
|
+
sumSince: `SELECT COUNT(*) AS requests,
|
|
16606
|
+
COALESCE(SUM(input_tokens + output_tokens
|
|
16607
|
+
+ cache_read_tokens + cache_write_tokens), 0) AS tokens,
|
|
16608
|
+
COALESCE(SUM(cost_usd), 0) AS cost_usd
|
|
16609
|
+
FROM request_logs
|
|
16610
|
+
WHERE api_key_id = ? AND requested_model = ? AND state = 'done' AND at >= ?`,
|
|
16611
|
+
sumBuckets: `SELECT (at / ?) * ? AS bucket, COUNT(*) AS requests,
|
|
16612
|
+
COALESCE(SUM(input_tokens + output_tokens
|
|
16613
|
+
+ cache_read_tokens + cache_write_tokens), 0) AS tokens,
|
|
16614
|
+
COALESCE(SUM(cost_usd), 0) AS cost_usd
|
|
16615
|
+
FROM request_logs
|
|
16616
|
+
WHERE api_key_id = ? AND requested_model = ? AND state = 'done' AND at >= ?
|
|
16617
|
+
GROUP BY bucket`,
|
|
16618
|
+
oldestSince: `SELECT MIN(at) AS at
|
|
16619
|
+
FROM request_logs
|
|
16620
|
+
WHERE api_key_id = ? AND requested_model = ? AND state = 'done' AND at >= ?`
|
|
16621
|
+
};
|
|
16454
16622
|
function createUsageRepo2(db, nodeId, dayOffsetMinutes = hostDayOffsetMinutes()) {
|
|
16455
16623
|
const complete = db.transaction((log) => {
|
|
16456
16624
|
if (db.run(COMPLETE2, values2(log, "done", nodeId)).changes === 0)
|
|
@@ -16523,7 +16691,15 @@ function createUsageRepo2(db, nodeId, dayOffsetMinutes = hostDayOffsetMinutes())
|
|
|
16523
16691
|
ORDER BY at, id LIMIT ?`).all(cursor.at, cursor.at, cursor.id, limit);
|
|
16524
16692
|
return rows.map(toLog2);
|
|
16525
16693
|
},
|
|
16526
|
-
async sumSince(apiKeyId, sinceMs) {
|
|
16694
|
+
async sumSince(apiKeyId, sinceMs, requestedModel) {
|
|
16695
|
+
if (requestedModel !== undefined) {
|
|
16696
|
+
const row = db.query(NARROWED_SQL2.sumSince).get(apiKeyId, requestedModel, sinceMs);
|
|
16697
|
+
return {
|
|
16698
|
+
requests: row?.requests ?? 0,
|
|
16699
|
+
tokens: row?.tokens ?? 0,
|
|
16700
|
+
costUsd: row?.cost_usd ?? 0
|
|
16701
|
+
};
|
|
16702
|
+
}
|
|
16527
16703
|
const boundary = hourOf(sinceMs);
|
|
16528
16704
|
const whole = db.query(`SELECT COALESCE(SUM(requests), 0) AS requests,
|
|
16529
16705
|
COALESCE(SUM(input_tokens + output_tokens
|
|
@@ -16543,7 +16719,15 @@ function createUsageRepo2(db, nodeId, dayOffsetMinutes = hostDayOffsetMinutes())
|
|
|
16543
16719
|
costUsd: (whole?.cost_usd ?? 0) + (edge?.cost_usd ?? 0)
|
|
16544
16720
|
};
|
|
16545
16721
|
},
|
|
16546
|
-
async sumBuckets(apiKeyId, sinceMs, grainMs) {
|
|
16722
|
+
async sumBuckets(apiKeyId, sinceMs, grainMs, requestedModel) {
|
|
16723
|
+
if (requestedModel !== undefined) {
|
|
16724
|
+
const from = grainMs === HOUR_MS ? hourOf(sinceMs) * HOUR_MS : sinceMs;
|
|
16725
|
+
const narrowed = db.query(NARROWED_SQL2.sumBuckets).all(grainMs, grainMs, apiKeyId, requestedModel, from);
|
|
16726
|
+
return narrowed.map((row) => [
|
|
16727
|
+
row.bucket,
|
|
16728
|
+
{ requests: row.requests, tokens: row.tokens, costUsd: row.cost_usd }
|
|
16729
|
+
]);
|
|
16730
|
+
}
|
|
16547
16731
|
const rows = grainMs === HOUR_MS ? db.query(`SELECT hour * ${HOUR_MS} AS bucket, requests,
|
|
16548
16732
|
input_tokens + output_tokens + cache_read_tokens + cache_write_tokens
|
|
16549
16733
|
AS tokens,
|
|
@@ -16567,10 +16751,10 @@ function createUsageRepo2(db, nodeId, dayOffsetMinutes = hostDayOffsetMinutes())
|
|
|
16567
16751
|
async auditRollup() {
|
|
16568
16752
|
return auditRollup(db);
|
|
16569
16753
|
},
|
|
16570
|
-
async oldestSince(apiKeyId, sinceMs) {
|
|
16571
|
-
const row = db.query(`SELECT MIN(at) AS at
|
|
16572
|
-
|
|
16573
|
-
|
|
16754
|
+
async oldestSince(apiKeyId, sinceMs, requestedModel) {
|
|
16755
|
+
const row = requestedModel === undefined ? db.query(`SELECT MIN(at) AS at
|
|
16756
|
+
FROM request_logs
|
|
16757
|
+
WHERE api_key_id = ? AND state = 'done' AND at >= ?`).get(apiKeyId, sinceMs) : db.query(NARROWED_SQL2.oldestSince).get(apiKeyId, requestedModel, sinceMs);
|
|
16574
16758
|
return row?.at ?? null;
|
|
16575
16759
|
},
|
|
16576
16760
|
async aggregate(q) {
|
|
@@ -16711,9 +16895,9 @@ async function createStore(opts) {
|
|
|
16711
16895
|
lastUsedByCredential: (credentialId) => handle.usage.lastUsedByCredential(credentialId),
|
|
16712
16896
|
scan: (cursor, limit) => handle.usage.scan(cursor, limit),
|
|
16713
16897
|
aggregate: (q) => handle.usage.aggregate(q),
|
|
16714
|
-
sumSince: (apiKeyId, sinceMs) => handle.usage.sumSince(apiKeyId, sinceMs),
|
|
16715
|
-
sumBuckets: (apiKeyId, sinceMs, grainMs) => handle.usage.sumBuckets(apiKeyId, sinceMs, grainMs),
|
|
16716
|
-
oldestSince: (apiKeyId, sinceMs) => handle.usage.oldestSince(apiKeyId, sinceMs),
|
|
16898
|
+
sumSince: (apiKeyId, sinceMs, requestedModel) => handle.usage.sumSince(apiKeyId, sinceMs, requestedModel),
|
|
16899
|
+
sumBuckets: (apiKeyId, sinceMs, grainMs, requestedModel) => handle.usage.sumBuckets(apiKeyId, sinceMs, grainMs, requestedModel),
|
|
16900
|
+
oldestSince: (apiKeyId, sinceMs, requestedModel) => handle.usage.oldestSince(apiKeyId, sinceMs, requestedModel),
|
|
16717
16901
|
rebuildRollup: () => handle.usage.rebuildRollup(),
|
|
16718
16902
|
auditRollup: () => handle.usage.auditRollup(),
|
|
16719
16903
|
prune: (olderThan) => handle.usage.prune(olderThan),
|
|
@@ -17938,6 +18122,22 @@ function codecFailure(id, hook, what) {
|
|
|
17938
18122
|
gatewayAuthored: true
|
|
17939
18123
|
});
|
|
17940
18124
|
}
|
|
18125
|
+
function framingAgrees(headers, body, bodyEncoding) {
|
|
18126
|
+
const named = (name) => headers.filter(([n]) => n.toLowerCase() === name);
|
|
18127
|
+
const lengths = named("content-length");
|
|
18128
|
+
const encodings = named("content-encoding");
|
|
18129
|
+
if (bodyEncoding !== undefined) {
|
|
18130
|
+
return lengths.length === 0 && encodings.length === 1 && encodings[0]?.[1].trim().toLowerCase() === bodyEncoding;
|
|
18131
|
+
}
|
|
18132
|
+
if (lengths.length > 1 || encodings.length > 1)
|
|
18133
|
+
return false;
|
|
18134
|
+
const length = lengths[0]?.[1];
|
|
18135
|
+
const bytes = new TextEncoder().encode(body).byteLength;
|
|
18136
|
+
if (length !== undefined && length !== String(bytes))
|
|
18137
|
+
return false;
|
|
18138
|
+
const encoding = encodings[0]?.[1];
|
|
18139
|
+
return encoding === undefined || encoding.trim().toLowerCase() === "identity";
|
|
18140
|
+
}
|
|
17941
18141
|
function boundedDegradations(entries) {
|
|
17942
18142
|
if (!Array.isArray(entries))
|
|
17943
18143
|
return [];
|
|
@@ -17994,23 +18194,26 @@ function codecAdapter(id, capabilities, codec, origins) {
|
|
|
17994
18194
|
fail,
|
|
17995
18195
|
...req.requestId === undefined ? {} : { requestId: req.requestId },
|
|
17996
18196
|
...req.autoCache === undefined ? {} : { autoCacheEnabled: req.autoCache },
|
|
17997
|
-
...req.maxOutputTokens === undefined ? {} : { maxOutputTokens: req.maxOutputTokens }
|
|
18197
|
+
...req.maxOutputTokens === undefined ? {} : { maxOutputTokens: req.maxOutputTokens },
|
|
18198
|
+
...req.requestStartedAt === undefined ? {} : { requestStartedAt: req.requestStartedAt }
|
|
17998
18199
|
}));
|
|
17999
18200
|
const described = built?.request ?? {};
|
|
18000
18201
|
const request = {
|
|
18001
18202
|
url: described.url,
|
|
18002
18203
|
method: described.method,
|
|
18003
18204
|
headers: Array.isArray(described.headers) ? [...described.headers] : described.headers,
|
|
18004
|
-
body: described.body
|
|
18205
|
+
body: described.body,
|
|
18206
|
+
bodyEncoding: described.bodyEncoding
|
|
18005
18207
|
};
|
|
18006
|
-
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")) {
|
|
18208
|
+
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)) {
|
|
18007
18209
|
throw codecFailure(id, "buildRequest", "did not return a usable request");
|
|
18008
18210
|
}
|
|
18009
18211
|
const sendable = {
|
|
18010
18212
|
url: request.url,
|
|
18011
18213
|
method: request.method,
|
|
18012
18214
|
headers: request.headers,
|
|
18013
|
-
body: request.body
|
|
18215
|
+
body: request.body,
|
|
18216
|
+
...request.bodyEncoding === "zstd" ? { bodyEncoding: request.bodyEncoding } : {}
|
|
18014
18217
|
};
|
|
18015
18218
|
if (origins !== undefined && !withinOrigins(request.url, origins)) {
|
|
18016
18219
|
throw codecFailure(id, "buildRequest", "described a request to an origin its manifest does not declare");
|
|
@@ -18368,53 +18571,61 @@ var museBodyOrder = [
|
|
|
18368
18571
|
];
|
|
18369
18572
|
|
|
18370
18573
|
// packages/providers/src/openai/profile.ts
|
|
18371
|
-
var OPENAI_CLI_VERSION = env("OMNI_OPENAI_CLI_VERSION", "0.
|
|
18574
|
+
var OPENAI_CLI_VERSION = env("OMNI_OPENAI_CLI_VERSION", "0.163.0");
|
|
18372
18575
|
var OPENAI_UA_PLATFORM = env("OMNI_OPENAI_UA_PLATFORM", "Windows 10.0.26200");
|
|
18373
18576
|
var OPENAI_UA_ARCH = env("OMNI_OPENAI_UA_ARCH", "x86_64");
|
|
18374
18577
|
var OPENAI_UA_TERMINAL = env("OMNI_OPENAI_UA_TERMINAL", "WindowsTerminal");
|
|
18375
|
-
var
|
|
18578
|
+
var OPENAI_ORIGINATOR = env("OMNI_OPENAI_ORIGINATOR", "codex-tui");
|
|
18579
|
+
var OPENAI_SANDBOX = OPENAI_UA_PLATFORM.startsWith("Windows") ? "windows_elevated" : /^(Mac OS|macOS)/.test(OPENAI_UA_PLATFORM) ? "seatbelt" : "seccomp";
|
|
18376
18580
|
var openaiProfile = {
|
|
18377
18581
|
headers: [
|
|
18378
18582
|
[
|
|
18379
18583
|
"user-agent",
|
|
18380
|
-
env("OMNI_UA_OPENAI", `${
|
|
18584
|
+
env("OMNI_UA_OPENAI", `${OPENAI_ORIGINATOR}/${OPENAI_CLI_VERSION} (${OPENAI_UA_PLATFORM}; ${OPENAI_UA_ARCH}) ` + `${OPENAI_UA_TERMINAL} (${OPENAI_ORIGINATOR}; ${OPENAI_CLI_VERSION})`)
|
|
18381
18585
|
],
|
|
18382
|
-
["originator",
|
|
18586
|
+
["originator", OPENAI_ORIGINATOR],
|
|
18383
18587
|
["version", OPENAI_CLI_VERSION],
|
|
18384
18588
|
["x-codex-beta-features", "remote_compaction_v2"],
|
|
18385
18589
|
["accept", "text/event-stream"]
|
|
18386
18590
|
],
|
|
18387
18591
|
order: envOrder("OMNI_ORDER_OPENAI", [
|
|
18388
|
-
"host",
|
|
18389
18592
|
"version",
|
|
18390
18593
|
"x-codex-beta-features",
|
|
18391
|
-
"
|
|
18594
|
+
"x-codex-window-id",
|
|
18595
|
+
"x-codex-turn-metadata",
|
|
18596
|
+
"x-openai-internal-codex-responses-lite",
|
|
18597
|
+
"x-codex-routing-hint",
|
|
18392
18598
|
"x-client-request-id",
|
|
18393
18599
|
"session-id",
|
|
18394
18600
|
"thread-id",
|
|
18395
18601
|
"accept",
|
|
18602
|
+
"content-encoding",
|
|
18396
18603
|
"content-type",
|
|
18397
18604
|
"authorization",
|
|
18398
18605
|
"chatgpt-account-id",
|
|
18606
|
+
"originator",
|
|
18399
18607
|
"user-agent",
|
|
18400
|
-
"
|
|
18608
|
+
"host",
|
|
18401
18609
|
"content-length"
|
|
18402
18610
|
])
|
|
18403
18611
|
};
|
|
18404
18612
|
var openaiBodyOrder = [
|
|
18405
18613
|
"model",
|
|
18406
18614
|
"stream",
|
|
18615
|
+
"service_tier",
|
|
18407
18616
|
"input",
|
|
18408
18617
|
"instructions",
|
|
18409
|
-
"store",
|
|
18410
|
-
"reasoning",
|
|
18411
|
-
"prompt_cache_key",
|
|
18412
18618
|
"tools",
|
|
18413
18619
|
"tool_choice",
|
|
18620
|
+
"parallel_tool_calls",
|
|
18621
|
+
"reasoning",
|
|
18622
|
+
"store",
|
|
18623
|
+
"stream_options",
|
|
18414
18624
|
"include",
|
|
18415
|
-
"
|
|
18625
|
+
"prompt_cache_key",
|
|
18626
|
+
"text",
|
|
18416
18627
|
"client_metadata",
|
|
18417
|
-
"
|
|
18628
|
+
"access_programs",
|
|
18418
18629
|
"metadata"
|
|
18419
18630
|
];
|
|
18420
18631
|
|
|
@@ -22226,6 +22437,81 @@ function parseMuseUsage(value, now) {
|
|
|
22226
22437
|
]);
|
|
22227
22438
|
}
|
|
22228
22439
|
|
|
22440
|
+
// packages/providers/src/openai/codexIdentity.ts
|
|
22441
|
+
import { createHash as createHash3, createHmac } from "crypto";
|
|
22442
|
+
var NAMESPACE_OID = "6ba7b812-9dad-11d1-80b4-00c04fd430c8";
|
|
22443
|
+
var UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
22444
|
+
var WINDOW_END_MS = Date.UTC(2026, 9, 1);
|
|
22445
|
+
var WINDOW_MS2 = 365 * 24 * 60 * 60 * 1000;
|
|
22446
|
+
function isUuid(value) {
|
|
22447
|
+
return UUID.test(value);
|
|
22448
|
+
}
|
|
22449
|
+
function format(bytes) {
|
|
22450
|
+
const hex = Buffer.from(bytes).toString("hex");
|
|
22451
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20, 32)}`;
|
|
22452
|
+
}
|
|
22453
|
+
function withVersion(bytes, version) {
|
|
22454
|
+
const out = Uint8Array.from(bytes.subarray(0, 16));
|
|
22455
|
+
out[6] = (out[6] ?? 0) & 15 | version << 4;
|
|
22456
|
+
out[8] = (out[8] ?? 0) & 63 | 128;
|
|
22457
|
+
return out;
|
|
22458
|
+
}
|
|
22459
|
+
var sha256 = (seed) => createHash3("sha256").update(seed).digest();
|
|
22460
|
+
function uuidV5(namespace, name) {
|
|
22461
|
+
const digest = createHash3("sha1").update(Buffer.from(namespace.replace(/-/g, ""), "hex")).update(name, "utf8").digest();
|
|
22462
|
+
return format(withVersion(digest, 5));
|
|
22463
|
+
}
|
|
22464
|
+
function uuidV4From(seed) {
|
|
22465
|
+
return format(withVersion(sha256(seed), 4));
|
|
22466
|
+
}
|
|
22467
|
+
function uuidV7From(seed, timestampMs) {
|
|
22468
|
+
const bytes = Uint8Array.from(sha256(seed).subarray(0, 16));
|
|
22469
|
+
let ts = Math.max(0, Math.floor(timestampMs));
|
|
22470
|
+
for (let i = 5;i >= 0; i--) {
|
|
22471
|
+
bytes[i] = ts % 256;
|
|
22472
|
+
ts = Math.floor(ts / 256);
|
|
22473
|
+
}
|
|
22474
|
+
return format(withVersion(bytes, 7));
|
|
22475
|
+
}
|
|
22476
|
+
function threadIdFrom(seed) {
|
|
22477
|
+
const digest = sha256(`thread:${seed}`);
|
|
22478
|
+
const offset = digest.readUIntBE(0, 6) % WINDOW_MS2;
|
|
22479
|
+
return uuidV7From(seed, WINDOW_END_MS - WINDOW_MS2 + offset);
|
|
22480
|
+
}
|
|
22481
|
+
function threadInstant(threadId, now) {
|
|
22482
|
+
const hex = threadId.replace(/-/g, "");
|
|
22483
|
+
const version = hex[12];
|
|
22484
|
+
const ts = Number.parseInt(hex.slice(0, 12), 16);
|
|
22485
|
+
if (version === "7" && Number.isFinite(ts) && ts <= now)
|
|
22486
|
+
return ts;
|
|
22487
|
+
const digest = sha256(`thread:${threadId}`);
|
|
22488
|
+
return WINDOW_END_MS - WINDOW_MS2 + digest.readUIntBE(0, 6) % WINDOW_MS2;
|
|
22489
|
+
}
|
|
22490
|
+
var ID_SALT = /^[0-9a-f]{64}$/;
|
|
22491
|
+
function codexSecretsFrom(providerData) {
|
|
22492
|
+
const { installationId, idSalt } = providerData;
|
|
22493
|
+
return {
|
|
22494
|
+
...typeof installationId === "string" && isUuid(installationId) ? { installationId } : {},
|
|
22495
|
+
...typeof idSalt === "string" && ID_SALT.test(idSalt) ? { idSalt } : {}
|
|
22496
|
+
};
|
|
22497
|
+
}
|
|
22498
|
+
function ensureCodexSecrets(providerData) {
|
|
22499
|
+
const stored = codexSecretsFrom(providerData);
|
|
22500
|
+
const salt = () => Buffer.from(globalThis.crypto.getRandomValues(new Uint8Array(32))).toString("hex");
|
|
22501
|
+
return {
|
|
22502
|
+
installationId: stored.installationId ?? globalThis.crypto.randomUUID(),
|
|
22503
|
+
idSalt: stored.idSalt ?? salt()
|
|
22504
|
+
};
|
|
22505
|
+
}
|
|
22506
|
+
function keyed(salt, seed) {
|
|
22507
|
+
if (salt === undefined)
|
|
22508
|
+
return seed;
|
|
22509
|
+
return createHmac("sha256", salt).update(seed).digest("hex");
|
|
22510
|
+
}
|
|
22511
|
+
function prefixNamespace(threadId) {
|
|
22512
|
+
return uuidV5(NAMESPACE_OID, threadId);
|
|
22513
|
+
}
|
|
22514
|
+
|
|
22229
22515
|
// packages/providers/src/openai/oauth.ts
|
|
22230
22516
|
var CLIENT_ID6 = "app_EMoamEEZ73f0CkXaXp7hrann";
|
|
22231
22517
|
var AUTHORIZE_URL3 = "https://auth.openai.com/oauth/authorize";
|
|
@@ -22286,7 +22572,7 @@ async function* postToken4(body, fail) {
|
|
|
22286
22572
|
}
|
|
22287
22573
|
return token;
|
|
22288
22574
|
}
|
|
22289
|
-
function toResult6(token, fallbackRefresh, now, fallbackAccountId
|
|
22575
|
+
function toResult6(token, fallbackRefresh, now, fallbackAccountId, prior) {
|
|
22290
22576
|
const claims = decodeClaims(token.idToken);
|
|
22291
22577
|
return {
|
|
22292
22578
|
secrets: {
|
|
@@ -22297,7 +22583,10 @@ function toResult6(token, fallbackRefresh, now, fallbackAccountId = null) {
|
|
|
22297
22583
|
},
|
|
22298
22584
|
expiresAt: token.expiresIn === null ? null : now() + token.expiresIn * 1000,
|
|
22299
22585
|
accountEmail: claims.email,
|
|
22300
|
-
providerData: {
|
|
22586
|
+
providerData: {
|
|
22587
|
+
accountId: claims.accountId ?? fallbackAccountId,
|
|
22588
|
+
...ensureCodexSecrets(prior)
|
|
22589
|
+
}
|
|
22301
22590
|
};
|
|
22302
22591
|
}
|
|
22303
22592
|
function windowSecondsOf(value) {
|
|
@@ -22354,11 +22643,11 @@ var openaiOAuthFlow = {
|
|
|
22354
22643
|
url.searchParams.set("id_token_add_organizations", "true");
|
|
22355
22644
|
url.searchParams.set("codex_cli_simplified_flow", "true");
|
|
22356
22645
|
url.searchParams.set("state", state);
|
|
22357
|
-
url.searchParams.set("originator",
|
|
22646
|
+
url.searchParams.set("originator", OPENAI_ORIGINATOR);
|
|
22358
22647
|
url.searchParams.set("prompt", "login");
|
|
22359
22648
|
return { authorizeUrl: url.toString(), pending: { verifier, challenge, state, redirectUri } };
|
|
22360
22649
|
},
|
|
22361
|
-
async* exchange({ code, pending, fail, now }) {
|
|
22650
|
+
async* exchange({ code, pending, providerData, fail, now }) {
|
|
22362
22651
|
const [rawCode, pastedState] = code.split("#");
|
|
22363
22652
|
if (pastedState !== undefined && pastedState !== pending.state) {
|
|
22364
22653
|
throw fail("AUTH", "authorization state mismatch");
|
|
@@ -22369,11 +22658,11 @@ var openaiOAuthFlow = {
|
|
|
22369
22658
|
redirect_uri: pending.redirectUri,
|
|
22370
22659
|
code_verifier: pending.verifier
|
|
22371
22660
|
}, fail);
|
|
22372
|
-
return toResult6(token, null, now);
|
|
22661
|
+
return toResult6(token, null, now, null, providerData ?? {});
|
|
22373
22662
|
},
|
|
22374
22663
|
async* refresh({ refreshToken, providerData, fail, now }) {
|
|
22375
22664
|
const token = yield* postToken4({ grant_type: "refresh_token", refresh_token: refreshToken, scope: SCOPES4 }, fail);
|
|
22376
|
-
return toResult6(token, refreshToken, now, accountIdFromProviderData(providerData));
|
|
22665
|
+
return toResult6(token, refreshToken, now, accountIdFromProviderData(providerData), providerData);
|
|
22377
22666
|
},
|
|
22378
22667
|
async* usage({ secrets, providerData, now }) {
|
|
22379
22668
|
if (secrets.accessToken === null)
|
|
@@ -23123,7 +23412,7 @@ var customCodec = {
|
|
|
23123
23412
|
// packages/providers/src/custom/index.ts
|
|
23124
23413
|
var customAdapter = codecAdapter("custom", customDescriptor.capabilities, customCodec);
|
|
23125
23414
|
// packages/providers/src/grok/codec.ts
|
|
23126
|
-
import { createHash as
|
|
23415
|
+
import { createHash as createHash5 } from "crypto";
|
|
23127
23416
|
|
|
23128
23417
|
// packages/providers/src/grok/decode.ts
|
|
23129
23418
|
var ERROR_CODE2 = {
|
|
@@ -23322,7 +23611,7 @@ async function* decodeGrokResponses(messages) {
|
|
|
23322
23611
|
}
|
|
23323
23612
|
|
|
23324
23613
|
// packages/providers/src/grok/wire.ts
|
|
23325
|
-
import { createHash as
|
|
23614
|
+
import { createHash as createHash4 } from "crypto";
|
|
23326
23615
|
var MAX_TOOLS = 200;
|
|
23327
23616
|
function encodeToolChoice3(c) {
|
|
23328
23617
|
switch (c.type) {
|
|
@@ -23338,7 +23627,7 @@ function encodeToolChoice3(c) {
|
|
|
23338
23627
|
}
|
|
23339
23628
|
function promptCacheKey(instructions, firstInput) {
|
|
23340
23629
|
const stable = JSON.stringify({ instructions, firstInput });
|
|
23341
|
-
return
|
|
23630
|
+
return createHash4("sha256").update(stable).digest("hex").slice(0, 32);
|
|
23342
23631
|
}
|
|
23343
23632
|
function toGrokWire(req, model) {
|
|
23344
23633
|
const degradations = [];
|
|
@@ -23478,7 +23767,7 @@ var PROXY_URL = "https://cli-chat-proxy.grok.com/v1/responses";
|
|
|
23478
23767
|
var API_URL = "https://api.x.ai/v1/responses";
|
|
23479
23768
|
var CONV_NAMESPACE = "omni-grok-conv";
|
|
23480
23769
|
function derivedUuid(value) {
|
|
23481
|
-
const h =
|
|
23770
|
+
const h = createHash5("sha256").update(`${CONV_NAMESPACE}:${value}`).digest("hex");
|
|
23482
23771
|
const variant = (Number.parseInt(h[16], 16) & 3 | 8).toString(16);
|
|
23483
23772
|
return [
|
|
23484
23773
|
h.slice(0, 8),
|
|
@@ -23538,11 +23827,12 @@ var grokAdapter = codecAdapter("grok", grokDescriptor.capabilities, grokCodec);
|
|
|
23538
23827
|
import { request as httpRequest } from "http";
|
|
23539
23828
|
import { request as httpsRequest } from "https";
|
|
23540
23829
|
import { Readable } from "stream";
|
|
23830
|
+
import * as zlib from "zlib";
|
|
23541
23831
|
var CONNECT_ATTEMPT_TIMEOUT_MS = 3000;
|
|
23542
23832
|
function nodeHttpClient(options = {}) {
|
|
23543
23833
|
const logger = options.logger ?? noopLogger;
|
|
23544
23834
|
const now = options.now ?? (() => Date.now());
|
|
23545
|
-
return (req) => new Promise((resolve, reject) => {
|
|
23835
|
+
return (req) => encodeBody(req).then((bodyBytes) => new Promise((resolve, reject) => {
|
|
23546
23836
|
const url = new URL(req.url);
|
|
23547
23837
|
const startedAt = now();
|
|
23548
23838
|
let traced = false;
|
|
@@ -23573,7 +23863,6 @@ function nodeHttpClient(options = {}) {
|
|
|
23573
23863
|
});
|
|
23574
23864
|
};
|
|
23575
23865
|
const send = url.protocol === "https:" ? httpsRequest : httpRequest;
|
|
23576
|
-
const bodyBytes = Buffer.from(req.body, "utf8");
|
|
23577
23866
|
const headers = {};
|
|
23578
23867
|
for (const [name, value] of req.headers)
|
|
23579
23868
|
headers[name] = value;
|
|
@@ -23631,6 +23920,22 @@ function nodeHttpClient(options = {}) {
|
|
|
23631
23920
|
if (bodyBytes.byteLength > 0)
|
|
23632
23921
|
outgoing.write(bodyBytes);
|
|
23633
23922
|
outgoing.end();
|
|
23923
|
+
}));
|
|
23924
|
+
}
|
|
23925
|
+
function encodeBody(req) {
|
|
23926
|
+
const raw = Buffer.from(req.body, "utf8");
|
|
23927
|
+
if (req.bodyEncoding !== "zstd")
|
|
23928
|
+
return Promise.resolve(raw);
|
|
23929
|
+
return zstdEncode(raw);
|
|
23930
|
+
}
|
|
23931
|
+
function zstdEncode(raw, create = (options) => zlib.createZstdCompress(options)) {
|
|
23932
|
+
return new Promise((resolve, reject) => {
|
|
23933
|
+
const stream = create({ params: { [zlib.constants.ZSTD_c_compressionLevel]: 3 } });
|
|
23934
|
+
const chunks = [];
|
|
23935
|
+
stream.on("data", (chunk) => chunks.push(chunk));
|
|
23936
|
+
stream.on("end", () => resolve(Buffer.concat(chunks)));
|
|
23937
|
+
stream.on("error", reject);
|
|
23938
|
+
stream.end(raw);
|
|
23634
23939
|
});
|
|
23635
23940
|
}
|
|
23636
23941
|
function hasHeader(req, lowerName) {
|
|
@@ -24473,7 +24778,7 @@ async function* decodeMuseResponses(messages, options = {}) {
|
|
|
24473
24778
|
}
|
|
24474
24779
|
|
|
24475
24780
|
// packages/providers/src/muse/wire.ts
|
|
24476
|
-
import { createHash as
|
|
24781
|
+
import { createHash as createHash6 } from "crypto";
|
|
24477
24782
|
function suppliedKey(req) {
|
|
24478
24783
|
const vendor = req.vendor?.openai;
|
|
24479
24784
|
for (const name of ["prompt_cache_key", "session_id"]) {
|
|
@@ -24485,7 +24790,7 @@ function suppliedKey(req) {
|
|
|
24485
24790
|
}
|
|
24486
24791
|
function cacheKey(req, instructions, firstInput) {
|
|
24487
24792
|
const stable = req.conversationId !== undefined && req.conversationId.length > 0 ? JSON.stringify({ conversation: req.conversationId }) : JSON.stringify({ instructions, firstInput: firstInput ?? null });
|
|
24488
|
-
return
|
|
24793
|
+
return createHash6("sha256").update(stable).digest("hex").slice(0, 32);
|
|
24489
24794
|
}
|
|
24490
24795
|
function repairOrphanedCalls(input) {
|
|
24491
24796
|
const answered = new Set;
|
|
@@ -24685,6 +24990,49 @@ var museCodec = {
|
|
|
24685
24990
|
|
|
24686
24991
|
// packages/providers/src/muse/index.ts
|
|
24687
24992
|
var museAdapter = codecAdapter("muse", museDescriptor.capabilities, museCodec);
|
|
24993
|
+
// packages/providers/src/openai/codexCatalog.ts
|
|
24994
|
+
var CODEX_EFFORTS = new Set([
|
|
24995
|
+
"none",
|
|
24996
|
+
"minimal",
|
|
24997
|
+
"low",
|
|
24998
|
+
"medium",
|
|
24999
|
+
"high",
|
|
25000
|
+
"xhigh",
|
|
25001
|
+
"max",
|
|
25002
|
+
"ultra",
|
|
25003
|
+
"persistent"
|
|
25004
|
+
]);
|
|
25005
|
+
function isCodexEffort(value) {
|
|
25006
|
+
return typeof value === "string" && CODEX_EFFORTS.has(value);
|
|
25007
|
+
}
|
|
25008
|
+
var entry = (defaultEffort, nodeReplAutoReviewRequired, over = {}) => ({
|
|
25009
|
+
lite: true,
|
|
25010
|
+
verbosity: "low",
|
|
25011
|
+
defaultEffort,
|
|
25012
|
+
lowestEffort: "low",
|
|
25013
|
+
nodeReplAutoReviewRequired,
|
|
25014
|
+
nodeReplDisabled: false,
|
|
25015
|
+
supportsParallelToolCalls: true,
|
|
25016
|
+
...over
|
|
25017
|
+
});
|
|
25018
|
+
var UNLISTED = entry("low", false);
|
|
25019
|
+
var MODELS = Object.assign(Object.create(null), {
|
|
25020
|
+
"gpt-6-astra": entry("low", true),
|
|
25021
|
+
"gpt-6.1-sol": entry("low", true),
|
|
25022
|
+
"gpt-6-sol": entry("medium", true),
|
|
25023
|
+
"gpt-6-luna": entry("medium", false),
|
|
25024
|
+
"gpt-5.6-sol": entry("low", false),
|
|
25025
|
+
"gpt-5.6-terra": entry("medium", false),
|
|
25026
|
+
"gpt-5.6-luna": entry("medium", false),
|
|
25027
|
+
"gpt-daybreak-blue-latest": entry("low", false),
|
|
25028
|
+
"gpt-daybreak-red-latest": entry("medium", false, { verbosity: "high" }),
|
|
25029
|
+
"gpt-5.5": entry("medium", false, { lite: false }),
|
|
25030
|
+
"codex-auto-review": entry("medium", false)
|
|
25031
|
+
});
|
|
25032
|
+
function codexModel(model) {
|
|
25033
|
+
return MODELS[model] ?? UNLISTED;
|
|
25034
|
+
}
|
|
25035
|
+
|
|
24688
25036
|
// packages/providers/src/openai/decode.ts
|
|
24689
25037
|
var ERROR_CODE4 = {
|
|
24690
25038
|
rate_limit_exceeded: "RATE_LIMIT",
|
|
@@ -24902,8 +25250,170 @@ async function* decodeResponses(messages, options = {}) {
|
|
|
24902
25250
|
}
|
|
24903
25251
|
}
|
|
24904
25252
|
|
|
25253
|
+
// packages/providers/src/openai/codexLite.ts
|
|
25254
|
+
var PASSTHROUGH = "internal_chat_message_metadata_passthrough";
|
|
25255
|
+
function turnId(threadId, index, threadAt, salt) {
|
|
25256
|
+
return uuidV7From(keyed(salt, `${threadId}:turn:${index}`), threadAt + index);
|
|
25257
|
+
}
|
|
25258
|
+
function contentKinds(content, role) {
|
|
25259
|
+
if (!Array.isArray(content))
|
|
25260
|
+
return;
|
|
25261
|
+
if (role === "assistant")
|
|
25262
|
+
return content.map(() => "unknown");
|
|
25263
|
+
const kinds = [];
|
|
25264
|
+
for (const part of content) {
|
|
25265
|
+
const type = part.type;
|
|
25266
|
+
if (type === "input_text")
|
|
25267
|
+
kinds.push("user.text");
|
|
25268
|
+
else if (type === "input_image")
|
|
25269
|
+
kinds.push("user.image");
|
|
25270
|
+
else
|
|
25271
|
+
return;
|
|
25272
|
+
}
|
|
25273
|
+
return kinds;
|
|
25274
|
+
}
|
|
25275
|
+
function toLiteInput(args) {
|
|
25276
|
+
const { threadId } = args;
|
|
25277
|
+
const threadAt = threadInstant(threadId, args.startedAt ?? Number.POSITIVE_INFINITY);
|
|
25278
|
+
const startedAt = args.startedAt ?? threadAt;
|
|
25279
|
+
const historySeed = args.requestId ?? threadId;
|
|
25280
|
+
const salt = args.idSalt;
|
|
25281
|
+
const namespace = prefixNamespace(threadId);
|
|
25282
|
+
const prefix = [];
|
|
25283
|
+
if (args.tools !== undefined && args.tools.length > 0) {
|
|
25284
|
+
const tools = [{ type: "namespace", name: "functions", description: "", tools: args.tools }];
|
|
25285
|
+
prefix.push({
|
|
25286
|
+
type: "additional_tools",
|
|
25287
|
+
id: `at_${uuidV5(namespace, JSON.stringify(tools))}`,
|
|
25288
|
+
role: "developer",
|
|
25289
|
+
tools
|
|
25290
|
+
});
|
|
25291
|
+
}
|
|
25292
|
+
if (args.instructions !== undefined && args.instructions.length > 0) {
|
|
25293
|
+
prefix.push({
|
|
25294
|
+
type: "message",
|
|
25295
|
+
id: `msg_${uuidV5(namespace, args.instructions)}`,
|
|
25296
|
+
role: "developer",
|
|
25297
|
+
content: [{ type: "input_text", text: args.instructions }],
|
|
25298
|
+
[PASSTHROUGH]: { content_item_kinds: ["model.base_instructions"] }
|
|
25299
|
+
});
|
|
25300
|
+
}
|
|
25301
|
+
let turn = 0;
|
|
25302
|
+
const history = args.input.map((raw, index) => {
|
|
25303
|
+
const item = raw;
|
|
25304
|
+
turn = args.turnOf.get(item) ?? turn;
|
|
25305
|
+
const turn_id = turnId(threadId, turn, threadAt, salt);
|
|
25306
|
+
const fresh = (prefixName) => `${prefixName}_${uuidV7From(keyed(salt, `${historySeed}:item:${index}`), startedAt)}`;
|
|
25307
|
+
const create_time = (startedAt * 1000 + index) / 1e6;
|
|
25308
|
+
switch (item.type) {
|
|
25309
|
+
case "message": {
|
|
25310
|
+
const role = String(item.role);
|
|
25311
|
+
const kinds = contentKinds(item.content, role);
|
|
25312
|
+
if (role === "assistant") {
|
|
25313
|
+
return {
|
|
25314
|
+
type: "message",
|
|
25315
|
+
status: "completed",
|
|
25316
|
+
id: fresh("msg"),
|
|
25317
|
+
role,
|
|
25318
|
+
content: Array.isArray(item.content) ? item.content.map((part) => part.type === "output_text" ? { ...part, annotations: [] } : part) : item.content,
|
|
25319
|
+
[PASSTHROUGH]: {
|
|
25320
|
+
turn_id,
|
|
25321
|
+
...kinds === undefined ? {} : { content_item_kinds: kinds }
|
|
25322
|
+
}
|
|
25323
|
+
};
|
|
25324
|
+
}
|
|
25325
|
+
return {
|
|
25326
|
+
type: "message",
|
|
25327
|
+
id: fresh("msg"),
|
|
25328
|
+
role,
|
|
25329
|
+
content: item.content,
|
|
25330
|
+
[PASSTHROUGH]: {
|
|
25331
|
+
turn_id,
|
|
25332
|
+
create_time,
|
|
25333
|
+
...kinds === undefined ? {} : { content_item_kinds: kinds }
|
|
25334
|
+
}
|
|
25335
|
+
};
|
|
25336
|
+
}
|
|
25337
|
+
case "function_call":
|
|
25338
|
+
return {
|
|
25339
|
+
type: "function_call",
|
|
25340
|
+
status: "completed",
|
|
25341
|
+
id: fresh("fc"),
|
|
25342
|
+
name: item.name,
|
|
25343
|
+
arguments: item.arguments,
|
|
25344
|
+
call_id: item.call_id,
|
|
25345
|
+
[PASSTHROUGH]: { turn_id }
|
|
25346
|
+
};
|
|
25347
|
+
case "function_call_output":
|
|
25348
|
+
return {
|
|
25349
|
+
type: "function_call_output",
|
|
25350
|
+
id: fresh("fco"),
|
|
25351
|
+
call_id: item.call_id,
|
|
25352
|
+
output: item.output,
|
|
25353
|
+
[PASSTHROUGH]: { turn_id, create_time }
|
|
25354
|
+
};
|
|
25355
|
+
case "reasoning": {
|
|
25356
|
+
const { [PASSTHROUGH]: _client, ...rest } = item;
|
|
25357
|
+
return { ...rest, [PASSTHROUGH]: { turn_id } };
|
|
25358
|
+
}
|
|
25359
|
+
default:
|
|
25360
|
+
return item;
|
|
25361
|
+
}
|
|
25362
|
+
});
|
|
25363
|
+
return {
|
|
25364
|
+
input: [...prefix, ...history],
|
|
25365
|
+
turnId: turnId(threadId, turn, threadAt, salt),
|
|
25366
|
+
startedAt
|
|
25367
|
+
};
|
|
25368
|
+
}
|
|
25369
|
+
function asciiJson(value) {
|
|
25370
|
+
return JSON.stringify(value).replace(/[^\x20-\x7e]/g, (c) => `\\u${c.charCodeAt(0).toString(16).padStart(4, "0")}`);
|
|
25371
|
+
}
|
|
25372
|
+
function codexTurn(args) {
|
|
25373
|
+
const { threadId, turnId: turn } = args;
|
|
25374
|
+
const windowId = `${threadId}:0`;
|
|
25375
|
+
const installationId = args.installationId ?? uuidV4From(`installation:${args.installationSeed}`);
|
|
25376
|
+
const threadAt = threadInstant(threadId, args.startedAt);
|
|
25377
|
+
const catalog = codexModel(args.model);
|
|
25378
|
+
const turnMetadata = asciiJson({
|
|
25379
|
+
installation_id: installationId,
|
|
25380
|
+
session_id: threadId,
|
|
25381
|
+
thread_id: threadId,
|
|
25382
|
+
agent_name: "/root",
|
|
25383
|
+
turn_id: turn,
|
|
25384
|
+
window_id: windowId,
|
|
25385
|
+
window_number: 0,
|
|
25386
|
+
context_window_id: uuidV7From(keyed(args.idSalt, `${threadId}:context-window`), threadAt),
|
|
25387
|
+
request_kind: "turn",
|
|
25388
|
+
history_initialization: "new",
|
|
25389
|
+
root_turn_id: turn,
|
|
25390
|
+
thread_source: "user",
|
|
25391
|
+
turn_trigger: "user",
|
|
25392
|
+
sandbox: OPENAI_SANDBOX,
|
|
25393
|
+
sandbox_mode: "workspace-write",
|
|
25394
|
+
auto_review_enabled: false,
|
|
25395
|
+
node_repl_auto_review_required: catalog.nodeReplAutoReviewRequired,
|
|
25396
|
+
node_repl_disabled: catalog.nodeReplDisabled,
|
|
25397
|
+
turn_started_at_unix_ms: args.startedAt,
|
|
25398
|
+
analytics_enabled: false,
|
|
25399
|
+
model: args.model,
|
|
25400
|
+
reasoning_effort: args.effort
|
|
25401
|
+
});
|
|
25402
|
+
const clientMetadata = {
|
|
25403
|
+
turn_id: turn,
|
|
25404
|
+
thread_id: threadId,
|
|
25405
|
+
"x-codex-turn-metadata": turnMetadata,
|
|
25406
|
+
mcp_attribution: '{"status":"none"}',
|
|
25407
|
+
guardian_credits_requested: "true",
|
|
25408
|
+
"x-codex-installation-id": installationId,
|
|
25409
|
+
"x-codex-window-id": windowId,
|
|
25410
|
+
root_turn_id: turn,
|
|
25411
|
+
session_id: threadId
|
|
25412
|
+
};
|
|
25413
|
+
return { windowId, turnMetadata, clientMetadata };
|
|
25414
|
+
}
|
|
25415
|
+
|
|
24905
25416
|
// packages/providers/src/openai/wire.ts
|
|
24906
|
-
import { createHash as createHash6 } from "crypto";
|
|
24907
25417
|
var MAX_CACHE_KEY_LENGTH = 1024;
|
|
24908
25418
|
var HEADER_SAFE_KEY = /^[\x21-\x7e]+$/;
|
|
24909
25419
|
function suppliedKey2(req) {
|
|
@@ -24915,9 +25425,16 @@ function suppliedKey2(req) {
|
|
|
24915
25425
|
}
|
|
24916
25426
|
return;
|
|
24917
25427
|
}
|
|
24918
|
-
function cacheKey2(req, instructions, firstInput) {
|
|
24919
|
-
const
|
|
24920
|
-
|
|
25428
|
+
function cacheKey2(req, instructions, firstInput, salt) {
|
|
25429
|
+
const supplied = suppliedKey2(req);
|
|
25430
|
+
if (supplied !== undefined) {
|
|
25431
|
+
if (isUuid(supplied))
|
|
25432
|
+
return { key: supplied, seed: supplied };
|
|
25433
|
+
const seed = JSON.stringify({ key: supplied });
|
|
25434
|
+
return { key: threadIdFrom(keyed(salt, seed)), seed };
|
|
25435
|
+
}
|
|
25436
|
+
const seed = req.conversationId !== undefined && req.conversationId.length > 0 ? JSON.stringify({ conversation: req.conversationId }) : JSON.stringify({ instructions, firstInput: firstInput ?? null });
|
|
25437
|
+
return { key: threadIdFrom(keyed(salt, seed)), seed };
|
|
24921
25438
|
}
|
|
24922
25439
|
function repairOrphanedCalls2(input) {
|
|
24923
25440
|
const answered = new Set;
|
|
@@ -24948,6 +25465,44 @@ function encodeToolChoice7(c) {
|
|
|
24948
25465
|
return { type: "function", name: c.name };
|
|
24949
25466
|
}
|
|
24950
25467
|
}
|
|
25468
|
+
function codexReasoning(req, model, note) {
|
|
25469
|
+
const catalog = codexModel(model);
|
|
25470
|
+
const context = catalog.lite ? { context: "all_turns" } : {};
|
|
25471
|
+
const r = req.reasoning;
|
|
25472
|
+
if (r === undefined)
|
|
25473
|
+
return { effort: catalog.defaultEffort, ...context };
|
|
25474
|
+
if (r.mode === "off") {
|
|
25475
|
+
note("openai:reasoning-off-as-lowest");
|
|
25476
|
+
return { effort: catalog.lowestEffort, ...context };
|
|
25477
|
+
}
|
|
25478
|
+
if (r.mode === "betweenTools")
|
|
25479
|
+
return { effort: catalog.lowestEffort, ...context };
|
|
25480
|
+
if (r.mode === "budget") {
|
|
25481
|
+
note("openai:reasoning-budget-dropped");
|
|
25482
|
+
return { effort: catalog.defaultEffort, ...context };
|
|
25483
|
+
}
|
|
25484
|
+
return {
|
|
25485
|
+
effort: r.effort ?? catalog.defaultEffort,
|
|
25486
|
+
...r.display === "omitted" || r.display === "updates" ? {} : { summary: "auto" },
|
|
25487
|
+
...context
|
|
25488
|
+
};
|
|
25489
|
+
}
|
|
25490
|
+
var isRecord8 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
25491
|
+
function mergeCodexVendor(body, vendor, note) {
|
|
25492
|
+
const target = body;
|
|
25493
|
+
for (const [name, value] of Object.entries(vendor)) {
|
|
25494
|
+
if (name === "client_metadata") {
|
|
25495
|
+
note("openai:client-metadata-replaced");
|
|
25496
|
+
continue;
|
|
25497
|
+
}
|
|
25498
|
+
const ours = target[name];
|
|
25499
|
+
if ((name === "text" || name === "reasoning") && isRecord8(ours) && isRecord8(value)) {
|
|
25500
|
+
target[name] = { ...ours, ...value };
|
|
25501
|
+
continue;
|
|
25502
|
+
}
|
|
25503
|
+
target[name] = value;
|
|
25504
|
+
}
|
|
25505
|
+
}
|
|
24951
25506
|
function toResponsesWire(req, model, opts = { oauth: false }) {
|
|
24952
25507
|
const degradations = [];
|
|
24953
25508
|
const input = [];
|
|
@@ -24955,6 +25510,12 @@ function toResponsesWire(req, model, opts = { oauth: false }) {
|
|
|
24955
25510
|
if (!degradations.includes(d))
|
|
24956
25511
|
degradations.push(d);
|
|
24957
25512
|
};
|
|
25513
|
+
const turnOf = new WeakMap;
|
|
25514
|
+
let turn = -1;
|
|
25515
|
+
const push = (item) => {
|
|
25516
|
+
input.push(item);
|
|
25517
|
+
turnOf.set(item, Math.max(0, turn));
|
|
25518
|
+
};
|
|
24958
25519
|
if (req.betas?.includes(CONTEXT_1M_BETA))
|
|
24959
25520
|
note("openai:context-1m-dropped");
|
|
24960
25521
|
for (const message of req.messages) {
|
|
@@ -24966,10 +25527,15 @@ function toResponsesWire(req, model, opts = { oauth: false }) {
|
|
|
24966
25527
|
const flush = () => {
|
|
24967
25528
|
if (parts.length === 0)
|
|
24968
25529
|
return;
|
|
24969
|
-
|
|
25530
|
+
push({ type: "message", role, content: [...parts] });
|
|
24970
25531
|
parts.length = 0;
|
|
24971
25532
|
};
|
|
25533
|
+
let opened = false;
|
|
24972
25534
|
for (const block of message.content) {
|
|
25535
|
+
if (message.role === "user" && !opened && block.type !== "toolResult") {
|
|
25536
|
+
opened = true;
|
|
25537
|
+
turn++;
|
|
25538
|
+
}
|
|
24973
25539
|
switch (block.type) {
|
|
24974
25540
|
case "text":
|
|
24975
25541
|
parts.push({
|
|
@@ -24990,7 +25556,7 @@ function toResponsesWire(req, model, opts = { oauth: false }) {
|
|
|
24990
25556
|
break;
|
|
24991
25557
|
case "toolUse":
|
|
24992
25558
|
flush();
|
|
24993
|
-
|
|
25559
|
+
push({
|
|
24994
25560
|
type: "function_call",
|
|
24995
25561
|
call_id: block.id,
|
|
24996
25562
|
name: block.name,
|
|
@@ -25002,7 +25568,7 @@ function toResponsesWire(req, model, opts = { oauth: false }) {
|
|
|
25002
25568
|
note("openai:anthropic-native-block-dropped");
|
|
25003
25569
|
flush();
|
|
25004
25570
|
const files = responsesFiles(block.files ?? []);
|
|
25005
|
-
|
|
25571
|
+
push({
|
|
25006
25572
|
type: "function_call_output",
|
|
25007
25573
|
call_id: block.toolUseId,
|
|
25008
25574
|
output: withFileText(block.content, files.text)
|
|
@@ -25030,7 +25596,7 @@ function toResponsesWire(req, model, opts = { oauth: false }) {
|
|
|
25030
25596
|
if (block.provider === "openai") {
|
|
25031
25597
|
flush();
|
|
25032
25598
|
const { id: _id, ...rest } = block.data;
|
|
25033
|
-
|
|
25599
|
+
push({ type: block.blockType, ...rest });
|
|
25034
25600
|
break;
|
|
25035
25601
|
}
|
|
25036
25602
|
note("openai:anthropic-native-block-dropped");
|
|
@@ -25042,8 +25608,10 @@ function toResponsesWire(req, model, opts = { oauth: false }) {
|
|
|
25042
25608
|
repairOrphanedCalls2(input);
|
|
25043
25609
|
const body = { model, input, stream: req.stream, store: false };
|
|
25044
25610
|
const instructions = systemText(req.system, "openai", note);
|
|
25045
|
-
|
|
25611
|
+
const lite = opts.oauth && codexModel(model).lite;
|
|
25612
|
+
if (instructions !== undefined && instructions.length > 0 && !opts.oauth) {
|
|
25046
25613
|
body.instructions = instructions;
|
|
25614
|
+
}
|
|
25047
25615
|
if (req.maxTokens !== undefined) {
|
|
25048
25616
|
if (opts.oauth)
|
|
25049
25617
|
note("openai:max-tokens-dropped");
|
|
@@ -25056,22 +25624,45 @@ function toResponsesWire(req, model, opts = { oauth: false }) {
|
|
|
25056
25624
|
else
|
|
25057
25625
|
body.temperature = req.temperature;
|
|
25058
25626
|
}
|
|
25627
|
+
let liteTools;
|
|
25059
25628
|
if (req.tools !== undefined) {
|
|
25060
25629
|
const custom = req.tools.filter((t) => t.kind === "portable");
|
|
25061
25630
|
if (custom.length !== req.tools.length)
|
|
25062
25631
|
note("openai:anthropic-tool-dropped");
|
|
25063
|
-
|
|
25064
|
-
|
|
25065
|
-
|
|
25066
|
-
|
|
25067
|
-
|
|
25068
|
-
|
|
25632
|
+
if (opts.oauth) {
|
|
25633
|
+
const tools = custom.map((t) => ({
|
|
25634
|
+
type: "function",
|
|
25635
|
+
name: t.name,
|
|
25636
|
+
description: t.description,
|
|
25637
|
+
strict: false,
|
|
25638
|
+
parameters: t.inputSchema
|
|
25639
|
+
}));
|
|
25640
|
+
if (lite)
|
|
25641
|
+
liteTools = tools;
|
|
25642
|
+
else
|
|
25643
|
+
body.tools = tools;
|
|
25644
|
+
} else {
|
|
25645
|
+
body.tools = custom.map((t) => ({
|
|
25646
|
+
type: "function",
|
|
25647
|
+
name: t.name,
|
|
25648
|
+
description: t.description,
|
|
25649
|
+
parameters: t.inputSchema
|
|
25650
|
+
}));
|
|
25651
|
+
}
|
|
25069
25652
|
}
|
|
25070
25653
|
if (req.toolChoice !== undefined)
|
|
25071
25654
|
body.tool_choice = encodeToolChoice7(req.toolChoice);
|
|
25072
|
-
if (
|
|
25073
|
-
|
|
25074
|
-
if (
|
|
25655
|
+
else if (opts.oauth)
|
|
25656
|
+
body.tool_choice = "auto";
|
|
25657
|
+
if (opts.oauth) {
|
|
25658
|
+
body.parallel_tool_calls = !lite && codexModel(model).supportsParallelToolCalls;
|
|
25659
|
+
}
|
|
25660
|
+
if (req.reasoning?.mode === "betweenTools") {
|
|
25661
|
+
note(opts.oauth ? "openai:reasoning-between-tools-as-lowest" : "openai:reasoning-between-tools-as-off");
|
|
25662
|
+
}
|
|
25663
|
+
if (opts.oauth) {
|
|
25664
|
+
body.reasoning = codexReasoning(req, model, note);
|
|
25665
|
+
} else if (req.reasoning !== undefined && req.reasoning.mode !== "off" && req.reasoning.mode !== "betweenTools") {
|
|
25075
25666
|
if (req.reasoning.mode === "budget") {
|
|
25076
25667
|
degradations.push("openai:reasoning-budget-dropped");
|
|
25077
25668
|
} else {
|
|
@@ -25082,36 +25673,98 @@ function toResponsesWire(req, model, opts = { oauth: false }) {
|
|
|
25082
25673
|
};
|
|
25083
25674
|
}
|
|
25084
25675
|
}
|
|
25085
|
-
const
|
|
25086
|
-
|
|
25676
|
+
const salt = opts.oauth ? opts.idSalt : undefined;
|
|
25677
|
+
const { key, seed } = cacheKey2(req, instructions ?? "", input[0], salt);
|
|
25678
|
+
if (opts.oauth) {
|
|
25679
|
+
body.include = ["reasoning.encrypted_content"];
|
|
25680
|
+
body.text = { verbosity: codexModel(model).verbosity };
|
|
25681
|
+
mergeCodexVendor(body, req.vendor?.openai ?? {}, note);
|
|
25682
|
+
} else {
|
|
25683
|
+
Object.assign(body, req.vendor?.openai ?? {});
|
|
25684
|
+
}
|
|
25087
25685
|
const moved = moveToResponsesText(req, body, "openai");
|
|
25088
25686
|
if (moved !== undefined)
|
|
25089
25687
|
note(moved);
|
|
25090
25688
|
body.prompt_cache_key = key;
|
|
25091
|
-
|
|
25689
|
+
if (!opts.oauth)
|
|
25690
|
+
return { body, degradations, cacheKey: key };
|
|
25691
|
+
const built = toLiteInput({
|
|
25692
|
+
threadId: key,
|
|
25693
|
+
input,
|
|
25694
|
+
turnOf,
|
|
25695
|
+
tools: liteTools,
|
|
25696
|
+
instructions,
|
|
25697
|
+
requestId: opts.requestId,
|
|
25698
|
+
startedAt: opts.startedAt,
|
|
25699
|
+
idSalt: salt
|
|
25700
|
+
});
|
|
25701
|
+
body.input = built.input;
|
|
25702
|
+
const sent = body.reasoning?.effort;
|
|
25703
|
+
const codex = codexTurn({
|
|
25704
|
+
threadId: key,
|
|
25705
|
+
turnId: built.turnId,
|
|
25706
|
+
installationId: opts.installationId,
|
|
25707
|
+
installationSeed: opts.accountId ?? seed,
|
|
25708
|
+
idSalt: salt,
|
|
25709
|
+
model,
|
|
25710
|
+
effort: isCodexEffort(sent) ? sent : codexModel(model).defaultEffort,
|
|
25711
|
+
startedAt: built.startedAt
|
|
25712
|
+
});
|
|
25713
|
+
body.client_metadata = codex.clientMetadata;
|
|
25714
|
+
return { body, degradations, cacheKey: key, codex };
|
|
25092
25715
|
}
|
|
25093
25716
|
|
|
25094
25717
|
// packages/providers/src/openai/codec.ts
|
|
25095
25718
|
var OAUTH_URL2 = "https://chatgpt.com/backend-api/codex/responses";
|
|
25096
25719
|
var API_URL3 = "https://api.openai.com/v1/responses";
|
|
25720
|
+
var HEADER_SAFE = /^[\x21-\x7e]+$/;
|
|
25721
|
+
function routingHint(model, tier) {
|
|
25722
|
+
if (!HEADER_SAFE.test(model))
|
|
25723
|
+
return;
|
|
25724
|
+
if (typeof tier === "string" && HEADER_SAFE.test(tier) && !tier.includes(";")) {
|
|
25725
|
+
return `model=${model};tier=${tier}`;
|
|
25726
|
+
}
|
|
25727
|
+
return `model=${model}`;
|
|
25728
|
+
}
|
|
25097
25729
|
var openaiCodec = {
|
|
25098
25730
|
buildRequest(input) {
|
|
25099
25731
|
const oauth = input.credentials.accessToken !== null;
|
|
25100
|
-
const
|
|
25101
|
-
const
|
|
25732
|
+
const accountId = input.credentials.providerData.accountId;
|
|
25733
|
+
const secrets = oauth ? codexSecretsFrom(input.credentials.providerData) : {};
|
|
25734
|
+
const { body, degradations, cacheKey, codex } = toResponsesWire(input.request, input.model, {
|
|
25735
|
+
oauth,
|
|
25736
|
+
requestId: input.requestId,
|
|
25737
|
+
startedAt: input.requestStartedAt,
|
|
25738
|
+
accountId: typeof accountId === "string" ? accountId : undefined,
|
|
25739
|
+
installationId: secrets.installationId,
|
|
25740
|
+
idSalt: secrets.idSalt
|
|
25741
|
+
});
|
|
25742
|
+
const protocol = [];
|
|
25743
|
+
if (codex !== undefined) {
|
|
25744
|
+
protocol.push(["x-codex-window-id", codex.windowId], ["x-codex-turn-metadata", codex.turnMetadata]);
|
|
25745
|
+
if (codexModel(input.model).lite) {
|
|
25746
|
+
protocol.push(["x-openai-internal-codex-responses-lite", "true"]);
|
|
25747
|
+
}
|
|
25748
|
+
const hint = routingHint(input.model, body.service_tier);
|
|
25749
|
+
if (hint !== undefined)
|
|
25750
|
+
protocol.push(["x-codex-routing-hint", hint]);
|
|
25751
|
+
}
|
|
25752
|
+
protocol.push(["x-client-request-id", cacheKey], ["session-id", cacheKey], ["thread-id", cacheKey]);
|
|
25753
|
+
if (oauth)
|
|
25754
|
+
protocol.push(["content-encoding", "zstd"]);
|
|
25755
|
+
protocol.push(["content-type", "application/json"]);
|
|
25102
25756
|
if (oauth) {
|
|
25103
25757
|
protocol.push(["authorization", `Bearer ${input.credentials.accessToken}`]);
|
|
25104
|
-
const accountId = input.credentials.providerData.accountId;
|
|
25105
25758
|
if (typeof accountId === "string")
|
|
25106
25759
|
protocol.push(["chatgpt-account-id", accountId]);
|
|
25760
|
+
protocol.push(["host", "chatgpt.com"]);
|
|
25107
25761
|
} else if (input.credentials.apiKey !== null) {
|
|
25108
25762
|
protocol.push(["authorization", `Bearer ${input.credentials.apiKey}`]);
|
|
25109
25763
|
} else {
|
|
25110
25764
|
throw input.fail("AUTH", "openai credential has no token");
|
|
25111
25765
|
}
|
|
25112
|
-
protocol.push(["x-client-request-id", cacheKey], ["session-id", cacheKey], ["thread-id", cacheKey]);
|
|
25113
25766
|
const headers = orderHeaders(mergeHeaders(openaiProfile.headers, protocol), openaiProfile.order);
|
|
25114
|
-
const include =
|
|
25767
|
+
const include = input.request.vendor?.openai?.include;
|
|
25115
25768
|
const nativeReasoning = Array.isArray(include) && include.includes("reasoning.encrypted_content");
|
|
25116
25769
|
return {
|
|
25117
25770
|
decodeState: { nativeReasoning },
|
|
@@ -25119,7 +25772,8 @@ var openaiCodec = {
|
|
|
25119
25772
|
url: oauth ? OAUTH_URL2 : API_URL3,
|
|
25120
25773
|
method: "POST",
|
|
25121
25774
|
headers,
|
|
25122
|
-
body: JSON.stringify(orderFields({ ...body, stream: true }, openaiBodyOrder))
|
|
25775
|
+
body: JSON.stringify(orderFields({ ...body, stream: true }, openaiBodyOrder)),
|
|
25776
|
+
...oauth ? { bodyEncoding: "zstd" } : {}
|
|
25123
25777
|
},
|
|
25124
25778
|
degradations
|
|
25125
25779
|
};
|
|
@@ -25218,11 +25872,17 @@ function createConnectFlows(deps) {
|
|
|
25218
25872
|
const provider = deps.providers[flow.provider];
|
|
25219
25873
|
if (provider === undefined)
|
|
25220
25874
|
throw new GatewayError("BAD_REQUEST", "provider does not support OAuth");
|
|
25221
|
-
const
|
|
25875
|
+
const existing = flow.credentialId === undefined ? null : await deps.store.credentials.get(flow.credentialId);
|
|
25876
|
+
if (flow.credentialId !== undefined && existing === null) {
|
|
25877
|
+
throw new GatewayError("BAD_REQUEST", "credential not found");
|
|
25878
|
+
}
|
|
25879
|
+
const prior = existing !== null && existing.provider === flow.provider ? existing.providerData : undefined;
|
|
25880
|
+
const result = await provider.exchange({
|
|
25881
|
+
code,
|
|
25882
|
+
pending: flow.pending,
|
|
25883
|
+
...prior === undefined ? {} : { providerData: prior }
|
|
25884
|
+
}, { http: deps.http, now: deps.now });
|
|
25222
25885
|
if (flow.credentialId !== undefined) {
|
|
25223
|
-
const existing = await deps.store.credentials.get(flow.credentialId);
|
|
25224
|
-
if (existing === null)
|
|
25225
|
-
throw new GatewayError("BAD_REQUEST", "credential not found");
|
|
25226
25886
|
await deps.store.credentials.updateSecrets(flow.credentialId, result.secrets, result.expiresAt);
|
|
25227
25887
|
await deps.store.credentials.update(flow.credentialId, {
|
|
25228
25888
|
enabled: true,
|
|
@@ -26553,9 +27213,20 @@ function configuredWindows(limits) {
|
|
|
26553
27213
|
async function readLimitUsage(store, id, limits, now) {
|
|
26554
27214
|
if (limits === null)
|
|
26555
27215
|
return [];
|
|
27216
|
+
const readings = await readScope(store, id, limits, null, now);
|
|
27217
|
+
for (const model of Object.keys(limits.models ?? {})) {
|
|
27218
|
+
const override = modelOverride(limits, model);
|
|
27219
|
+
if (override === undefined)
|
|
27220
|
+
continue;
|
|
27221
|
+
readings.push(...await readScope(store, id, override, model, now));
|
|
27222
|
+
}
|
|
27223
|
+
return readings;
|
|
27224
|
+
}
|
|
27225
|
+
async function readScope(store, id, limits, model, now) {
|
|
26556
27226
|
const sums = new Map;
|
|
26557
27227
|
for (const window2 of configuredWindows(limits)) {
|
|
26558
|
-
|
|
27228
|
+
const since = now - WINDOW_MS[window2];
|
|
27229
|
+
sums.set(window2, await store.usage.sumSince(id, since, model ?? undefined));
|
|
26559
27230
|
}
|
|
26560
27231
|
const readings = [];
|
|
26561
27232
|
for (const dimension of ["requests", "tokens", "spend"]) {
|
|
@@ -26568,7 +27239,7 @@ async function readLimitUsage(store, id, limits, now) {
|
|
|
26568
27239
|
continue;
|
|
26569
27240
|
const sum = sums.get(window2);
|
|
26570
27241
|
const used = sum === undefined ? null : dimension === "requests" ? sum.requests : dimension === "tokens" ? sum.tokens : sum.costUsd;
|
|
26571
|
-
readings.push({ dimension, window: window2, limit, used });
|
|
27242
|
+
readings.push({ dimension, window: window2, limit, used, model });
|
|
26572
27243
|
}
|
|
26573
27244
|
}
|
|
26574
27245
|
if (limits.concurrency !== undefined && limits.concurrency !== null) {
|
|
@@ -26576,7 +27247,8 @@ async function readLimitUsage(store, id, limits, now) {
|
|
|
26576
27247
|
dimension: "concurrency",
|
|
26577
27248
|
window: null,
|
|
26578
27249
|
limit: limits.concurrency,
|
|
26579
|
-
used: null
|
|
27250
|
+
used: null,
|
|
27251
|
+
model
|
|
26580
27252
|
});
|
|
26581
27253
|
}
|
|
26582
27254
|
return readings;
|
|
@@ -27550,7 +28222,7 @@ function createRefresher(deps) {
|
|
|
27550
28222
|
};
|
|
27551
28223
|
}
|
|
27552
28224
|
// packages/control/src/pluginProviders.ts
|
|
27553
|
-
function
|
|
28225
|
+
function isRecord9(value) {
|
|
27554
28226
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
27555
28227
|
}
|
|
27556
28228
|
function isNumber(value) {
|
|
@@ -27561,7 +28233,7 @@ function checkAt(ok, id, path, expected) {
|
|
|
27561
28233
|
throw new Error(`provider ${id} descriptor: ${path} must be ${expected}`);
|
|
27562
28234
|
}
|
|
27563
28235
|
function checkLimits(id, path, value) {
|
|
27564
|
-
checkAt(
|
|
28236
|
+
checkAt(isRecord9(value), id, path, "an object");
|
|
27565
28237
|
const limits = value;
|
|
27566
28238
|
checkAt(isNumber(limits.contextWindow), id, `${path}.contextWindow`, "a finite number");
|
|
27567
28239
|
checkAt(isNumber(limits.maxOutputTokens), id, `${path}.maxOutputTokens`, "a finite number");
|
|
@@ -27579,19 +28251,19 @@ function checkDescriptor(id, record) {
|
|
|
27579
28251
|
}
|
|
27580
28252
|
}
|
|
27581
28253
|
const capabilities = record.capabilities;
|
|
27582
|
-
checkAt(
|
|
28254
|
+
checkAt(isRecord9(capabilities), id, "capabilities", "an object");
|
|
27583
28255
|
for (const flag of ["tools", "images", "reasoning"]) {
|
|
27584
28256
|
const value = capabilities[flag];
|
|
27585
28257
|
checkAt(typeof value === "boolean", id, `capabilities.${flag}`, "a boolean");
|
|
27586
28258
|
}
|
|
27587
28259
|
const write = record.writeOverInput;
|
|
27588
|
-
checkAt(
|
|
28260
|
+
checkAt(isRecord9(write), id, "writeOverInput", "an object");
|
|
27589
28261
|
for (const ttl of ["fiveMinute", "oneHour"]) {
|
|
27590
28262
|
const value = write[ttl];
|
|
27591
28263
|
checkAt(isNumber(value), id, `writeOverInput.${ttl}`, "a finite number");
|
|
27592
28264
|
}
|
|
27593
28265
|
const catalog = record.catalog;
|
|
27594
|
-
checkAt(
|
|
28266
|
+
checkAt(isRecord9(catalog), id, "catalog", "an object");
|
|
27595
28267
|
const entry = catalog;
|
|
27596
28268
|
checkAt(typeof entry.defaultModel === "string", id, "catalog.defaultModel", "a string");
|
|
27597
28269
|
checkAt(Array.isArray(entry.authTypes), id, "catalog.authTypes", "an array");
|
|
@@ -27602,11 +28274,11 @@ function checkDescriptor(id, record) {
|
|
|
27602
28274
|
checkAt(Array.isArray(entry.models), id, "catalog.models", "an array");
|
|
27603
28275
|
for (const [index, model] of entry.models.entries()) {
|
|
27604
28276
|
const at = `catalog.models[${index}]`;
|
|
27605
|
-
checkAt(
|
|
28277
|
+
checkAt(isRecord9(model), id, at, "an object");
|
|
27606
28278
|
const choice = model;
|
|
27607
28279
|
checkAt(typeof choice.id === "string", id, `${at}.id`, "a string");
|
|
27608
28280
|
checkAt(typeof choice.label === "string", id, `${at}.label`, "a string");
|
|
27609
|
-
checkAt(
|
|
28281
|
+
checkAt(isRecord9(choice.pricing), id, `${at}.pricing`, "an object");
|
|
27610
28282
|
const pricing = choice.pricing;
|
|
27611
28283
|
for (const field of ["input", "output", "cacheRead", "cacheWrite5m", "cacheWrite1h"]) {
|
|
27612
28284
|
checkAt(isNumber(pricing[field]), id, `${at}.pricing.${field}`, "a finite number");
|
|
@@ -27622,13 +28294,13 @@ function checkDescriptor(id, record) {
|
|
|
27622
28294
|
checkAt(typeof prefix === "string", id, `modelPrefixes[${index}]`, "a string");
|
|
27623
28295
|
}
|
|
27624
28296
|
const presentation = record.presentation;
|
|
27625
|
-
checkAt(
|
|
28297
|
+
checkAt(isRecord9(presentation), id, "presentation", "an object");
|
|
27626
28298
|
const shown = presentation;
|
|
27627
28299
|
for (const field of ["label", "tone"]) {
|
|
27628
28300
|
checkAt(typeof shown[field] === "string", id, `presentation.${field}`, "a string");
|
|
27629
28301
|
}
|
|
27630
28302
|
checkAt(isNumber(shown.order), id, "presentation.order", "a finite number");
|
|
27631
|
-
checkAt(
|
|
28303
|
+
checkAt(isRecord9(shown.colour), id, "presentation.colour", "an object");
|
|
27632
28304
|
for (const scheme of ["light", "dark"]) {
|
|
27633
28305
|
const value = shown.colour[scheme];
|
|
27634
28306
|
checkAt(typeof value === "string", id, `presentation.colour.${scheme}`, "a string");
|
|
@@ -27640,7 +28312,7 @@ function checkDescriptor(id, record) {
|
|
|
27640
28312
|
function validateOAuthFlow(id, value) {
|
|
27641
28313
|
if (value === undefined)
|
|
27642
28314
|
return;
|
|
27643
|
-
if (!
|
|
28315
|
+
if (!isRecord9(value))
|
|
27644
28316
|
throw new Error(`provider ${id} oauth must be an object`);
|
|
27645
28317
|
const kind = value.kind;
|
|
27646
28318
|
if (kind !== "pkce" && kind !== "device") {
|
|
@@ -28297,7 +28969,7 @@ function readFrom(path, offset) {
|
|
|
28297
28969
|
}
|
|
28298
28970
|
}
|
|
28299
28971
|
// packages/plugin-api/src/events.ts
|
|
28300
|
-
var
|
|
28972
|
+
var WINDOW_MS3 = {
|
|
28301
28973
|
"1m": 60000,
|
|
28302
28974
|
"5h": 5 * 60 * 60 * 1000,
|
|
28303
28975
|
"1w": 7 * 24 * 60 * 60 * 1000
|
|
@@ -41648,8 +42320,26 @@ var SEED_LOCK_MS = 5000;
|
|
|
41648
42320
|
var GAUGE_TTL_MS = 300000;
|
|
41649
42321
|
var RENEW_INTERVAL_MS = 1e5;
|
|
41650
42322
|
var MAX_RENEWED_MS = 86400000;
|
|
41651
|
-
function bucketKey(
|
|
41652
|
-
return `lim:${
|
|
42323
|
+
function bucketKey(subject, window2) {
|
|
42324
|
+
return `lim:${subject}:${window2}`;
|
|
42325
|
+
}
|
|
42326
|
+
function modelSubjectId(keyId, model) {
|
|
42327
|
+
return `${keyId}/m/${encodeURIComponent(model)}`;
|
|
42328
|
+
}
|
|
42329
|
+
function keyWide(limits) {
|
|
42330
|
+
const { models: _models, ...base } = limits;
|
|
42331
|
+
return base;
|
|
42332
|
+
}
|
|
42333
|
+
function subjectsOf(keyId, limits, model) {
|
|
42334
|
+
const subjects = [];
|
|
42335
|
+
const base = keyWide(limits);
|
|
42336
|
+
if (anyLimit(base))
|
|
42337
|
+
subjects.push({ id: keyId, keyId, model: undefined, limits: base });
|
|
42338
|
+
const override = modelOverride(limits, model);
|
|
42339
|
+
if (override !== undefined) {
|
|
42340
|
+
subjects.push({ id: modelSubjectId(keyId, model), keyId, model, limits: override });
|
|
42341
|
+
}
|
|
42342
|
+
return subjects;
|
|
41653
42343
|
}
|
|
41654
42344
|
|
|
41655
42345
|
class RateLimitExceeded extends GatewayError {
|
|
@@ -41704,27 +42394,31 @@ class ApiKeyRateLimiter {
|
|
|
41704
42394
|
this.coord = deps.coord ?? memoryCoord();
|
|
41705
42395
|
this.onRejected = deps.onRejected;
|
|
41706
42396
|
}
|
|
41707
|
-
async admit(keyId, limits, requestId) {
|
|
41708
|
-
|
|
42397
|
+
async admit(keyId, limits, model, requestId) {
|
|
42398
|
+
const subjects = subjectsOf(keyId, limits, model);
|
|
42399
|
+
if (subjects.length === 0)
|
|
41709
42400
|
return { release: () => {}, renew: async () => {}, headroom: {} };
|
|
41710
42401
|
const now = this.now();
|
|
41711
|
-
|
|
41712
|
-
const holder = configured(limits.concurrency) ? crypto.randomUUID() : undefined;
|
|
41713
|
-
const claim = await this.claim(keyId, now, true, holder);
|
|
42402
|
+
const claims = [];
|
|
41714
42403
|
let admitted = false;
|
|
41715
42404
|
try {
|
|
41716
|
-
|
|
41717
|
-
|
|
41718
|
-
|
|
42405
|
+
for (const subject of subjects) {
|
|
42406
|
+
this.limits.set(subject.id, subject.limits);
|
|
42407
|
+
const holder = configured(subject.limits.concurrency) ? crypto.randomUUID() : undefined;
|
|
42408
|
+
claims.push(await this.claim(subject.id, now, true, holder));
|
|
42409
|
+
}
|
|
42410
|
+
const decision = await this.judge(subjects, claims, (scope) => scope, now, requestId);
|
|
41719
42411
|
admitted = true;
|
|
42412
|
+
const held = claims.flatMap((claim) => claim.holder === undefined ? [] : [{ subject: claim.subject, holder: claim.holder }]);
|
|
41720
42413
|
let released = false;
|
|
41721
42414
|
const renewUntil = now + MAX_RENEWED_MS;
|
|
41722
42415
|
const renew = async () => {
|
|
41723
|
-
if (released
|
|
42416
|
+
if (released)
|
|
41724
42417
|
return;
|
|
41725
|
-
|
|
42418
|
+
for (const slot of held)
|
|
42419
|
+
await this.coord.gauge.renew(slot.subject, slot.holder, GAUGE_TTL_MS);
|
|
41726
42420
|
};
|
|
41727
|
-
const renewal =
|
|
42421
|
+
const renewal = held.length === 0 ? null : setInterval(() => {
|
|
41728
42422
|
if (this.now() >= renewUntil) {
|
|
41729
42423
|
clearInterval(renewal ?? undefined);
|
|
41730
42424
|
return;
|
|
@@ -41738,91 +42432,153 @@ class ApiKeyRateLimiter {
|
|
|
41738
42432
|
released = true;
|
|
41739
42433
|
if (renewal !== null)
|
|
41740
42434
|
clearInterval(renewal);
|
|
41741
|
-
|
|
42435
|
+
for (const claim of claims)
|
|
42436
|
+
this.coord.gauge.release(claim.subject, claim.holder);
|
|
41742
42437
|
};
|
|
41743
42438
|
return { release, renew, headroom: decision.headroom };
|
|
41744
42439
|
} finally {
|
|
41745
42440
|
if (!admitted)
|
|
41746
|
-
|
|
42441
|
+
for (const claim of claims)
|
|
42442
|
+
await this.rollback(claim);
|
|
41747
42443
|
}
|
|
41748
42444
|
}
|
|
41749
|
-
async consume(keyId, limits, requestId) {
|
|
41750
|
-
const
|
|
41751
|
-
if (
|
|
42445
|
+
async consume(keyId, limits, model, requestId) {
|
|
42446
|
+
const subjects = subjectsOf(keyId, limits, model).filter((subject) => anyRequestLimit(subject.limits));
|
|
42447
|
+
if (subjects.length === 0)
|
|
41752
42448
|
return;
|
|
41753
42449
|
const now = this.now();
|
|
41754
|
-
|
|
41755
|
-
const claim = await this.claim(keyId, now, false);
|
|
42450
|
+
const claims = [];
|
|
41756
42451
|
let consumed = false;
|
|
41757
42452
|
try {
|
|
41758
|
-
const
|
|
41759
|
-
|
|
42453
|
+
for (const subject of subjects) {
|
|
42454
|
+
this.limits.set(subject.id, subject.limits);
|
|
42455
|
+
claims.push(await this.claim(subject.id, now, false));
|
|
42456
|
+
}
|
|
42457
|
+
await this.judge(subjects, claims, requestsOnly, now, requestId);
|
|
41760
42458
|
consumed = true;
|
|
41761
42459
|
} finally {
|
|
41762
42460
|
if (!consumed)
|
|
41763
|
-
|
|
42461
|
+
for (const claim of claims)
|
|
42462
|
+
await this.rollback(claim);
|
|
42463
|
+
}
|
|
42464
|
+
}
|
|
42465
|
+
async judge(subjects, claims, scope, now, requestId) {
|
|
42466
|
+
const decisions = [];
|
|
42467
|
+
for (const [index, subject] of subjects.entries()) {
|
|
42468
|
+
const claim = claims[index];
|
|
42469
|
+
if (claim === undefined)
|
|
42470
|
+
throw new Error("rate limit claim missing for a subject");
|
|
42471
|
+
const limits = scope(subject.limits);
|
|
42472
|
+
const counters = await this.counters(subject, limits, claim, now, requestId);
|
|
42473
|
+
decisions.push(evaluate(limits, counters, now));
|
|
42474
|
+
}
|
|
42475
|
+
const decision = combineAll(decisions);
|
|
42476
|
+
if (decision.violation !== null)
|
|
42477
|
+
await this.refuse(subjects, decisions, now, requestId);
|
|
42478
|
+
return decision;
|
|
42479
|
+
}
|
|
42480
|
+
async claim(subject, now, gauge, holder) {
|
|
42481
|
+
const ring = await this.coord.window.claim(subject, WINDOW_MS["1m"], now);
|
|
42482
|
+
let concurrency;
|
|
42483
|
+
try {
|
|
42484
|
+
concurrency = gauge ? await this.coord.gauge.acquire(subject, GAUGE_TTL_MS, holder) : await this.coord.gauge.read(subject);
|
|
42485
|
+
} catch (error) {
|
|
42486
|
+
await this.coord.window.rollback(subject, ring.stamp);
|
|
42487
|
+
throw error;
|
|
41764
42488
|
}
|
|
42489
|
+
return {
|
|
42490
|
+
subject,
|
|
42491
|
+
stamp: ring.stamp,
|
|
42492
|
+
gauge,
|
|
42493
|
+
holder,
|
|
42494
|
+
before: { requests: ring.before, concurrency }
|
|
42495
|
+
};
|
|
41765
42496
|
}
|
|
41766
|
-
async claim
|
|
41767
|
-
|
|
41768
|
-
const concurrency = gauge ? await this.coord.gauge.acquire(keyId, GAUGE_TTL_MS, holder) : await this.coord.gauge.read(keyId);
|
|
41769
|
-
return { stamp: ring.stamp, gauge, holder, before: { requests: ring.before, concurrency } };
|
|
41770
|
-
}
|
|
41771
|
-
async rollback(keyId, claim) {
|
|
41772
|
-
await this.coord.window.rollback(keyId, claim.stamp);
|
|
42497
|
+
async rollback(claim) {
|
|
42498
|
+
await this.coord.window.rollback(claim.subject, claim.stamp);
|
|
41773
42499
|
if (claim.gauge)
|
|
41774
|
-
await this.coord.gauge.release(
|
|
42500
|
+
await this.coord.gauge.release(claim.subject, claim.holder);
|
|
41775
42501
|
}
|
|
41776
|
-
debit(keyId, usage) {
|
|
41777
|
-
const
|
|
42502
|
+
debit(keyId, requestedModel, usage) {
|
|
42503
|
+
const delta = { requests: 1, tokens: usage.tokens, costUsd: usage.costUsd };
|
|
42504
|
+
this.debitSubject(keyId, delta);
|
|
42505
|
+
this.debitSubject(modelSubjectId(keyId, requestedModel), delta);
|
|
42506
|
+
}
|
|
42507
|
+
debitSubject(subject, delta) {
|
|
42508
|
+
const limits = this.limits.get(subject);
|
|
41778
42509
|
if (limits === undefined || !anyLongLimit(limits))
|
|
41779
42510
|
return;
|
|
41780
42511
|
const now = this.now();
|
|
41781
|
-
const delta = { requests: 1, tokens: usage.tokens, costUsd: usage.costUsd };
|
|
41782
42512
|
for (const window2 of LONG_WINDOWS) {
|
|
41783
|
-
this.coord.buckets.add(bucketKey(
|
|
42513
|
+
this.coord.buckets.add(bucketKey(subject, window2), GRAIN_MS[window2], WINDOW_MS[window2], now, delta);
|
|
41784
42514
|
}
|
|
41785
42515
|
}
|
|
41786
|
-
inFlight(
|
|
41787
|
-
return this.coord.gauge.read(
|
|
42516
|
+
inFlight(subject) {
|
|
42517
|
+
return this.coord.gauge.read(subject);
|
|
41788
42518
|
}
|
|
41789
|
-
async refuse(
|
|
42519
|
+
async refuse(subjects, decisions, now, requestId) {
|
|
42520
|
+
const corrected = [];
|
|
42521
|
+
for (const [index, scope] of decisions.entries()) {
|
|
42522
|
+
const subject = subjects[index];
|
|
42523
|
+
if (subject === undefined)
|
|
42524
|
+
throw new Error("rate limit decision from no subject");
|
|
42525
|
+
corrected.push(await this.correct(subject, scope, now, requestId));
|
|
42526
|
+
}
|
|
42527
|
+
const decision = combineAll(corrected);
|
|
41790
42528
|
const violation = decision.violation;
|
|
41791
42529
|
if (violation === null)
|
|
41792
|
-
|
|
41793
|
-
const window2 = violation.window;
|
|
41794
|
-
const exact = await this.exactReset(keyId, window2, now, requestId);
|
|
41795
|
-
const resolved = exact === null ? violation : { ...violation, resetAt: exact };
|
|
41796
|
-
const headroom = exact === null || window2 === null ? decision.headroom : withReset(decision.headroom, window2, exact);
|
|
42530
|
+
throw new Error("rate limit refusal with no violation");
|
|
41797
42531
|
try {
|
|
41798
42532
|
this.onRejected?.(violation.dimension, violation.window);
|
|
41799
42533
|
} catch {}
|
|
41800
|
-
throw new RateLimitExceeded(retryAfterMs(
|
|
42534
|
+
throw new RateLimitExceeded(retryAfterMs(violation, now), refusedHeadroom(decision, violation));
|
|
42535
|
+
}
|
|
42536
|
+
async correct(subject, scope, now, requestId) {
|
|
42537
|
+
if (scope.violations.length === 0)
|
|
42538
|
+
return scope;
|
|
42539
|
+
const exact = new Map;
|
|
42540
|
+
for (const violation of scope.violations) {
|
|
42541
|
+
const window2 = violation.window;
|
|
42542
|
+
if (window2 === null || exact.has(window2))
|
|
42543
|
+
continue;
|
|
42544
|
+
const reset = await this.exactReset(subject, window2, now, requestId);
|
|
42545
|
+
if (reset !== null)
|
|
42546
|
+
exact.set(window2, reset);
|
|
42547
|
+
}
|
|
42548
|
+
const violations = scope.violations.map((violation) => {
|
|
42549
|
+
const reset = violation.window === null ? undefined : exact.get(violation.window);
|
|
42550
|
+
return reset === undefined ? violation : { ...violation, resetAt: reset };
|
|
42551
|
+
});
|
|
42552
|
+
let headroom = scope.headroom;
|
|
42553
|
+
for (const [window2, reset] of exact)
|
|
42554
|
+
headroom = withReset(headroom, window2, reset);
|
|
42555
|
+
return { ...scope, violation: lastToClear(violations), violations, headroom };
|
|
41801
42556
|
}
|
|
41802
|
-
async exactReset(
|
|
42557
|
+
async exactReset(subject, window2, now, requestId) {
|
|
41803
42558
|
if (window2 === null || window2 === "1m")
|
|
41804
42559
|
return null;
|
|
41805
42560
|
const length = WINDOW_MS[window2];
|
|
41806
42561
|
try {
|
|
41807
|
-
const oldest = await this.store.usage.oldestSince(keyId, now - length);
|
|
42562
|
+
const oldest = await this.store.usage.oldestSince(subject.keyId, now - length, subject.model);
|
|
41808
42563
|
if (oldest === null)
|
|
41809
42564
|
return null;
|
|
41810
42565
|
return oldest + length;
|
|
41811
42566
|
} catch (error) {
|
|
41812
42567
|
this.logger.warn("rate limit reset unavailable", {
|
|
41813
42568
|
...requestId === undefined ? {} : { requestId },
|
|
41814
|
-
apiKeyId: keyId,
|
|
42569
|
+
apiKeyId: subject.keyId,
|
|
42570
|
+
...subject.model === undefined ? {} : { model: subject.model },
|
|
41815
42571
|
reason: describeError(error, "unknown")
|
|
41816
42572
|
});
|
|
41817
42573
|
return null;
|
|
41818
42574
|
}
|
|
41819
42575
|
}
|
|
41820
|
-
async counters(
|
|
42576
|
+
async counters(subject, limits, claim, now, requestId) {
|
|
41821
42577
|
const requests = { "1m": claim.before.requests };
|
|
41822
42578
|
const snapshot = { requests, concurrency: claim.before.concurrency };
|
|
41823
42579
|
if (!anyLongLimit(limits))
|
|
41824
42580
|
return snapshot;
|
|
41825
|
-
const counts = await this.longCounts(
|
|
42581
|
+
const counts = await this.longCounts(subject, now, requestId);
|
|
41826
42582
|
if (counts === null)
|
|
41827
42583
|
return snapshot;
|
|
41828
42584
|
const tokens = {};
|
|
@@ -41838,20 +42594,21 @@ class ApiKeyRateLimiter {
|
|
|
41838
42594
|
snapshot.spend = spend;
|
|
41839
42595
|
return snapshot;
|
|
41840
42596
|
}
|
|
41841
|
-
async longCounts(
|
|
42597
|
+
async longCounts(subject, now, requestId) {
|
|
41842
42598
|
const out = {};
|
|
41843
42599
|
for (const window2 of LONG_WINDOWS) {
|
|
41844
|
-
const key = bucketKey(
|
|
42600
|
+
const key = bucketKey(subject.id, window2);
|
|
41845
42601
|
const grain = GRAIN_MS[window2];
|
|
41846
42602
|
const length = WINDOW_MS[window2];
|
|
41847
42603
|
let sum = await this.coord.buckets.sum(key, grain, length, now);
|
|
41848
42604
|
if (sum === null) {
|
|
41849
42605
|
try {
|
|
41850
|
-
sum = await this.seed(
|
|
42606
|
+
sum = await this.seed(subject, window2, now);
|
|
41851
42607
|
} catch (error) {
|
|
41852
42608
|
this.logger.warn("rate limit counters unavailable", {
|
|
41853
42609
|
...requestId === undefined ? {} : { requestId },
|
|
41854
|
-
apiKeyId: keyId,
|
|
42610
|
+
apiKeyId: subject.keyId,
|
|
42611
|
+
...subject.model === undefined ? {} : { model: subject.model },
|
|
41855
42612
|
reason: describeError(error, "unknown")
|
|
41856
42613
|
});
|
|
41857
42614
|
return null;
|
|
@@ -41861,15 +42618,15 @@ class ApiKeyRateLimiter {
|
|
|
41861
42618
|
}
|
|
41862
42619
|
return out;
|
|
41863
42620
|
}
|
|
41864
|
-
async seed(
|
|
41865
|
-
const key = bucketKey(
|
|
42621
|
+
async seed(subject, window2, now) {
|
|
42622
|
+
const key = bucketKey(subject.id, window2);
|
|
41866
42623
|
const grain = GRAIN_MS[window2];
|
|
41867
42624
|
const length = WINDOW_MS[window2];
|
|
41868
42625
|
const install = async () => {
|
|
41869
42626
|
const already = await this.coord.buckets.sum(key, grain, length, now);
|
|
41870
42627
|
if (already !== null)
|
|
41871
42628
|
return already;
|
|
41872
|
-
const rows = await this.store.usage.sumBuckets(keyId, now - length, grain);
|
|
42629
|
+
const rows = await this.store.usage.sumBuckets(subject.keyId, now - length, grain, subject.model);
|
|
41873
42630
|
await this.coord.buckets.seed(key, grain, length, now, rows);
|
|
41874
42631
|
return await this.coord.buckets.sum(key, grain, length, now) ?? zero();
|
|
41875
42632
|
};
|
|
@@ -41885,6 +42642,9 @@ class ApiKeyRateLimiter {
|
|
|
41885
42642
|
function zero() {
|
|
41886
42643
|
return { requests: 0, tokens: 0, costUsd: 0 };
|
|
41887
42644
|
}
|
|
42645
|
+
function requestsOnly(limits) {
|
|
42646
|
+
return limits.requests === undefined ? {} : { requests: limits.requests };
|
|
42647
|
+
}
|
|
41888
42648
|
function withReset(headroom, window2, resetAt) {
|
|
41889
42649
|
const corrected = { ...headroom };
|
|
41890
42650
|
for (const dimension of ["requests", "tokens", "spend"]) {
|
|
@@ -41894,6 +42654,27 @@ function withReset(headroom, window2, resetAt) {
|
|
|
41894
42654
|
}
|
|
41895
42655
|
return corrected;
|
|
41896
42656
|
}
|
|
42657
|
+
function refusedHeadroom(decision, violation) {
|
|
42658
|
+
const window2 = violation.window;
|
|
42659
|
+
if (window2 === null || violation.dimension === "concurrency")
|
|
42660
|
+
return decision.headroom;
|
|
42661
|
+
return {
|
|
42662
|
+
...decision.headroom,
|
|
42663
|
+
[violation.dimension]: {
|
|
42664
|
+
window: window2,
|
|
42665
|
+
limit: violation.limit,
|
|
42666
|
+
used: violation.used,
|
|
42667
|
+
remaining: Math.max(0, violation.limit - violation.used),
|
|
42668
|
+
resetAt: violation.resetAt
|
|
42669
|
+
}
|
|
42670
|
+
};
|
|
42671
|
+
}
|
|
42672
|
+
function combineAll(decisions) {
|
|
42673
|
+
const [first, ...rest] = decisions;
|
|
42674
|
+
if (first === undefined)
|
|
42675
|
+
return { allowed: true, violation: null, violations: [], headroom: {} };
|
|
42676
|
+
return rest.reduce(combine, first);
|
|
42677
|
+
}
|
|
41897
42678
|
|
|
41898
42679
|
// apps/gateway/src/dispatch/loadRegistry.ts
|
|
41899
42680
|
var PREFIX6 = "load:";
|
|
@@ -42811,7 +43592,7 @@ function zodDetail(issue) {
|
|
|
42811
43592
|
}
|
|
42812
43593
|
return issue.message;
|
|
42813
43594
|
}
|
|
42814
|
-
function
|
|
43595
|
+
function isRecord10(value) {
|
|
42815
43596
|
return typeof value === "object" && value !== null;
|
|
42816
43597
|
}
|
|
42817
43598
|
function extraFields(body, known) {
|
|
@@ -42954,7 +43735,7 @@ async function readJson(request) {
|
|
|
42954
43735
|
}
|
|
42955
43736
|
async function readJsonRecord(request) {
|
|
42956
43737
|
const body = await readJson(request);
|
|
42957
|
-
return
|
|
43738
|
+
return isRecord10(body) ? body : null;
|
|
42958
43739
|
}
|
|
42959
43740
|
function apiErrorResponse(error) {
|
|
42960
43741
|
const gatewayError = error instanceof GatewayError ? error : new GatewayError("INTERNAL", "internal error");
|
|
@@ -46041,7 +46822,7 @@ async function finishLog(store, log, keyId, logger = noopLogger, bodies, debit,
|
|
|
46041
46822
|
report(logger, "failed to persist request log", log.id, error);
|
|
46042
46823
|
}
|
|
46043
46824
|
if (keyId !== null && debit !== undefined) {
|
|
46044
|
-
debit(keyId, {
|
|
46825
|
+
debit(keyId, log.requestedModel, {
|
|
46045
46826
|
tokens: log.inputTokens + log.outputTokens + log.cacheReadTokens + log.cacheWriteTokens,
|
|
46046
46827
|
costUsd: log.costUsd
|
|
46047
46828
|
});
|
|
@@ -46210,6 +46991,7 @@ async function attempt(opts) {
|
|
|
46210
46991
|
signal,
|
|
46211
46992
|
...opts.requestId === undefined ? {} : { requestId: opts.requestId },
|
|
46212
46993
|
...opts.autoCache === undefined ? {} : { autoCache: opts.autoCache },
|
|
46994
|
+
...opts.requestStartedAt === undefined ? {} : { requestStartedAt: opts.requestStartedAt },
|
|
46213
46995
|
...candidate.target.maxOutputTokens === undefined ? {} : { maxOutputTokens: candidate.target.maxOutputTokens }
|
|
46214
46996
|
});
|
|
46215
46997
|
}
|
|
@@ -46529,6 +47311,7 @@ async function dispatch(request, deps, signal, requestId) {
|
|
|
46529
47311
|
...retrySecrets === undefined ? {} : { secrets: retrySecrets },
|
|
46530
47312
|
logger,
|
|
46531
47313
|
requestId,
|
|
47314
|
+
requestStartedAt: startedAt,
|
|
46532
47315
|
autoCache: snapshot.settings.autoCacheEnabled
|
|
46533
47316
|
}), dispatchSignal);
|
|
46534
47317
|
noteDegradations(result.degradations);
|
|
@@ -47374,10 +48157,10 @@ function toIrToolChoice(c) {
|
|
|
47374
48157
|
}
|
|
47375
48158
|
var EFFORTS = REASONING_EFFORTS;
|
|
47376
48159
|
function readEffort(body) {
|
|
47377
|
-
if (!
|
|
48160
|
+
if (!isRecord10(body))
|
|
47378
48161
|
return;
|
|
47379
48162
|
const outputConfig = body.output_config;
|
|
47380
|
-
if (!
|
|
48163
|
+
if (!isRecord10(outputConfig))
|
|
47381
48164
|
return;
|
|
47382
48165
|
const effort = outputConfig.effort;
|
|
47383
48166
|
return EFFORTS.find((level) => level === effort);
|
|
@@ -47759,7 +48542,7 @@ function roleOf(raw) {
|
|
|
47759
48542
|
return "user";
|
|
47760
48543
|
}
|
|
47761
48544
|
function contentPart(part) {
|
|
47762
|
-
if (!
|
|
48545
|
+
if (!isRecord10(part))
|
|
47763
48546
|
throw new GatewayError("BAD_REQUEST", "input: content part must be an object");
|
|
47764
48547
|
const type = part.type;
|
|
47765
48548
|
if (type === "input_text" || type === "output_text") {
|
|
@@ -47783,11 +48566,11 @@ function readToolOutput(output) {
|
|
|
47783
48566
|
const images = [];
|
|
47784
48567
|
const files = [];
|
|
47785
48568
|
for (const part of output) {
|
|
47786
|
-
if (
|
|
48569
|
+
if (isRecord10(part) && (part.type === "input_text" || part.type === "output_text")) {
|
|
47787
48570
|
text.push(String(part.text ?? ""));
|
|
47788
|
-
} else if (
|
|
48571
|
+
} else if (isRecord10(part) && part.type === "input_image" && typeof part.image_url === "string" && part.image_url.startsWith("data:")) {
|
|
47789
48572
|
images.push({ type: "image", ...parseDataUrl(part.image_url) });
|
|
47790
|
-
} else if (
|
|
48573
|
+
} else if (isRecord10(part) && part.type === "input_file" && typeof part.file_data === "string") {
|
|
47791
48574
|
files.push({
|
|
47792
48575
|
type: "file",
|
|
47793
48576
|
...fileData(part.file_data),
|
|
@@ -47838,7 +48621,7 @@ function readInputItems(items) {
|
|
|
47838
48621
|
messages.push({ role, content });
|
|
47839
48622
|
};
|
|
47840
48623
|
for (const raw of items) {
|
|
47841
|
-
if (!
|
|
48624
|
+
if (!isRecord10(raw))
|
|
47842
48625
|
throw new GatewayError("BAD_REQUEST", "input: item must be an object");
|
|
47843
48626
|
const type = raw.type ?? (raw.role === undefined ? undefined : "message");
|
|
47844
48627
|
switch (type) {
|
|
@@ -47894,7 +48677,7 @@ function jsonArguments(raw, callId) {
|
|
|
47894
48677
|
const text = typeof raw === "string" ? raw : "";
|
|
47895
48678
|
try {
|
|
47896
48679
|
const parsed = text === "" ? {} : JSON.parse(text);
|
|
47897
|
-
return
|
|
48680
|
+
return isRecord10(parsed) ? parsed : { value: parsed };
|
|
47898
48681
|
} catch {
|
|
47899
48682
|
throw new GatewayError("BAD_REQUEST", `input: arguments for call ${safeToken(callId)} are not valid JSON`);
|
|
47900
48683
|
}
|
|
@@ -47903,7 +48686,7 @@ var TOOL_NAME = /^[A-Za-z0-9_-]{1,128}$/;
|
|
|
47903
48686
|
function readTools(raw) {
|
|
47904
48687
|
const tools = [];
|
|
47905
48688
|
for (const entry of raw) {
|
|
47906
|
-
if (!
|
|
48689
|
+
if (!isRecord10(entry))
|
|
47907
48690
|
throw new GatewayError("BAD_REQUEST", "tools: each tool must be an object");
|
|
47908
48691
|
const type = typeof entry.type === "string" ? entry.type : "";
|
|
47909
48692
|
const declared = typeof entry.name === "string" ? entry.name : "";
|
|
@@ -47916,7 +48699,7 @@ function readTools(raw) {
|
|
|
47916
48699
|
kind: "portable",
|
|
47917
48700
|
name: declared,
|
|
47918
48701
|
...description === undefined ? {} : { description },
|
|
47919
|
-
inputSchema: type === "custom" ? { type: "object", properties: { input: { type: "string" } }, required: ["input"] } :
|
|
48702
|
+
inputSchema: type === "custom" ? { type: "object", properties: { input: { type: "string" } }, required: ["input"] } : isRecord10(entry.parameters) ? entry.parameters : { type: "object" }
|
|
47920
48703
|
});
|
|
47921
48704
|
continue;
|
|
47922
48705
|
}
|
|
@@ -47931,10 +48714,10 @@ function readTools(raw) {
|
|
|
47931
48714
|
}
|
|
47932
48715
|
function customToolNames(body) {
|
|
47933
48716
|
const names = new Set;
|
|
47934
|
-
if (!
|
|
48717
|
+
if (!isRecord10(body) || !Array.isArray(body.tools))
|
|
47935
48718
|
return names;
|
|
47936
48719
|
for (const entry of body.tools) {
|
|
47937
|
-
if (
|
|
48720
|
+
if (isRecord10(entry) && entry.type === "custom" && typeof entry.name === "string") {
|
|
47938
48721
|
names.add(entry.name);
|
|
47939
48722
|
}
|
|
47940
48723
|
}
|
|
@@ -48143,8 +48926,8 @@ async function handle(deps, rateLimiter, surface, request) {
|
|
|
48143
48926
|
let streaming = false;
|
|
48144
48927
|
let headroom = {};
|
|
48145
48928
|
const limitHeaders = () => rateLimitHeaders(surface, release === null ? headroom : afterThisRequest(headroom), startedAt);
|
|
48146
|
-
const debit = (id, usage) => {
|
|
48147
|
-
rateLimiter.debit(id, usage);
|
|
48929
|
+
const debit = (id, model, usage) => {
|
|
48930
|
+
rateLimiter.debit(id, model, usage);
|
|
48148
48931
|
};
|
|
48149
48932
|
try {
|
|
48150
48933
|
let key;
|
|
@@ -48161,24 +48944,6 @@ async function handle(deps, rateLimiter, surface, request) {
|
|
|
48161
48944
|
throw error;
|
|
48162
48945
|
}
|
|
48163
48946
|
keyId = key.id;
|
|
48164
|
-
try {
|
|
48165
|
-
const admitted = await rateLimiter.admit(key.id, key.limits, requestId);
|
|
48166
|
-
release = admitted.release;
|
|
48167
|
-
headroom = admitted.headroom;
|
|
48168
|
-
} catch (error) {
|
|
48169
|
-
if (error instanceof RateLimitExceeded)
|
|
48170
|
-
headroom = error.headroom;
|
|
48171
|
-
if (error instanceof GatewayError && error.code === "RATE_LIMIT") {
|
|
48172
|
-
deps.logger.warn("rate limit rejected", {
|
|
48173
|
-
requestId,
|
|
48174
|
-
surface,
|
|
48175
|
-
apiKeyId: key.id,
|
|
48176
|
-
code: error.code,
|
|
48177
|
-
retryAfterMs: error.retryAfterMs
|
|
48178
|
-
});
|
|
48179
|
-
}
|
|
48180
|
-
throw error;
|
|
48181
|
-
}
|
|
48182
48947
|
const captured = await bodyCollectorFor(deps, key);
|
|
48183
48948
|
collector = captured;
|
|
48184
48949
|
if (captured !== null) {
|
|
@@ -48209,6 +48974,25 @@ async function handle(deps, rateLimiter, surface, request) {
|
|
|
48209
48974
|
throw new GatewayError("AUTH", `model "${safeToken(chatRequest.model)}" is not allowed for this API key`);
|
|
48210
48975
|
}
|
|
48211
48976
|
requestedModel = chatRequest.model;
|
|
48977
|
+
try {
|
|
48978
|
+
const admitted = await rateLimiter.admit(key.id, key.limits, requestedModel, requestId);
|
|
48979
|
+
release = admitted.release;
|
|
48980
|
+
headroom = admitted.headroom;
|
|
48981
|
+
} catch (error) {
|
|
48982
|
+
if (error instanceof RateLimitExceeded)
|
|
48983
|
+
headroom = error.headroom;
|
|
48984
|
+
if (error instanceof GatewayError && error.code === "RATE_LIMIT") {
|
|
48985
|
+
deps.logger.warn("rate limit rejected", {
|
|
48986
|
+
requestId,
|
|
48987
|
+
surface,
|
|
48988
|
+
apiKeyId: key.id,
|
|
48989
|
+
model: requestedModel,
|
|
48990
|
+
code: error.code,
|
|
48991
|
+
retryAfterMs: error.retryAfterMs
|
|
48992
|
+
});
|
|
48993
|
+
}
|
|
48994
|
+
throw error;
|
|
48995
|
+
}
|
|
48212
48996
|
let began = false;
|
|
48213
48997
|
const dispatched = await dispatch(chatRequest, {
|
|
48214
48998
|
...deps,
|
|
@@ -48344,12 +49128,12 @@ function proxyRoutes(deps) {
|
|
|
48344
49128
|
}).post("/v1/messages/count_tokens", async ({ request }) => {
|
|
48345
49129
|
try {
|
|
48346
49130
|
const key = await authenticateApiKey(deps.store, apiKeyHeader(request.headers), deps.now(), logger);
|
|
48347
|
-
await rateLimiter.consume(key.id, key.limits, deps.requestId());
|
|
48348
49131
|
const body = await request.json();
|
|
48349
49132
|
const chatRequest = parseAnthropicRequest(body, request.headers);
|
|
48350
49133
|
if (key.modelAllowlist !== null && !key.modelAllowlist.includes(chatRequest.model)) {
|
|
48351
49134
|
throw new GatewayError("AUTH", `model "${safeToken(chatRequest.model)}" is not allowed for this API key`);
|
|
48352
49135
|
}
|
|
49136
|
+
await rateLimiter.consume(key.id, key.limits, chatRequest.model, deps.requestId());
|
|
48353
49137
|
const { settings } = await dispatchDeps.snapshots.get(deps.now());
|
|
48354
49138
|
const counted = injectPonytail(chatRequest, { mode: settings.ponytailMode }).request;
|
|
48355
49139
|
return Response.json({ input_tokens: estimateInputTokens(counted) });
|
|
@@ -48570,11 +49354,11 @@ function topicClass(topic) {
|
|
|
48570
49354
|
return null;
|
|
48571
49355
|
}
|
|
48572
49356
|
var MAX_TOPIC = 128;
|
|
48573
|
-
function
|
|
49357
|
+
function isRecord11(value) {
|
|
48574
49358
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
48575
49359
|
}
|
|
48576
49360
|
function parseClientFrame(raw) {
|
|
48577
|
-
if (!
|
|
49361
|
+
if (!isRecord11(raw))
|
|
48578
49362
|
return null;
|
|
48579
49363
|
const { id, type, topic } = raw;
|
|
48580
49364
|
if (id !== undefined && typeof id !== "string")
|
|
@@ -49258,7 +50042,7 @@ function createRing(limits = DEFAULT_RING_LIMITS) {
|
|
|
49258
50042
|
}
|
|
49259
50043
|
|
|
49260
50044
|
// apps/gateway/src/version.ts
|
|
49261
|
-
var VERSION = "0.
|
|
50045
|
+
var VERSION = "0.14.1";
|
|
49262
50046
|
|
|
49263
50047
|
// apps/gateway/src/app.ts
|
|
49264
50048
|
var ADMIN_SESSION_TTL_MS = 12 * 60 * 60 * 1000;
|