omnigateway 0.13.6 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -0
- package/bin/omni.js +905 -111
- package/gateway.js +974 -189
- package/package.json +1 -1
- package/public/assets/{_app.console-facio1_r.js → _app.console-BgA6_yPW.js} +1 -1
- package/public/assets/_app.keys-CY65Ov9F.js +107 -0
- package/public/assets/{_app.plugins._pluginId-cCDDM_D_.js → _app.plugins._pluginId-B825jp0T.js} +1 -1
- package/public/assets/client-CkeRVeTV.js +39 -0
- package/public/assets/{index-BYDVTknB.js → index-Ug93No6D.js} +2 -2
- package/public/assets/limits-iRyDMg04.js +1 -0
- package/public/assets/{login-BhkdR1f4.js → login-BBURVBwb.js} +1 -1
- package/public/index.html +1 -1
- package/public/assets/_app.keys-Dq5O0AG9.js +0 -92
- package/public/assets/client-DEALADtD.js +0 -39
package/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",
|
|
@@ -24716,6 +25064,7 @@ var KNOWN_EVENTS4 = new Set([
|
|
|
24716
25064
|
"response.reasoning_text.done",
|
|
24717
25065
|
"response.function_call_arguments.delta",
|
|
24718
25066
|
"response.function_call_arguments.done",
|
|
25067
|
+
"response.metadata",
|
|
24719
25068
|
"response.completed",
|
|
24720
25069
|
"response.incomplete",
|
|
24721
25070
|
"response.failed",
|
|
@@ -24901,8 +25250,170 @@ async function* decodeResponses(messages, options = {}) {
|
|
|
24901
25250
|
}
|
|
24902
25251
|
}
|
|
24903
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
|
+
|
|
24904
25416
|
// packages/providers/src/openai/wire.ts
|
|
24905
|
-
import { createHash as createHash6 } from "crypto";
|
|
24906
25417
|
var MAX_CACHE_KEY_LENGTH = 1024;
|
|
24907
25418
|
var HEADER_SAFE_KEY = /^[\x21-\x7e]+$/;
|
|
24908
25419
|
function suppliedKey2(req) {
|
|
@@ -24914,9 +25425,16 @@ function suppliedKey2(req) {
|
|
|
24914
25425
|
}
|
|
24915
25426
|
return;
|
|
24916
25427
|
}
|
|
24917
|
-
function cacheKey2(req, instructions, firstInput) {
|
|
24918
|
-
const
|
|
24919
|
-
|
|
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 };
|
|
24920
25438
|
}
|
|
24921
25439
|
function repairOrphanedCalls2(input) {
|
|
24922
25440
|
const answered = new Set;
|
|
@@ -24947,6 +25465,44 @@ function encodeToolChoice7(c) {
|
|
|
24947
25465
|
return { type: "function", name: c.name };
|
|
24948
25466
|
}
|
|
24949
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
|
+
}
|
|
24950
25506
|
function toResponsesWire(req, model, opts = { oauth: false }) {
|
|
24951
25507
|
const degradations = [];
|
|
24952
25508
|
const input = [];
|
|
@@ -24954,6 +25510,12 @@ function toResponsesWire(req, model, opts = { oauth: false }) {
|
|
|
24954
25510
|
if (!degradations.includes(d))
|
|
24955
25511
|
degradations.push(d);
|
|
24956
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
|
+
};
|
|
24957
25519
|
if (req.betas?.includes(CONTEXT_1M_BETA))
|
|
24958
25520
|
note("openai:context-1m-dropped");
|
|
24959
25521
|
for (const message of req.messages) {
|
|
@@ -24965,10 +25527,15 @@ function toResponsesWire(req, model, opts = { oauth: false }) {
|
|
|
24965
25527
|
const flush = () => {
|
|
24966
25528
|
if (parts.length === 0)
|
|
24967
25529
|
return;
|
|
24968
|
-
|
|
25530
|
+
push({ type: "message", role, content: [...parts] });
|
|
24969
25531
|
parts.length = 0;
|
|
24970
25532
|
};
|
|
25533
|
+
let opened = false;
|
|
24971
25534
|
for (const block of message.content) {
|
|
25535
|
+
if (message.role === "user" && !opened && block.type !== "toolResult") {
|
|
25536
|
+
opened = true;
|
|
25537
|
+
turn++;
|
|
25538
|
+
}
|
|
24972
25539
|
switch (block.type) {
|
|
24973
25540
|
case "text":
|
|
24974
25541
|
parts.push({
|
|
@@ -24989,7 +25556,7 @@ function toResponsesWire(req, model, opts = { oauth: false }) {
|
|
|
24989
25556
|
break;
|
|
24990
25557
|
case "toolUse":
|
|
24991
25558
|
flush();
|
|
24992
|
-
|
|
25559
|
+
push({
|
|
24993
25560
|
type: "function_call",
|
|
24994
25561
|
call_id: block.id,
|
|
24995
25562
|
name: block.name,
|
|
@@ -25001,7 +25568,7 @@ function toResponsesWire(req, model, opts = { oauth: false }) {
|
|
|
25001
25568
|
note("openai:anthropic-native-block-dropped");
|
|
25002
25569
|
flush();
|
|
25003
25570
|
const files = responsesFiles(block.files ?? []);
|
|
25004
|
-
|
|
25571
|
+
push({
|
|
25005
25572
|
type: "function_call_output",
|
|
25006
25573
|
call_id: block.toolUseId,
|
|
25007
25574
|
output: withFileText(block.content, files.text)
|
|
@@ -25029,7 +25596,7 @@ function toResponsesWire(req, model, opts = { oauth: false }) {
|
|
|
25029
25596
|
if (block.provider === "openai") {
|
|
25030
25597
|
flush();
|
|
25031
25598
|
const { id: _id, ...rest } = block.data;
|
|
25032
|
-
|
|
25599
|
+
push({ type: block.blockType, ...rest });
|
|
25033
25600
|
break;
|
|
25034
25601
|
}
|
|
25035
25602
|
note("openai:anthropic-native-block-dropped");
|
|
@@ -25041,8 +25608,10 @@ function toResponsesWire(req, model, opts = { oauth: false }) {
|
|
|
25041
25608
|
repairOrphanedCalls2(input);
|
|
25042
25609
|
const body = { model, input, stream: req.stream, store: false };
|
|
25043
25610
|
const instructions = systemText(req.system, "openai", note);
|
|
25044
|
-
|
|
25611
|
+
const lite = opts.oauth && codexModel(model).lite;
|
|
25612
|
+
if (instructions !== undefined && instructions.length > 0 && !opts.oauth) {
|
|
25045
25613
|
body.instructions = instructions;
|
|
25614
|
+
}
|
|
25046
25615
|
if (req.maxTokens !== undefined) {
|
|
25047
25616
|
if (opts.oauth)
|
|
25048
25617
|
note("openai:max-tokens-dropped");
|
|
@@ -25055,22 +25624,45 @@ function toResponsesWire(req, model, opts = { oauth: false }) {
|
|
|
25055
25624
|
else
|
|
25056
25625
|
body.temperature = req.temperature;
|
|
25057
25626
|
}
|
|
25627
|
+
let liteTools;
|
|
25058
25628
|
if (req.tools !== undefined) {
|
|
25059
25629
|
const custom = req.tools.filter((t) => t.kind === "portable");
|
|
25060
25630
|
if (custom.length !== req.tools.length)
|
|
25061
25631
|
note("openai:anthropic-tool-dropped");
|
|
25062
|
-
|
|
25063
|
-
|
|
25064
|
-
|
|
25065
|
-
|
|
25066
|
-
|
|
25067
|
-
|
|
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
|
+
}
|
|
25068
25652
|
}
|
|
25069
25653
|
if (req.toolChoice !== undefined)
|
|
25070
25654
|
body.tool_choice = encodeToolChoice7(req.toolChoice);
|
|
25071
|
-
if (
|
|
25072
|
-
|
|
25073
|
-
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") {
|
|
25074
25666
|
if (req.reasoning.mode === "budget") {
|
|
25075
25667
|
degradations.push("openai:reasoning-budget-dropped");
|
|
25076
25668
|
} else {
|
|
@@ -25081,36 +25673,98 @@ function toResponsesWire(req, model, opts = { oauth: false }) {
|
|
|
25081
25673
|
};
|
|
25082
25674
|
}
|
|
25083
25675
|
}
|
|
25084
|
-
const
|
|
25085
|
-
|
|
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
|
+
}
|
|
25086
25685
|
const moved = moveToResponsesText(req, body, "openai");
|
|
25087
25686
|
if (moved !== undefined)
|
|
25088
25687
|
note(moved);
|
|
25089
25688
|
body.prompt_cache_key = key;
|
|
25090
|
-
|
|
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 };
|
|
25091
25715
|
}
|
|
25092
25716
|
|
|
25093
25717
|
// packages/providers/src/openai/codec.ts
|
|
25094
25718
|
var OAUTH_URL2 = "https://chatgpt.com/backend-api/codex/responses";
|
|
25095
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
|
+
}
|
|
25096
25729
|
var openaiCodec = {
|
|
25097
25730
|
buildRequest(input) {
|
|
25098
25731
|
const oauth = input.credentials.accessToken !== null;
|
|
25099
|
-
const
|
|
25100
|
-
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"]);
|
|
25101
25756
|
if (oauth) {
|
|
25102
25757
|
protocol.push(["authorization", `Bearer ${input.credentials.accessToken}`]);
|
|
25103
|
-
const accountId = input.credentials.providerData.accountId;
|
|
25104
25758
|
if (typeof accountId === "string")
|
|
25105
25759
|
protocol.push(["chatgpt-account-id", accountId]);
|
|
25760
|
+
protocol.push(["host", "chatgpt.com"]);
|
|
25106
25761
|
} else if (input.credentials.apiKey !== null) {
|
|
25107
25762
|
protocol.push(["authorization", `Bearer ${input.credentials.apiKey}`]);
|
|
25108
25763
|
} else {
|
|
25109
25764
|
throw input.fail("AUTH", "openai credential has no token");
|
|
25110
25765
|
}
|
|
25111
|
-
protocol.push(["x-client-request-id", cacheKey], ["session-id", cacheKey], ["thread-id", cacheKey]);
|
|
25112
25766
|
const headers = orderHeaders(mergeHeaders(openaiProfile.headers, protocol), openaiProfile.order);
|
|
25113
|
-
const include =
|
|
25767
|
+
const include = input.request.vendor?.openai?.include;
|
|
25114
25768
|
const nativeReasoning = Array.isArray(include) && include.includes("reasoning.encrypted_content");
|
|
25115
25769
|
return {
|
|
25116
25770
|
decodeState: { nativeReasoning },
|
|
@@ -25118,7 +25772,8 @@ var openaiCodec = {
|
|
|
25118
25772
|
url: oauth ? OAUTH_URL2 : API_URL3,
|
|
25119
25773
|
method: "POST",
|
|
25120
25774
|
headers,
|
|
25121
|
-
body: JSON.stringify(orderFields({ ...body, stream: true }, openaiBodyOrder))
|
|
25775
|
+
body: JSON.stringify(orderFields({ ...body, stream: true }, openaiBodyOrder)),
|
|
25776
|
+
...oauth ? { bodyEncoding: "zstd" } : {}
|
|
25122
25777
|
},
|
|
25123
25778
|
degradations
|
|
25124
25779
|
};
|
|
@@ -25217,11 +25872,17 @@ function createConnectFlows(deps) {
|
|
|
25217
25872
|
const provider = deps.providers[flow.provider];
|
|
25218
25873
|
if (provider === undefined)
|
|
25219
25874
|
throw new GatewayError("BAD_REQUEST", "provider does not support OAuth");
|
|
25220
|
-
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 });
|
|
25221
25885
|
if (flow.credentialId !== undefined) {
|
|
25222
|
-
const existing = await deps.store.credentials.get(flow.credentialId);
|
|
25223
|
-
if (existing === null)
|
|
25224
|
-
throw new GatewayError("BAD_REQUEST", "credential not found");
|
|
25225
25886
|
await deps.store.credentials.updateSecrets(flow.credentialId, result.secrets, result.expiresAt);
|
|
25226
25887
|
await deps.store.credentials.update(flow.credentialId, {
|
|
25227
25888
|
enabled: true,
|
|
@@ -26552,9 +27213,20 @@ function configuredWindows(limits) {
|
|
|
26552
27213
|
async function readLimitUsage(store, id, limits, now) {
|
|
26553
27214
|
if (limits === null)
|
|
26554
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) {
|
|
26555
27226
|
const sums = new Map;
|
|
26556
27227
|
for (const window2 of configuredWindows(limits)) {
|
|
26557
|
-
|
|
27228
|
+
const since = now - WINDOW_MS[window2];
|
|
27229
|
+
sums.set(window2, await store.usage.sumSince(id, since, model ?? undefined));
|
|
26558
27230
|
}
|
|
26559
27231
|
const readings = [];
|
|
26560
27232
|
for (const dimension of ["requests", "tokens", "spend"]) {
|
|
@@ -26567,7 +27239,7 @@ async function readLimitUsage(store, id, limits, now) {
|
|
|
26567
27239
|
continue;
|
|
26568
27240
|
const sum = sums.get(window2);
|
|
26569
27241
|
const used = sum === undefined ? null : dimension === "requests" ? sum.requests : dimension === "tokens" ? sum.tokens : sum.costUsd;
|
|
26570
|
-
readings.push({ dimension, window: window2, limit, used });
|
|
27242
|
+
readings.push({ dimension, window: window2, limit, used, model });
|
|
26571
27243
|
}
|
|
26572
27244
|
}
|
|
26573
27245
|
if (limits.concurrency !== undefined && limits.concurrency !== null) {
|
|
@@ -26575,7 +27247,8 @@ async function readLimitUsage(store, id, limits, now) {
|
|
|
26575
27247
|
dimension: "concurrency",
|
|
26576
27248
|
window: null,
|
|
26577
27249
|
limit: limits.concurrency,
|
|
26578
|
-
used: null
|
|
27250
|
+
used: null,
|
|
27251
|
+
model
|
|
26579
27252
|
});
|
|
26580
27253
|
}
|
|
26581
27254
|
return readings;
|
|
@@ -27549,7 +28222,7 @@ function createRefresher(deps) {
|
|
|
27549
28222
|
};
|
|
27550
28223
|
}
|
|
27551
28224
|
// packages/control/src/pluginProviders.ts
|
|
27552
|
-
function
|
|
28225
|
+
function isRecord9(value) {
|
|
27553
28226
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
27554
28227
|
}
|
|
27555
28228
|
function isNumber(value) {
|
|
@@ -27560,7 +28233,7 @@ function checkAt(ok, id, path, expected) {
|
|
|
27560
28233
|
throw new Error(`provider ${id} descriptor: ${path} must be ${expected}`);
|
|
27561
28234
|
}
|
|
27562
28235
|
function checkLimits(id, path, value) {
|
|
27563
|
-
checkAt(
|
|
28236
|
+
checkAt(isRecord9(value), id, path, "an object");
|
|
27564
28237
|
const limits = value;
|
|
27565
28238
|
checkAt(isNumber(limits.contextWindow), id, `${path}.contextWindow`, "a finite number");
|
|
27566
28239
|
checkAt(isNumber(limits.maxOutputTokens), id, `${path}.maxOutputTokens`, "a finite number");
|
|
@@ -27578,19 +28251,19 @@ function checkDescriptor(id, record) {
|
|
|
27578
28251
|
}
|
|
27579
28252
|
}
|
|
27580
28253
|
const capabilities = record.capabilities;
|
|
27581
|
-
checkAt(
|
|
28254
|
+
checkAt(isRecord9(capabilities), id, "capabilities", "an object");
|
|
27582
28255
|
for (const flag of ["tools", "images", "reasoning"]) {
|
|
27583
28256
|
const value = capabilities[flag];
|
|
27584
28257
|
checkAt(typeof value === "boolean", id, `capabilities.${flag}`, "a boolean");
|
|
27585
28258
|
}
|
|
27586
28259
|
const write = record.writeOverInput;
|
|
27587
|
-
checkAt(
|
|
28260
|
+
checkAt(isRecord9(write), id, "writeOverInput", "an object");
|
|
27588
28261
|
for (const ttl of ["fiveMinute", "oneHour"]) {
|
|
27589
28262
|
const value = write[ttl];
|
|
27590
28263
|
checkAt(isNumber(value), id, `writeOverInput.${ttl}`, "a finite number");
|
|
27591
28264
|
}
|
|
27592
28265
|
const catalog = record.catalog;
|
|
27593
|
-
checkAt(
|
|
28266
|
+
checkAt(isRecord9(catalog), id, "catalog", "an object");
|
|
27594
28267
|
const entry = catalog;
|
|
27595
28268
|
checkAt(typeof entry.defaultModel === "string", id, "catalog.defaultModel", "a string");
|
|
27596
28269
|
checkAt(Array.isArray(entry.authTypes), id, "catalog.authTypes", "an array");
|
|
@@ -27601,11 +28274,11 @@ function checkDescriptor(id, record) {
|
|
|
27601
28274
|
checkAt(Array.isArray(entry.models), id, "catalog.models", "an array");
|
|
27602
28275
|
for (const [index, model] of entry.models.entries()) {
|
|
27603
28276
|
const at = `catalog.models[${index}]`;
|
|
27604
|
-
checkAt(
|
|
28277
|
+
checkAt(isRecord9(model), id, at, "an object");
|
|
27605
28278
|
const choice = model;
|
|
27606
28279
|
checkAt(typeof choice.id === "string", id, `${at}.id`, "a string");
|
|
27607
28280
|
checkAt(typeof choice.label === "string", id, `${at}.label`, "a string");
|
|
27608
|
-
checkAt(
|
|
28281
|
+
checkAt(isRecord9(choice.pricing), id, `${at}.pricing`, "an object");
|
|
27609
28282
|
const pricing = choice.pricing;
|
|
27610
28283
|
for (const field of ["input", "output", "cacheRead", "cacheWrite5m", "cacheWrite1h"]) {
|
|
27611
28284
|
checkAt(isNumber(pricing[field]), id, `${at}.pricing.${field}`, "a finite number");
|
|
@@ -27621,13 +28294,13 @@ function checkDescriptor(id, record) {
|
|
|
27621
28294
|
checkAt(typeof prefix === "string", id, `modelPrefixes[${index}]`, "a string");
|
|
27622
28295
|
}
|
|
27623
28296
|
const presentation = record.presentation;
|
|
27624
|
-
checkAt(
|
|
28297
|
+
checkAt(isRecord9(presentation), id, "presentation", "an object");
|
|
27625
28298
|
const shown = presentation;
|
|
27626
28299
|
for (const field of ["label", "tone"]) {
|
|
27627
28300
|
checkAt(typeof shown[field] === "string", id, `presentation.${field}`, "a string");
|
|
27628
28301
|
}
|
|
27629
28302
|
checkAt(isNumber(shown.order), id, "presentation.order", "a finite number");
|
|
27630
|
-
checkAt(
|
|
28303
|
+
checkAt(isRecord9(shown.colour), id, "presentation.colour", "an object");
|
|
27631
28304
|
for (const scheme of ["light", "dark"]) {
|
|
27632
28305
|
const value = shown.colour[scheme];
|
|
27633
28306
|
checkAt(typeof value === "string", id, `presentation.colour.${scheme}`, "a string");
|
|
@@ -27639,7 +28312,7 @@ function checkDescriptor(id, record) {
|
|
|
27639
28312
|
function validateOAuthFlow(id, value) {
|
|
27640
28313
|
if (value === undefined)
|
|
27641
28314
|
return;
|
|
27642
|
-
if (!
|
|
28315
|
+
if (!isRecord9(value))
|
|
27643
28316
|
throw new Error(`provider ${id} oauth must be an object`);
|
|
27644
28317
|
const kind = value.kind;
|
|
27645
28318
|
if (kind !== "pkce" && kind !== "device") {
|
|
@@ -28296,7 +28969,7 @@ function readFrom(path, offset) {
|
|
|
28296
28969
|
}
|
|
28297
28970
|
}
|
|
28298
28971
|
// packages/plugin-api/src/events.ts
|
|
28299
|
-
var
|
|
28972
|
+
var WINDOW_MS3 = {
|
|
28300
28973
|
"1m": 60000,
|
|
28301
28974
|
"5h": 5 * 60 * 60 * 1000,
|
|
28302
28975
|
"1w": 7 * 24 * 60 * 60 * 1000
|
|
@@ -41647,8 +42320,26 @@ var SEED_LOCK_MS = 5000;
|
|
|
41647
42320
|
var GAUGE_TTL_MS = 300000;
|
|
41648
42321
|
var RENEW_INTERVAL_MS = 1e5;
|
|
41649
42322
|
var MAX_RENEWED_MS = 86400000;
|
|
41650
|
-
function bucketKey(
|
|
41651
|
-
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;
|
|
41652
42343
|
}
|
|
41653
42344
|
|
|
41654
42345
|
class RateLimitExceeded extends GatewayError {
|
|
@@ -41703,27 +42394,31 @@ class ApiKeyRateLimiter {
|
|
|
41703
42394
|
this.coord = deps.coord ?? memoryCoord();
|
|
41704
42395
|
this.onRejected = deps.onRejected;
|
|
41705
42396
|
}
|
|
41706
|
-
async admit(keyId, limits, requestId) {
|
|
41707
|
-
|
|
42397
|
+
async admit(keyId, limits, model, requestId) {
|
|
42398
|
+
const subjects = subjectsOf(keyId, limits, model);
|
|
42399
|
+
if (subjects.length === 0)
|
|
41708
42400
|
return { release: () => {}, renew: async () => {}, headroom: {} };
|
|
41709
42401
|
const now = this.now();
|
|
41710
|
-
|
|
41711
|
-
const holder = configured(limits.concurrency) ? crypto.randomUUID() : undefined;
|
|
41712
|
-
const claim = await this.claim(keyId, now, true, holder);
|
|
42402
|
+
const claims = [];
|
|
41713
42403
|
let admitted = false;
|
|
41714
42404
|
try {
|
|
41715
|
-
|
|
41716
|
-
|
|
41717
|
-
|
|
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);
|
|
41718
42411
|
admitted = true;
|
|
42412
|
+
const held = claims.flatMap((claim) => claim.holder === undefined ? [] : [{ subject: claim.subject, holder: claim.holder }]);
|
|
41719
42413
|
let released = false;
|
|
41720
42414
|
const renewUntil = now + MAX_RENEWED_MS;
|
|
41721
42415
|
const renew = async () => {
|
|
41722
|
-
if (released
|
|
42416
|
+
if (released)
|
|
41723
42417
|
return;
|
|
41724
|
-
|
|
42418
|
+
for (const slot of held)
|
|
42419
|
+
await this.coord.gauge.renew(slot.subject, slot.holder, GAUGE_TTL_MS);
|
|
41725
42420
|
};
|
|
41726
|
-
const renewal =
|
|
42421
|
+
const renewal = held.length === 0 ? null : setInterval(() => {
|
|
41727
42422
|
if (this.now() >= renewUntil) {
|
|
41728
42423
|
clearInterval(renewal ?? undefined);
|
|
41729
42424
|
return;
|
|
@@ -41737,91 +42432,153 @@ class ApiKeyRateLimiter {
|
|
|
41737
42432
|
released = true;
|
|
41738
42433
|
if (renewal !== null)
|
|
41739
42434
|
clearInterval(renewal);
|
|
41740
|
-
|
|
42435
|
+
for (const claim of claims)
|
|
42436
|
+
this.coord.gauge.release(claim.subject, claim.holder);
|
|
41741
42437
|
};
|
|
41742
42438
|
return { release, renew, headroom: decision.headroom };
|
|
41743
42439
|
} finally {
|
|
41744
42440
|
if (!admitted)
|
|
41745
|
-
|
|
42441
|
+
for (const claim of claims)
|
|
42442
|
+
await this.rollback(claim);
|
|
41746
42443
|
}
|
|
41747
42444
|
}
|
|
41748
|
-
async consume(keyId, limits, requestId) {
|
|
41749
|
-
const
|
|
41750
|
-
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)
|
|
41751
42448
|
return;
|
|
41752
42449
|
const now = this.now();
|
|
41753
|
-
|
|
41754
|
-
const claim = await this.claim(keyId, now, false);
|
|
42450
|
+
const claims = [];
|
|
41755
42451
|
let consumed = false;
|
|
41756
42452
|
try {
|
|
41757
|
-
const
|
|
41758
|
-
|
|
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);
|
|
41759
42458
|
consumed = true;
|
|
41760
42459
|
} finally {
|
|
41761
42460
|
if (!consumed)
|
|
41762
|
-
|
|
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;
|
|
41763
42488
|
}
|
|
42489
|
+
return {
|
|
42490
|
+
subject,
|
|
42491
|
+
stamp: ring.stamp,
|
|
42492
|
+
gauge,
|
|
42493
|
+
holder,
|
|
42494
|
+
before: { requests: ring.before, concurrency }
|
|
42495
|
+
};
|
|
41764
42496
|
}
|
|
41765
|
-
async claim
|
|
41766
|
-
|
|
41767
|
-
const concurrency = gauge ? await this.coord.gauge.acquire(keyId, GAUGE_TTL_MS, holder) : await this.coord.gauge.read(keyId);
|
|
41768
|
-
return { stamp: ring.stamp, gauge, holder, before: { requests: ring.before, concurrency } };
|
|
41769
|
-
}
|
|
41770
|
-
async rollback(keyId, claim) {
|
|
41771
|
-
await this.coord.window.rollback(keyId, claim.stamp);
|
|
42497
|
+
async rollback(claim) {
|
|
42498
|
+
await this.coord.window.rollback(claim.subject, claim.stamp);
|
|
41772
42499
|
if (claim.gauge)
|
|
41773
|
-
await this.coord.gauge.release(
|
|
42500
|
+
await this.coord.gauge.release(claim.subject, claim.holder);
|
|
41774
42501
|
}
|
|
41775
|
-
debit(keyId, usage) {
|
|
41776
|
-
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);
|
|
41777
42509
|
if (limits === undefined || !anyLongLimit(limits))
|
|
41778
42510
|
return;
|
|
41779
42511
|
const now = this.now();
|
|
41780
|
-
const delta = { requests: 1, tokens: usage.tokens, costUsd: usage.costUsd };
|
|
41781
42512
|
for (const window2 of LONG_WINDOWS) {
|
|
41782
|
-
this.coord.buckets.add(bucketKey(
|
|
42513
|
+
this.coord.buckets.add(bucketKey(subject, window2), GRAIN_MS[window2], WINDOW_MS[window2], now, delta);
|
|
41783
42514
|
}
|
|
41784
42515
|
}
|
|
41785
|
-
inFlight(
|
|
41786
|
-
return this.coord.gauge.read(
|
|
42516
|
+
inFlight(subject) {
|
|
42517
|
+
return this.coord.gauge.read(subject);
|
|
41787
42518
|
}
|
|
41788
|
-
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);
|
|
41789
42528
|
const violation = decision.violation;
|
|
41790
42529
|
if (violation === null)
|
|
41791
|
-
|
|
41792
|
-
const window2 = violation.window;
|
|
41793
|
-
const exact = await this.exactReset(keyId, window2, now, requestId);
|
|
41794
|
-
const resolved = exact === null ? violation : { ...violation, resetAt: exact };
|
|
41795
|
-
const headroom = exact === null || window2 === null ? decision.headroom : withReset(decision.headroom, window2, exact);
|
|
42530
|
+
throw new Error("rate limit refusal with no violation");
|
|
41796
42531
|
try {
|
|
41797
42532
|
this.onRejected?.(violation.dimension, violation.window);
|
|
41798
42533
|
} catch {}
|
|
41799
|
-
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 };
|
|
41800
42556
|
}
|
|
41801
|
-
async exactReset(
|
|
42557
|
+
async exactReset(subject, window2, now, requestId) {
|
|
41802
42558
|
if (window2 === null || window2 === "1m")
|
|
41803
42559
|
return null;
|
|
41804
42560
|
const length = WINDOW_MS[window2];
|
|
41805
42561
|
try {
|
|
41806
|
-
const oldest = await this.store.usage.oldestSince(keyId, now - length);
|
|
42562
|
+
const oldest = await this.store.usage.oldestSince(subject.keyId, now - length, subject.model);
|
|
41807
42563
|
if (oldest === null)
|
|
41808
42564
|
return null;
|
|
41809
42565
|
return oldest + length;
|
|
41810
42566
|
} catch (error) {
|
|
41811
42567
|
this.logger.warn("rate limit reset unavailable", {
|
|
41812
42568
|
...requestId === undefined ? {} : { requestId },
|
|
41813
|
-
apiKeyId: keyId,
|
|
42569
|
+
apiKeyId: subject.keyId,
|
|
42570
|
+
...subject.model === undefined ? {} : { model: subject.model },
|
|
41814
42571
|
reason: describeError(error, "unknown")
|
|
41815
42572
|
});
|
|
41816
42573
|
return null;
|
|
41817
42574
|
}
|
|
41818
42575
|
}
|
|
41819
|
-
async counters(
|
|
42576
|
+
async counters(subject, limits, claim, now, requestId) {
|
|
41820
42577
|
const requests = { "1m": claim.before.requests };
|
|
41821
42578
|
const snapshot = { requests, concurrency: claim.before.concurrency };
|
|
41822
42579
|
if (!anyLongLimit(limits))
|
|
41823
42580
|
return snapshot;
|
|
41824
|
-
const counts = await this.longCounts(
|
|
42581
|
+
const counts = await this.longCounts(subject, now, requestId);
|
|
41825
42582
|
if (counts === null)
|
|
41826
42583
|
return snapshot;
|
|
41827
42584
|
const tokens = {};
|
|
@@ -41837,20 +42594,21 @@ class ApiKeyRateLimiter {
|
|
|
41837
42594
|
snapshot.spend = spend;
|
|
41838
42595
|
return snapshot;
|
|
41839
42596
|
}
|
|
41840
|
-
async longCounts(
|
|
42597
|
+
async longCounts(subject, now, requestId) {
|
|
41841
42598
|
const out = {};
|
|
41842
42599
|
for (const window2 of LONG_WINDOWS) {
|
|
41843
|
-
const key = bucketKey(
|
|
42600
|
+
const key = bucketKey(subject.id, window2);
|
|
41844
42601
|
const grain = GRAIN_MS[window2];
|
|
41845
42602
|
const length = WINDOW_MS[window2];
|
|
41846
42603
|
let sum = await this.coord.buckets.sum(key, grain, length, now);
|
|
41847
42604
|
if (sum === null) {
|
|
41848
42605
|
try {
|
|
41849
|
-
sum = await this.seed(
|
|
42606
|
+
sum = await this.seed(subject, window2, now);
|
|
41850
42607
|
} catch (error) {
|
|
41851
42608
|
this.logger.warn("rate limit counters unavailable", {
|
|
41852
42609
|
...requestId === undefined ? {} : { requestId },
|
|
41853
|
-
apiKeyId: keyId,
|
|
42610
|
+
apiKeyId: subject.keyId,
|
|
42611
|
+
...subject.model === undefined ? {} : { model: subject.model },
|
|
41854
42612
|
reason: describeError(error, "unknown")
|
|
41855
42613
|
});
|
|
41856
42614
|
return null;
|
|
@@ -41860,15 +42618,15 @@ class ApiKeyRateLimiter {
|
|
|
41860
42618
|
}
|
|
41861
42619
|
return out;
|
|
41862
42620
|
}
|
|
41863
|
-
async seed(
|
|
41864
|
-
const key = bucketKey(
|
|
42621
|
+
async seed(subject, window2, now) {
|
|
42622
|
+
const key = bucketKey(subject.id, window2);
|
|
41865
42623
|
const grain = GRAIN_MS[window2];
|
|
41866
42624
|
const length = WINDOW_MS[window2];
|
|
41867
42625
|
const install = async () => {
|
|
41868
42626
|
const already = await this.coord.buckets.sum(key, grain, length, now);
|
|
41869
42627
|
if (already !== null)
|
|
41870
42628
|
return already;
|
|
41871
|
-
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);
|
|
41872
42630
|
await this.coord.buckets.seed(key, grain, length, now, rows);
|
|
41873
42631
|
return await this.coord.buckets.sum(key, grain, length, now) ?? zero();
|
|
41874
42632
|
};
|
|
@@ -41884,6 +42642,9 @@ class ApiKeyRateLimiter {
|
|
|
41884
42642
|
function zero() {
|
|
41885
42643
|
return { requests: 0, tokens: 0, costUsd: 0 };
|
|
41886
42644
|
}
|
|
42645
|
+
function requestsOnly(limits) {
|
|
42646
|
+
return limits.requests === undefined ? {} : { requests: limits.requests };
|
|
42647
|
+
}
|
|
41887
42648
|
function withReset(headroom, window2, resetAt) {
|
|
41888
42649
|
const corrected = { ...headroom };
|
|
41889
42650
|
for (const dimension of ["requests", "tokens", "spend"]) {
|
|
@@ -41893,6 +42654,27 @@ function withReset(headroom, window2, resetAt) {
|
|
|
41893
42654
|
}
|
|
41894
42655
|
return corrected;
|
|
41895
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
|
+
}
|
|
41896
42678
|
|
|
41897
42679
|
// apps/gateway/src/dispatch/loadRegistry.ts
|
|
41898
42680
|
var PREFIX6 = "load:";
|
|
@@ -42810,7 +43592,7 @@ function zodDetail(issue) {
|
|
|
42810
43592
|
}
|
|
42811
43593
|
return issue.message;
|
|
42812
43594
|
}
|
|
42813
|
-
function
|
|
43595
|
+
function isRecord10(value) {
|
|
42814
43596
|
return typeof value === "object" && value !== null;
|
|
42815
43597
|
}
|
|
42816
43598
|
function extraFields(body, known) {
|
|
@@ -42953,7 +43735,7 @@ async function readJson(request) {
|
|
|
42953
43735
|
}
|
|
42954
43736
|
async function readJsonRecord(request) {
|
|
42955
43737
|
const body = await readJson(request);
|
|
42956
|
-
return
|
|
43738
|
+
return isRecord10(body) ? body : null;
|
|
42957
43739
|
}
|
|
42958
43740
|
function apiErrorResponse(error) {
|
|
42959
43741
|
const gatewayError = error instanceof GatewayError ? error : new GatewayError("INTERNAL", "internal error");
|
|
@@ -46040,7 +46822,7 @@ async function finishLog(store, log, keyId, logger = noopLogger, bodies, debit,
|
|
|
46040
46822
|
report(logger, "failed to persist request log", log.id, error);
|
|
46041
46823
|
}
|
|
46042
46824
|
if (keyId !== null && debit !== undefined) {
|
|
46043
|
-
debit(keyId, {
|
|
46825
|
+
debit(keyId, log.requestedModel, {
|
|
46044
46826
|
tokens: log.inputTokens + log.outputTokens + log.cacheReadTokens + log.cacheWriteTokens,
|
|
46045
46827
|
costUsd: log.costUsd
|
|
46046
46828
|
});
|
|
@@ -46209,6 +46991,7 @@ async function attempt(opts) {
|
|
|
46209
46991
|
signal,
|
|
46210
46992
|
...opts.requestId === undefined ? {} : { requestId: opts.requestId },
|
|
46211
46993
|
...opts.autoCache === undefined ? {} : { autoCache: opts.autoCache },
|
|
46994
|
+
...opts.requestStartedAt === undefined ? {} : { requestStartedAt: opts.requestStartedAt },
|
|
46212
46995
|
...candidate.target.maxOutputTokens === undefined ? {} : { maxOutputTokens: candidate.target.maxOutputTokens }
|
|
46213
46996
|
});
|
|
46214
46997
|
}
|
|
@@ -46528,6 +47311,7 @@ async function dispatch(request, deps, signal, requestId) {
|
|
|
46528
47311
|
...retrySecrets === undefined ? {} : { secrets: retrySecrets },
|
|
46529
47312
|
logger,
|
|
46530
47313
|
requestId,
|
|
47314
|
+
requestStartedAt: startedAt,
|
|
46531
47315
|
autoCache: snapshot.settings.autoCacheEnabled
|
|
46532
47316
|
}), dispatchSignal);
|
|
46533
47317
|
noteDegradations(result.degradations);
|
|
@@ -47373,10 +48157,10 @@ function toIrToolChoice(c) {
|
|
|
47373
48157
|
}
|
|
47374
48158
|
var EFFORTS = REASONING_EFFORTS;
|
|
47375
48159
|
function readEffort(body) {
|
|
47376
|
-
if (!
|
|
48160
|
+
if (!isRecord10(body))
|
|
47377
48161
|
return;
|
|
47378
48162
|
const outputConfig = body.output_config;
|
|
47379
|
-
if (!
|
|
48163
|
+
if (!isRecord10(outputConfig))
|
|
47380
48164
|
return;
|
|
47381
48165
|
const effort = outputConfig.effort;
|
|
47382
48166
|
return EFFORTS.find((level) => level === effort);
|
|
@@ -47758,7 +48542,7 @@ function roleOf(raw) {
|
|
|
47758
48542
|
return "user";
|
|
47759
48543
|
}
|
|
47760
48544
|
function contentPart(part) {
|
|
47761
|
-
if (!
|
|
48545
|
+
if (!isRecord10(part))
|
|
47762
48546
|
throw new GatewayError("BAD_REQUEST", "input: content part must be an object");
|
|
47763
48547
|
const type = part.type;
|
|
47764
48548
|
if (type === "input_text" || type === "output_text") {
|
|
@@ -47782,11 +48566,11 @@ function readToolOutput(output) {
|
|
|
47782
48566
|
const images = [];
|
|
47783
48567
|
const files = [];
|
|
47784
48568
|
for (const part of output) {
|
|
47785
|
-
if (
|
|
48569
|
+
if (isRecord10(part) && (part.type === "input_text" || part.type === "output_text")) {
|
|
47786
48570
|
text.push(String(part.text ?? ""));
|
|
47787
|
-
} else if (
|
|
48571
|
+
} else if (isRecord10(part) && part.type === "input_image" && typeof part.image_url === "string" && part.image_url.startsWith("data:")) {
|
|
47788
48572
|
images.push({ type: "image", ...parseDataUrl(part.image_url) });
|
|
47789
|
-
} else if (
|
|
48573
|
+
} else if (isRecord10(part) && part.type === "input_file" && typeof part.file_data === "string") {
|
|
47790
48574
|
files.push({
|
|
47791
48575
|
type: "file",
|
|
47792
48576
|
...fileData(part.file_data),
|
|
@@ -47837,7 +48621,7 @@ function readInputItems(items) {
|
|
|
47837
48621
|
messages.push({ role, content });
|
|
47838
48622
|
};
|
|
47839
48623
|
for (const raw of items) {
|
|
47840
|
-
if (!
|
|
48624
|
+
if (!isRecord10(raw))
|
|
47841
48625
|
throw new GatewayError("BAD_REQUEST", "input: item must be an object");
|
|
47842
48626
|
const type = raw.type ?? (raw.role === undefined ? undefined : "message");
|
|
47843
48627
|
switch (type) {
|
|
@@ -47893,7 +48677,7 @@ function jsonArguments(raw, callId) {
|
|
|
47893
48677
|
const text = typeof raw === "string" ? raw : "";
|
|
47894
48678
|
try {
|
|
47895
48679
|
const parsed = text === "" ? {} : JSON.parse(text);
|
|
47896
|
-
return
|
|
48680
|
+
return isRecord10(parsed) ? parsed : { value: parsed };
|
|
47897
48681
|
} catch {
|
|
47898
48682
|
throw new GatewayError("BAD_REQUEST", `input: arguments for call ${safeToken(callId)} are not valid JSON`);
|
|
47899
48683
|
}
|
|
@@ -47902,7 +48686,7 @@ var TOOL_NAME = /^[A-Za-z0-9_-]{1,128}$/;
|
|
|
47902
48686
|
function readTools(raw) {
|
|
47903
48687
|
const tools = [];
|
|
47904
48688
|
for (const entry of raw) {
|
|
47905
|
-
if (!
|
|
48689
|
+
if (!isRecord10(entry))
|
|
47906
48690
|
throw new GatewayError("BAD_REQUEST", "tools: each tool must be an object");
|
|
47907
48691
|
const type = typeof entry.type === "string" ? entry.type : "";
|
|
47908
48692
|
const declared = typeof entry.name === "string" ? entry.name : "";
|
|
@@ -47915,7 +48699,7 @@ function readTools(raw) {
|
|
|
47915
48699
|
kind: "portable",
|
|
47916
48700
|
name: declared,
|
|
47917
48701
|
...description === undefined ? {} : { description },
|
|
47918
|
-
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" }
|
|
47919
48703
|
});
|
|
47920
48704
|
continue;
|
|
47921
48705
|
}
|
|
@@ -47930,10 +48714,10 @@ function readTools(raw) {
|
|
|
47930
48714
|
}
|
|
47931
48715
|
function customToolNames(body) {
|
|
47932
48716
|
const names = new Set;
|
|
47933
|
-
if (!
|
|
48717
|
+
if (!isRecord10(body) || !Array.isArray(body.tools))
|
|
47934
48718
|
return names;
|
|
47935
48719
|
for (const entry of body.tools) {
|
|
47936
|
-
if (
|
|
48720
|
+
if (isRecord10(entry) && entry.type === "custom" && typeof entry.name === "string") {
|
|
47937
48721
|
names.add(entry.name);
|
|
47938
48722
|
}
|
|
47939
48723
|
}
|
|
@@ -48142,8 +48926,8 @@ async function handle(deps, rateLimiter, surface, request) {
|
|
|
48142
48926
|
let streaming = false;
|
|
48143
48927
|
let headroom = {};
|
|
48144
48928
|
const limitHeaders = () => rateLimitHeaders(surface, release === null ? headroom : afterThisRequest(headroom), startedAt);
|
|
48145
|
-
const debit = (id, usage) => {
|
|
48146
|
-
rateLimiter.debit(id, usage);
|
|
48929
|
+
const debit = (id, model, usage) => {
|
|
48930
|
+
rateLimiter.debit(id, model, usage);
|
|
48147
48931
|
};
|
|
48148
48932
|
try {
|
|
48149
48933
|
let key;
|
|
@@ -48160,24 +48944,6 @@ async function handle(deps, rateLimiter, surface, request) {
|
|
|
48160
48944
|
throw error;
|
|
48161
48945
|
}
|
|
48162
48946
|
keyId = key.id;
|
|
48163
|
-
try {
|
|
48164
|
-
const admitted = await rateLimiter.admit(key.id, key.limits, requestId);
|
|
48165
|
-
release = admitted.release;
|
|
48166
|
-
headroom = admitted.headroom;
|
|
48167
|
-
} catch (error) {
|
|
48168
|
-
if (error instanceof RateLimitExceeded)
|
|
48169
|
-
headroom = error.headroom;
|
|
48170
|
-
if (error instanceof GatewayError && error.code === "RATE_LIMIT") {
|
|
48171
|
-
deps.logger.warn("rate limit rejected", {
|
|
48172
|
-
requestId,
|
|
48173
|
-
surface,
|
|
48174
|
-
apiKeyId: key.id,
|
|
48175
|
-
code: error.code,
|
|
48176
|
-
retryAfterMs: error.retryAfterMs
|
|
48177
|
-
});
|
|
48178
|
-
}
|
|
48179
|
-
throw error;
|
|
48180
|
-
}
|
|
48181
48947
|
const captured = await bodyCollectorFor(deps, key);
|
|
48182
48948
|
collector = captured;
|
|
48183
48949
|
if (captured !== null) {
|
|
@@ -48208,6 +48974,25 @@ async function handle(deps, rateLimiter, surface, request) {
|
|
|
48208
48974
|
throw new GatewayError("AUTH", `model "${safeToken(chatRequest.model)}" is not allowed for this API key`);
|
|
48209
48975
|
}
|
|
48210
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
|
+
}
|
|
48211
48996
|
let began = false;
|
|
48212
48997
|
const dispatched = await dispatch(chatRequest, {
|
|
48213
48998
|
...deps,
|
|
@@ -48343,12 +49128,12 @@ function proxyRoutes(deps) {
|
|
|
48343
49128
|
}).post("/v1/messages/count_tokens", async ({ request }) => {
|
|
48344
49129
|
try {
|
|
48345
49130
|
const key = await authenticateApiKey(deps.store, apiKeyHeader(request.headers), deps.now(), logger);
|
|
48346
|
-
await rateLimiter.consume(key.id, key.limits, deps.requestId());
|
|
48347
49131
|
const body = await request.json();
|
|
48348
49132
|
const chatRequest = parseAnthropicRequest(body, request.headers);
|
|
48349
49133
|
if (key.modelAllowlist !== null && !key.modelAllowlist.includes(chatRequest.model)) {
|
|
48350
49134
|
throw new GatewayError("AUTH", `model "${safeToken(chatRequest.model)}" is not allowed for this API key`);
|
|
48351
49135
|
}
|
|
49136
|
+
await rateLimiter.consume(key.id, key.limits, chatRequest.model, deps.requestId());
|
|
48352
49137
|
const { settings } = await dispatchDeps.snapshots.get(deps.now());
|
|
48353
49138
|
const counted = injectPonytail(chatRequest, { mode: settings.ponytailMode }).request;
|
|
48354
49139
|
return Response.json({ input_tokens: estimateInputTokens(counted) });
|
|
@@ -48569,11 +49354,11 @@ function topicClass(topic) {
|
|
|
48569
49354
|
return null;
|
|
48570
49355
|
}
|
|
48571
49356
|
var MAX_TOPIC = 128;
|
|
48572
|
-
function
|
|
49357
|
+
function isRecord11(value) {
|
|
48573
49358
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
48574
49359
|
}
|
|
48575
49360
|
function parseClientFrame(raw) {
|
|
48576
|
-
if (!
|
|
49361
|
+
if (!isRecord11(raw))
|
|
48577
49362
|
return null;
|
|
48578
49363
|
const { id, type, topic } = raw;
|
|
48579
49364
|
if (id !== undefined && typeof id !== "string")
|
|
@@ -49257,7 +50042,7 @@ function createRing(limits = DEFAULT_RING_LIMITS) {
|
|
|
49257
50042
|
}
|
|
49258
50043
|
|
|
49259
50044
|
// apps/gateway/src/version.ts
|
|
49260
|
-
var VERSION = "0.
|
|
50045
|
+
var VERSION = "0.14.0";
|
|
49261
50046
|
|
|
49262
50047
|
// apps/gateway/src/app.ts
|
|
49263
50048
|
var ADMIN_SESSION_TTL_MS = 12 * 60 * 60 * 1000;
|