omnigateway 0.1.3 → 0.1.5
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/bin/omni.js +101 -37
- package/gateway.js +332 -227
- package/package.json +1 -1
- package/public/assets/{Chip-D0YiqYkz.js → Chip-Ccqsb_Mq.js} +2 -2
- package/public/assets/{CopyValue-bzKz2tiJ.js → CopyValue-CvEMy9Zo.js} +5 -5
- package/public/assets/{Field-DdscQ9OO.js → Field-Devm4K46.js} +8 -8
- package/public/assets/Lamp-BXSUIAtG.js +25 -0
- package/public/assets/{Meter-Bm1oQE2U.js → Meter-Fn8X7t60.js} +2 -2
- package/public/assets/{Modal-Crr8uyWt.js → Modal-C4y9zALa.js} +8 -8
- package/public/assets/{Rack-uDxrjvaz.js → Rack-CRQSwSeB.js} +18 -18
- package/public/assets/{Readout-CYg6UOop.js → Readout-BKpOZIGX.js} +5 -5
- package/public/assets/{States-DPTTYUHL.js → States-DBzsiHjQ.js} +10 -10
- package/public/assets/{Table-LkPaHBsj.js → Table-DPDJeeVc.js} +1 -1
- package/public/assets/{Toggle-D0e4C94X.js → Toggle-CBq5XAMU.js} +3 -3
- package/public/assets/_app-VCodWes4.js +1 -0
- package/public/assets/_app.accounts-BP3L84us.js +51 -0
- package/public/assets/_app.index-C5Vd-zlp.js +61 -0
- package/public/assets/{_app.keys-BNEOjtbu.js → _app.keys-0e1BHrIZ.js} +8 -8
- package/public/assets/_app.logs-DNSND96R.js +33 -0
- package/public/assets/_app.models-DzFIbnem.js +144 -0
- package/public/assets/{_app.settings-C1gB4w7c.js → _app.settings-Du5Nsp9f.js} +6 -6
- package/public/assets/{_app.usage-B0iHKzmI.js → _app.usage-DKq4Hsia.js} +28 -28
- package/public/assets/index-BYyeRTQJ.js +170 -0
- package/public/assets/{login-D-FsWNX8.js → login-Gyf-1fSa.js} +8 -8
- package/public/assets/{queries-vY5lJqBe.js → queries-BMwc6EvM.js} +1 -1
- package/public/assets/{trash-2-Cep_aVLx.js → trash-2-DFVxurNH.js} +1 -1
- package/public/index.html +3 -3
- package/public/assets/Lamp-WOotyOTd.js +0 -15
- package/public/assets/_app-OSvB-Dv9.js +0 -1
- package/public/assets/_app.accounts-BOE49WyS.js +0 -51
- package/public/assets/_app.index-CTRlqxOq.js +0 -61
- package/public/assets/_app.logs-BxxqeEhM.js +0 -18
- package/public/assets/_app.models-BJi-RGUO.js +0 -144
- package/public/assets/index--dA3FFSN.js +0 -170
package/gateway.js
CHANGED
|
@@ -22634,6 +22634,27 @@ ALTER TABLE credentials ADD COLUMN disabled_reason TEXT;
|
|
|
22634
22634
|
ALTER TABLE credentials ADD COLUMN disabled_at INTEGER;
|
|
22635
22635
|
`;
|
|
22636
22636
|
|
|
22637
|
+
// packages/store/src/sqlite/migrations/004_request_state.sql
|
|
22638
|
+
var _004_request_state_default = `-- Whether a request is still running.
|
|
22639
|
+
--
|
|
22640
|
+
-- A row used to be written once, after the response stream drained, so a long
|
|
22641
|
+
-- stream was invisible for its whole life and the console showed an idle
|
|
22642
|
+
-- gateway. A row is now written twice: \`pending\` when dispatch starts, \`done\`
|
|
22643
|
+
-- when it finishes.
|
|
22644
|
+
--
|
|
22645
|
+
-- Existing rows default to 'done'. Every row that predates this migration
|
|
22646
|
+
-- describes a request that has already ended, by definition.
|
|
22647
|
+
ALTER TABLE request_logs ADD COLUMN state TEXT NOT NULL DEFAULT 'done';
|
|
22648
|
+
|
|
22649
|
+
-- \`status\` and \`duration_ms\` stay NOT NULL: making them nullable means a table
|
|
22650
|
+
-- rebuild in SQLite, and a pending row's zeros are never read. Readers key off
|
|
22651
|
+
-- \`state\` alone.
|
|
22652
|
+
--
|
|
22653
|
+
-- Partial, because pending rows are a handful at any moment while the table
|
|
22654
|
+
-- holds a month of finished ones.
|
|
22655
|
+
CREATE INDEX idx_request_logs_pending ON request_logs(state) WHERE state = 'pending';
|
|
22656
|
+
`;
|
|
22657
|
+
|
|
22637
22658
|
// packages/store/src/sqlite/rollup.ts
|
|
22638
22659
|
function startOfLocalDay(at) {
|
|
22639
22660
|
const day = new Date(at);
|
|
@@ -22740,7 +22761,8 @@ function backfillDaily(db) {
|
|
|
22740
22761
|
var MIGRATIONS = [
|
|
22741
22762
|
{ id: 1, sql: _001_init_default },
|
|
22742
22763
|
{ id: 2, sql: _002_usage_daily_default, after: backfillDaily },
|
|
22743
|
-
{ id: 3, sql: _003_quota_snapshot_default }
|
|
22764
|
+
{ id: 3, sql: _003_quota_snapshot_default },
|
|
22765
|
+
{ id: 4, sql: _004_request_state_default }
|
|
22744
22766
|
];
|
|
22745
22767
|
function openDb(path) {
|
|
22746
22768
|
const db = new Database(path, { create: true });
|
|
@@ -22812,6 +22834,7 @@ function createKeyRepo(db) {
|
|
|
22812
22834
|
// packages/store/src/sqlite/usage.ts
|
|
22813
22835
|
var toLog = (r) => ({
|
|
22814
22836
|
id: r.id,
|
|
22837
|
+
state: r.state === "pending" ? "pending" : "done",
|
|
22815
22838
|
at: r.at,
|
|
22816
22839
|
apiKeyId: r.api_key_id,
|
|
22817
22840
|
requestedModel: r.requested_model,
|
|
@@ -22870,37 +22893,78 @@ function label(value) {
|
|
|
22870
22893
|
const text = String(value);
|
|
22871
22894
|
return text.length === 0 ? "unknown" : text;
|
|
22872
22895
|
}
|
|
22896
|
+
var COLUMNS = `(id, state, at, api_key_id, requested_model, resolved_provider, resolved_model,
|
|
22897
|
+
credential_id, attempts, status, error_code, input_tokens, output_tokens,
|
|
22898
|
+
cache_read_tokens, cache_write_tokens, ttft_ms, duration_ms, cost_usd,
|
|
22899
|
+
degradations)`;
|
|
22900
|
+
var PLACEHOLDERS = "(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)";
|
|
22901
|
+
function values(log, state) {
|
|
22902
|
+
return [
|
|
22903
|
+
log.id,
|
|
22904
|
+
state,
|
|
22905
|
+
log.at,
|
|
22906
|
+
log.apiKeyId,
|
|
22907
|
+
log.requestedModel,
|
|
22908
|
+
log.resolvedProvider,
|
|
22909
|
+
log.resolvedModel,
|
|
22910
|
+
log.credentialId,
|
|
22911
|
+
log.attempts,
|
|
22912
|
+
log.status,
|
|
22913
|
+
log.errorCode,
|
|
22914
|
+
log.inputTokens,
|
|
22915
|
+
log.outputTokens,
|
|
22916
|
+
log.cacheReadTokens,
|
|
22917
|
+
log.cacheWriteTokens,
|
|
22918
|
+
log.ttftMs,
|
|
22919
|
+
log.durationMs,
|
|
22920
|
+
log.costUsd,
|
|
22921
|
+
JSON.stringify(log.degradations)
|
|
22922
|
+
];
|
|
22923
|
+
}
|
|
22924
|
+
var COMPLETE = `INSERT INTO request_logs ${COLUMNS} VALUES ${PLACEHOLDERS}
|
|
22925
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
22926
|
+
state = 'done',
|
|
22927
|
+
requested_model = COALESCE(NULLIF(excluded.requested_model, ''), request_logs.requested_model),
|
|
22928
|
+
api_key_id = COALESCE(excluded.api_key_id, request_logs.api_key_id),
|
|
22929
|
+
resolved_provider = excluded.resolved_provider,
|
|
22930
|
+
resolved_model = excluded.resolved_model,
|
|
22931
|
+
credential_id = excluded.credential_id,
|
|
22932
|
+
attempts = excluded.attempts,
|
|
22933
|
+
status = excluded.status,
|
|
22934
|
+
error_code = excluded.error_code,
|
|
22935
|
+
input_tokens = excluded.input_tokens,
|
|
22936
|
+
output_tokens = excluded.output_tokens,
|
|
22937
|
+
cache_read_tokens = excluded.cache_read_tokens,
|
|
22938
|
+
cache_write_tokens = excluded.cache_write_tokens,
|
|
22939
|
+
ttft_ms = excluded.ttft_ms,
|
|
22940
|
+
duration_ms = excluded.duration_ms,
|
|
22941
|
+
cost_usd = excluded.cost_usd,
|
|
22942
|
+
degradations = excluded.degradations`;
|
|
22873
22943
|
function createUsageRepo(db) {
|
|
22874
|
-
const
|
|
22875
|
-
db.run(
|
|
22876
|
-
|
|
22877
|
-
|
|
22878
|
-
|
|
22879
|
-
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)`, [
|
|
22880
|
-
log.id,
|
|
22881
|
-
log.at,
|
|
22882
|
-
log.apiKeyId,
|
|
22883
|
-
log.requestedModel,
|
|
22884
|
-
log.resolvedProvider,
|
|
22885
|
-
log.resolvedModel,
|
|
22886
|
-
log.credentialId,
|
|
22887
|
-
log.attempts,
|
|
22888
|
-
log.status,
|
|
22889
|
-
log.errorCode,
|
|
22890
|
-
log.inputTokens,
|
|
22891
|
-
log.outputTokens,
|
|
22892
|
-
log.cacheReadTokens,
|
|
22893
|
-
log.cacheWriteTokens,
|
|
22894
|
-
log.ttftMs,
|
|
22895
|
-
log.durationMs,
|
|
22896
|
-
log.costUsd,
|
|
22897
|
-
JSON.stringify(log.degradations)
|
|
22898
|
-
]);
|
|
22899
|
-
rollupLog(db, log);
|
|
22944
|
+
const complete = db.transaction((log) => {
|
|
22945
|
+
db.run(COMPLETE, values(log, "done"));
|
|
22946
|
+
const stored = db.query("SELECT * FROM request_logs WHERE id = ?").get(log.id);
|
|
22947
|
+
if (stored !== null)
|
|
22948
|
+
rollupLog(db, toLog(stored));
|
|
22900
22949
|
});
|
|
22901
22950
|
return {
|
|
22951
|
+
async begin(log) {
|
|
22952
|
+
db.run(`INSERT INTO request_logs ${COLUMNS} VALUES ${PLACEHOLDERS}`, values(log, "pending"));
|
|
22953
|
+
},
|
|
22954
|
+
async route(id, target) {
|
|
22955
|
+
db.run(`UPDATE request_logs
|
|
22956
|
+
SET resolved_provider = ?, resolved_model = ?, credential_id = ?
|
|
22957
|
+
WHERE id = ? AND state = 'pending'`, [target.provider, target.model, target.credentialId, id]);
|
|
22958
|
+
},
|
|
22902
22959
|
async append(log) {
|
|
22903
|
-
|
|
22960
|
+
complete(log);
|
|
22961
|
+
},
|
|
22962
|
+
async sweepPending() {
|
|
22963
|
+
const stale = db.query("SELECT * FROM request_logs WHERE state = 'pending'").all().map(toLog);
|
|
22964
|
+
for (const log of stale) {
|
|
22965
|
+
complete({ ...log, status: 499, errorCode: "interrupted", durationMs: 0 });
|
|
22966
|
+
}
|
|
22967
|
+
return stale.length;
|
|
22904
22968
|
},
|
|
22905
22969
|
async recent(limit) {
|
|
22906
22970
|
return db.query("SELECT * FROM request_logs ORDER BY at DESC LIMIT ?").all(limit).map(toLog);
|
|
@@ -22922,7 +22986,7 @@ function createUsageRepo(db) {
|
|
|
22922
22986
|
COALESCE(SUM(cache_write_tokens), 0) AS cache_write_tokens,
|
|
22923
22987
|
COALESCE(SUM(cost_usd), 0) AS cost_usd
|
|
22924
22988
|
FROM ${daily ? "usage_daily" : "request_logs"}
|
|
22925
|
-
WHERE ${timeColumn} >= ? AND ${timeColumn} <= ?
|
|
22989
|
+
WHERE ${daily ? "" : "state = 'done' AND "}${timeColumn} >= ? AND ${timeColumn} <= ?
|
|
22926
22990
|
GROUP BY key${split === null ? "" : ", split"}
|
|
22927
22991
|
ORDER BY requests DESC`).all(since, until);
|
|
22928
22992
|
return rows.map((r) => {
|
|
@@ -31467,17 +31531,17 @@ var ElysiaType = {
|
|
|
31467
31531
|
elysiaMeta: "ArrayString"
|
|
31468
31532
|
})).Decode((value) => {
|
|
31469
31533
|
if (Array.isArray(value)) {
|
|
31470
|
-
let
|
|
31534
|
+
let values2 = [];
|
|
31471
31535
|
for (let i = 0;i < value.length; i++) {
|
|
31472
31536
|
const v = value[i];
|
|
31473
31537
|
if (typeof v == "string") {
|
|
31474
31538
|
const t2 = decode4(v, true);
|
|
31475
|
-
Array.isArray(t2) ?
|
|
31539
|
+
Array.isArray(t2) ? values2 = values2.concat(t2) : values2.push(t2);
|
|
31476
31540
|
continue;
|
|
31477
31541
|
}
|
|
31478
|
-
|
|
31542
|
+
values2.push(v);
|
|
31479
31543
|
}
|
|
31480
|
-
return
|
|
31544
|
+
return values2;
|
|
31481
31545
|
}
|
|
31482
31546
|
return typeof value == "string" ? decode4(value) : value;
|
|
31483
31547
|
}).Encode((value) => {
|
|
@@ -31498,17 +31562,17 @@ var ElysiaType = {
|
|
|
31498
31562
|
elysiaMeta: "ArrayQuery"
|
|
31499
31563
|
})).Decode((value) => {
|
|
31500
31564
|
if (Array.isArray(value)) {
|
|
31501
|
-
let
|
|
31565
|
+
let values2 = [];
|
|
31502
31566
|
for (let i = 0;i < value.length; i++) {
|
|
31503
31567
|
const v = value[i];
|
|
31504
31568
|
if (typeof v == "string") {
|
|
31505
31569
|
const t2 = decode4(v);
|
|
31506
|
-
Array.isArray(t2) ?
|
|
31570
|
+
Array.isArray(t2) ? values2 = values2.concat(t2) : values2.push(t2);
|
|
31507
31571
|
continue;
|
|
31508
31572
|
}
|
|
31509
|
-
|
|
31573
|
+
values2.push(v);
|
|
31510
31574
|
}
|
|
31511
|
-
return
|
|
31575
|
+
return values2;
|
|
31512
31576
|
}
|
|
31513
31577
|
return typeof value == "string" ? decode4(value) : value;
|
|
31514
31578
|
}).Encode((value) => {
|
|
@@ -31552,16 +31616,16 @@ var ElysiaType = {
|
|
|
31552
31616
|
sign
|
|
31553
31617
|
}, v;
|
|
31554
31618
|
},
|
|
31555
|
-
UnionEnum: (
|
|
31556
|
-
const type =
|
|
31557
|
-
if (
|
|
31619
|
+
UnionEnum: (values2, options = {}) => {
|
|
31620
|
+
const type = values2.every((value) => typeof value == "string") ? { type: "string" } : values2.every((value) => typeof value == "number") ? { type: "number" } : values2.every((value) => value === null) ? { type: "null" } : {};
|
|
31621
|
+
if (values2.some((x) => typeof x == "object" && x !== null))
|
|
31558
31622
|
throw new Error("This type does not support objects or arrays");
|
|
31559
31623
|
return {
|
|
31560
|
-
default:
|
|
31624
|
+
default: values2[0],
|
|
31561
31625
|
...options,
|
|
31562
31626
|
[Kind]: "UnionEnum",
|
|
31563
31627
|
...type,
|
|
31564
|
-
enum:
|
|
31628
|
+
enum: values2
|
|
31565
31629
|
};
|
|
31566
31630
|
},
|
|
31567
31631
|
NoValidate: (v, enabled = true) => (v.noValidate = enabled, v),
|
|
@@ -34007,33 +34071,33 @@ var getSchemaValidator = (s, {
|
|
|
34007
34071
|
return v.then((v2) => Check22(v2, true));
|
|
34008
34072
|
if (v.issues)
|
|
34009
34073
|
return v;
|
|
34010
|
-
const
|
|
34011
|
-
return v && typeof v == "object" &&
|
|
34012
|
-
}, runCheckers2 = function(value, startIndex,
|
|
34074
|
+
const values2 = [];
|
|
34075
|
+
return v && typeof v == "object" && values2.push(v.value), runCheckers2(value, 0, values2, v);
|
|
34076
|
+
}, runCheckers2 = function(value, startIndex, values2, lastV) {
|
|
34013
34077
|
for (let i = startIndex;i < checkers.length; i++) {
|
|
34014
34078
|
let v = checkers[i].validate(value);
|
|
34015
34079
|
if (v instanceof Promise)
|
|
34016
34080
|
return v.then((resolved) => {
|
|
34017
34081
|
if (resolved.issues)
|
|
34018
34082
|
return resolved;
|
|
34019
|
-
const nextValues = [...
|
|
34083
|
+
const nextValues = [...values2];
|
|
34020
34084
|
return resolved && typeof resolved == "object" && nextValues.push(resolved.value), runCheckers2(value, i + 1, nextValues, resolved);
|
|
34021
34085
|
});
|
|
34022
34086
|
if (v.issues)
|
|
34023
34087
|
return v;
|
|
34024
|
-
v && typeof v == "object" &&
|
|
34088
|
+
v && typeof v == "object" && values2.push(v.value), lastV = v;
|
|
34025
34089
|
}
|
|
34026
|
-
return mergeValues22(
|
|
34027
|
-
}, mergeValues22 = function(
|
|
34028
|
-
if (!
|
|
34090
|
+
return mergeValues22(values2, lastV);
|
|
34091
|
+
}, mergeValues22 = function(values2, lastV) {
|
|
34092
|
+
if (!values2.length)
|
|
34029
34093
|
return { value: lastV };
|
|
34030
|
-
if (
|
|
34031
|
-
return { value:
|
|
34032
|
-
if (
|
|
34033
|
-
return { value: mergeDeep(
|
|
34034
|
-
let newValue = mergeDeep(
|
|
34035
|
-
for (let i = 2;i <
|
|
34036
|
-
newValue = mergeDeep(newValue,
|
|
34094
|
+
if (values2.length === 1)
|
|
34095
|
+
return { value: values2[0] };
|
|
34096
|
+
if (values2.length === 2)
|
|
34097
|
+
return { value: mergeDeep(values2[0], values2[1]) };
|
|
34098
|
+
let newValue = mergeDeep(values2[0], values2[1]);
|
|
34099
|
+
for (let i = 2;i < values2.length; i++)
|
|
34100
|
+
newValue = mergeDeep(newValue, values2[i]);
|
|
34037
34101
|
return { value: newValue };
|
|
34038
34102
|
};
|
|
34039
34103
|
var Check2 = Check22, runCheckers = runCheckers2, mergeValues2 = mergeValues22;
|
|
@@ -37924,7 +37988,18 @@ function parseDataUrl(url2) {
|
|
|
37924
37988
|
return { mediaType: match[1], data: match[2] };
|
|
37925
37989
|
}
|
|
37926
37990
|
|
|
37927
|
-
// apps/gateway/src/routes/
|
|
37991
|
+
// apps/gateway/src/routes/http.ts
|
|
37992
|
+
function readCookie(request2, name) {
|
|
37993
|
+
const header = request2.headers.get("cookie");
|
|
37994
|
+
if (header === null)
|
|
37995
|
+
return null;
|
|
37996
|
+
for (const part of header.split(";")) {
|
|
37997
|
+
const [key, ...rest] = part.trim().split("=");
|
|
37998
|
+
if (key === name)
|
|
37999
|
+
return rest.join("=");
|
|
38000
|
+
}
|
|
38001
|
+
return null;
|
|
38002
|
+
}
|
|
37928
38003
|
function sessionCookie(request2, token, maxAge) {
|
|
37929
38004
|
const secure = new URL(request2.url).protocol === "https:" ? ["Secure"] : [];
|
|
37930
38005
|
return [
|
|
@@ -37936,38 +38011,36 @@ function sessionCookie(request2, token, maxAge) {
|
|
|
37936
38011
|
`Max-Age=${maxAge}`
|
|
37937
38012
|
].join("; ");
|
|
37938
38013
|
}
|
|
37939
|
-
function
|
|
37940
|
-
const
|
|
37941
|
-
if (
|
|
37942
|
-
|
|
37943
|
-
for (const part of header.split(";")) {
|
|
37944
|
-
const [key, ...rest] = part.trim().split("=");
|
|
37945
|
-
if (key === name)
|
|
37946
|
-
return rest.join("=");
|
|
38014
|
+
async function requireAdmin(request2, admin) {
|
|
38015
|
+
const token = readCookie(request2, ADMIN_COOKIE);
|
|
38016
|
+
if (token === null || !await admin.verify(token)) {
|
|
38017
|
+
throw new GatewayError("AUTH", "admin session required");
|
|
37947
38018
|
}
|
|
37948
|
-
return null;
|
|
37949
38019
|
}
|
|
37950
|
-
async function
|
|
38020
|
+
async function readJson(request2) {
|
|
37951
38021
|
try {
|
|
37952
|
-
|
|
37953
|
-
|
|
37954
|
-
|
|
37955
|
-
|
|
38022
|
+
return await request2.json();
|
|
38023
|
+
} catch (error51) {
|
|
38024
|
+
if (!(error51 instanceof SyntaxError))
|
|
38025
|
+
throw error51;
|
|
38026
|
+
throw new GatewayError("BAD_REQUEST", "invalid JSON body");
|
|
37956
38027
|
}
|
|
37957
38028
|
}
|
|
38029
|
+
async function readJsonRecord(request2) {
|
|
38030
|
+
const body2 = await readJson(request2);
|
|
38031
|
+
return isRecord3(body2) ? body2 : null;
|
|
38032
|
+
}
|
|
38033
|
+
function apiErrorResponse(error51) {
|
|
38034
|
+
const gatewayError = error51 instanceof GatewayError ? error51 : new GatewayError("INTERNAL", "internal error");
|
|
38035
|
+
return Response.json({ error: { code: gatewayError.code, message: gatewayError.message } }, { status: HTTP_STATUS[gatewayError.code] });
|
|
38036
|
+
}
|
|
38037
|
+
function apiErrorHandler(input) {
|
|
38038
|
+
return input.code === "NOT_FOUND" ? undefined : apiErrorResponse(input.error);
|
|
38039
|
+
}
|
|
38040
|
+
|
|
38041
|
+
// apps/gateway/src/routes/admin.ts
|
|
37958
38042
|
function adminRoutes(deps) {
|
|
37959
|
-
|
|
37960
|
-
const gatewayError = error51 instanceof GatewayError ? error51 : new GatewayError("INTERNAL", "internal error");
|
|
37961
|
-
set2.status = HTTP_STATUS[gatewayError.code];
|
|
37962
|
-
return { error: { code: gatewayError.code, message: gatewayError.message } };
|
|
37963
|
-
});
|
|
37964
|
-
async function requireAdmin(request2) {
|
|
37965
|
-
const token = readCookie(request2, ADMIN_COOKIE);
|
|
37966
|
-
if (token === null || !await deps.admin.verify(token)) {
|
|
37967
|
-
throw new GatewayError("AUTH", "admin session required");
|
|
37968
|
-
}
|
|
37969
|
-
}
|
|
37970
|
-
return app.get("/api/status", async ({ request: request2 }) => {
|
|
38043
|
+
return new Elysia().onError(apiErrorHandler).get("/api/status", async ({ request: request2 }) => {
|
|
37971
38044
|
const token = readCookie(request2, ADMIN_COOKIE);
|
|
37972
38045
|
return {
|
|
37973
38046
|
configured: await deps.admin.isConfigured(),
|
|
@@ -37978,7 +38051,7 @@ function adminRoutes(deps) {
|
|
|
37978
38051
|
set2.status = 409;
|
|
37979
38052
|
return { error: { code: "CONFLICT", message: "an admin password is already configured" } };
|
|
37980
38053
|
}
|
|
37981
|
-
const body2 = await
|
|
38054
|
+
const body2 = await readJsonRecord(request2);
|
|
37982
38055
|
if (typeof body2?.password !== "string") {
|
|
37983
38056
|
throw new GatewayError("BAD_REQUEST", "password is required");
|
|
37984
38057
|
}
|
|
@@ -37998,7 +38071,7 @@ function adminRoutes(deps) {
|
|
|
37998
38071
|
set2.headers["set-cookie"] = sessionCookie(request2, token, Math.floor(deps.sessionTtlMs / 1000));
|
|
37999
38072
|
return { ok: true };
|
|
38000
38073
|
}).post("/api/login", async ({ request: request2, set: set2 }) => {
|
|
38001
|
-
const body2 = await
|
|
38074
|
+
const body2 = await readJsonRecord(request2);
|
|
38002
38075
|
const token = typeof body2?.password === "string" ? await deps.admin.login(body2.password) : null;
|
|
38003
38076
|
if (token === null)
|
|
38004
38077
|
throw new GatewayError("AUTH", "invalid password");
|
|
@@ -38011,52 +38084,52 @@ function adminRoutes(deps) {
|
|
|
38011
38084
|
set2.headers["set-cookie"] = sessionCookie(request2, "", 0);
|
|
38012
38085
|
return { ok: true };
|
|
38013
38086
|
}).get("/api/credentials", async ({ request: request2 }) => {
|
|
38014
|
-
await requireAdmin(request2);
|
|
38087
|
+
await requireAdmin(request2, deps.admin);
|
|
38015
38088
|
return { credentials: await listCredentials(deps.store) };
|
|
38016
38089
|
}).get("/api/credentials/health", async ({ request: request2 }) => {
|
|
38017
|
-
await requireAdmin(request2);
|
|
38090
|
+
await requireAdmin(request2, deps.admin);
|
|
38018
38091
|
return credentialHealth(deps.store);
|
|
38019
38092
|
}).patch("/api/credentials/:id", async ({ request: request2, params }) => {
|
|
38020
|
-
await requireAdmin(request2);
|
|
38021
|
-
await patchCredential(deps, params.id, await request2
|
|
38093
|
+
await requireAdmin(request2, deps.admin);
|
|
38094
|
+
await patchCredential(deps, params.id, await readJson(request2));
|
|
38022
38095
|
return { ok: true };
|
|
38023
38096
|
}).delete("/api/credentials/:id", async ({ request: request2, params }) => {
|
|
38024
|
-
await requireAdmin(request2);
|
|
38097
|
+
await requireAdmin(request2, deps.admin);
|
|
38025
38098
|
await removeCredential(deps.store, params.id);
|
|
38026
38099
|
return { ok: true };
|
|
38027
38100
|
}).get("/api/models", async ({ request: request2 }) => {
|
|
38028
|
-
await requireAdmin(request2);
|
|
38101
|
+
await requireAdmin(request2, deps.admin);
|
|
38029
38102
|
return { models: await listModels(deps.store) };
|
|
38030
38103
|
}).put("/api/models/:id", async ({ request: request2, params }) => {
|
|
38031
|
-
await requireAdmin(request2);
|
|
38032
|
-
await putModel(deps.store, params.id, await request2
|
|
38104
|
+
await requireAdmin(request2, deps.admin);
|
|
38105
|
+
await putModel(deps.store, params.id, await readJson(request2));
|
|
38033
38106
|
return { ok: true };
|
|
38034
38107
|
}).delete("/api/models/:id", async ({ request: request2, params }) => {
|
|
38035
|
-
await requireAdmin(request2);
|
|
38108
|
+
await requireAdmin(request2, deps.admin);
|
|
38036
38109
|
await removeModel(deps.store, params.id);
|
|
38037
38110
|
return { ok: true };
|
|
38038
38111
|
}).get("/api/keys", async ({ request: request2 }) => {
|
|
38039
|
-
await requireAdmin(request2);
|
|
38112
|
+
await requireAdmin(request2, deps.admin);
|
|
38040
38113
|
return { keys: await listKeys(deps.store) };
|
|
38041
38114
|
}).post("/api/keys", async ({ request: request2 }) => {
|
|
38042
|
-
await requireAdmin(request2);
|
|
38043
|
-
return createKey(deps.store, await request2
|
|
38115
|
+
await requireAdmin(request2, deps.admin);
|
|
38116
|
+
return createKey(deps.store, await readJson(request2));
|
|
38044
38117
|
}).delete("/api/keys/:id", async ({ request: request2, params }) => {
|
|
38045
|
-
await requireAdmin(request2);
|
|
38118
|
+
await requireAdmin(request2, deps.admin);
|
|
38046
38119
|
await revokeKey(deps.store, params.id);
|
|
38047
38120
|
return { ok: true };
|
|
38048
38121
|
}).get("/api/settings", async ({ request: request2 }) => {
|
|
38049
|
-
await requireAdmin(request2);
|
|
38122
|
+
await requireAdmin(request2, deps.admin);
|
|
38050
38123
|
return { settings: await getSettings(deps.store) };
|
|
38051
38124
|
}).put("/api/settings", async ({ request: request2 }) => {
|
|
38052
|
-
await requireAdmin(request2);
|
|
38053
|
-
await putSettings(deps.store, await request2
|
|
38125
|
+
await requireAdmin(request2, deps.admin);
|
|
38126
|
+
await putSettings(deps.store, await readJson(request2));
|
|
38054
38127
|
return { ok: true };
|
|
38055
38128
|
}).post("/api/models/:id/dry-run", async ({ request: request2, params }) => {
|
|
38056
|
-
await requireAdmin(request2);
|
|
38057
|
-
return dryRun(deps, params.id, await request2
|
|
38129
|
+
await requireAdmin(request2, deps.admin);
|
|
38130
|
+
return dryRun(deps, params.id, await readJson(request2));
|
|
38058
38131
|
}).get("/api/usage", async ({ request: request2, query }) => {
|
|
38059
|
-
await requireAdmin(request2);
|
|
38132
|
+
await requireAdmin(request2, deps.admin);
|
|
38060
38133
|
return {
|
|
38061
38134
|
rows: await queryUsage(deps, {
|
|
38062
38135
|
grain: query.grain,
|
|
@@ -38067,70 +38140,29 @@ function adminRoutes(deps) {
|
|
|
38067
38140
|
})
|
|
38068
38141
|
};
|
|
38069
38142
|
}).get("/api/logs", async ({ request: request2, query }) => {
|
|
38070
|
-
await requireAdmin(request2);
|
|
38143
|
+
await requireAdmin(request2, deps.admin);
|
|
38071
38144
|
return { logs: await recentLogs(deps.store, query.limit) };
|
|
38072
38145
|
});
|
|
38073
38146
|
}
|
|
38074
38147
|
|
|
38075
38148
|
// apps/gateway/src/routes/connect.ts
|
|
38076
|
-
function asGatewayError(error51) {
|
|
38077
|
-
if (error51 instanceof GatewayError)
|
|
38078
|
-
return error51;
|
|
38079
|
-
return new GatewayError("INTERNAL", error51 instanceof Error ? error51.message : "internal error");
|
|
38080
|
-
}
|
|
38081
|
-
function errorResponse(error51) {
|
|
38082
|
-
const gatewayError = asGatewayError(error51);
|
|
38083
|
-
return new Response(JSON.stringify({ error: { code: gatewayError.code, message: gatewayError.message } }), {
|
|
38084
|
-
status: HTTP_STATUS[gatewayError.code],
|
|
38085
|
-
headers: { "content-type": "application/json" }
|
|
38086
|
-
});
|
|
38087
|
-
}
|
|
38088
|
-
function readCookie2(request2, name) {
|
|
38089
|
-
const header = request2.headers.get("cookie");
|
|
38090
|
-
if (header === null)
|
|
38091
|
-
return null;
|
|
38092
|
-
for (const part of header.split(";")) {
|
|
38093
|
-
const [key, ...rest] = part.trim().split("=");
|
|
38094
|
-
if (key === name)
|
|
38095
|
-
return rest.join("=");
|
|
38096
|
-
}
|
|
38097
|
-
return null;
|
|
38098
|
-
}
|
|
38099
38149
|
function connectRoutes(deps) {
|
|
38100
38150
|
const flows = createConnectFlows(deps);
|
|
38101
|
-
async
|
|
38102
|
-
|
|
38103
|
-
|
|
38104
|
-
|
|
38105
|
-
}
|
|
38106
|
-
}
|
|
38107
|
-
return new Elysia().post("/api/connect/start", async ({ request: request2 }) => {
|
|
38108
|
-
try {
|
|
38109
|
-
await requireAdmin(request2);
|
|
38110
|
-
const body2 = await request2.json();
|
|
38111
|
-
return await flows.start(isRecord3(body2) ? body2.provider : undefined, isRecord3(body2) ? body2.label : undefined);
|
|
38112
|
-
} catch (error51) {
|
|
38113
|
-
return errorResponse(error51);
|
|
38114
|
-
}
|
|
38151
|
+
return new Elysia().onError(apiErrorHandler).post("/api/connect/start", async ({ request: request2 }) => {
|
|
38152
|
+
await requireAdmin(request2, deps.admin);
|
|
38153
|
+
const body2 = await readJsonRecord(request2);
|
|
38154
|
+
return flows.start(body2?.provider, body2?.label);
|
|
38115
38155
|
}).post("/api/connect/finish", async ({ request: request2 }) => {
|
|
38116
|
-
|
|
38117
|
-
|
|
38118
|
-
|
|
38119
|
-
return await flows.finish(isRecord3(body2) ? body2.flowId : undefined, isRecord3(body2) ? body2.code : undefined);
|
|
38120
|
-
} catch (error51) {
|
|
38121
|
-
return errorResponse(error51);
|
|
38122
|
-
}
|
|
38156
|
+
await requireAdmin(request2, deps.admin);
|
|
38157
|
+
const body2 = await readJsonRecord(request2);
|
|
38158
|
+
return flows.finish(body2?.flowId, body2?.code);
|
|
38123
38159
|
}).post("/api/connect/poll", async ({ request: request2, set: set2 }) => {
|
|
38124
|
-
|
|
38125
|
-
|
|
38126
|
-
|
|
38127
|
-
|
|
38128
|
-
|
|
38129
|
-
|
|
38130
|
-
return outcome;
|
|
38131
|
-
} catch (error51) {
|
|
38132
|
-
return errorResponse(error51);
|
|
38133
|
-
}
|
|
38160
|
+
await requireAdmin(request2, deps.admin);
|
|
38161
|
+
const body2 = await readJsonRecord(request2);
|
|
38162
|
+
const outcome = await flows.poll(body2?.flowId);
|
|
38163
|
+
if (outcome.status === "pending")
|
|
38164
|
+
set2.status = 202;
|
|
38165
|
+
return outcome;
|
|
38134
38166
|
});
|
|
38135
38167
|
}
|
|
38136
38168
|
|
|
@@ -38169,6 +38201,73 @@ function extractToken(header) {
|
|
|
38169
38201
|
return match === null ? value : match[1].trim();
|
|
38170
38202
|
}
|
|
38171
38203
|
|
|
38204
|
+
// apps/gateway/src/logging.ts
|
|
38205
|
+
function requestLogDefaults(id, at) {
|
|
38206
|
+
return {
|
|
38207
|
+
id,
|
|
38208
|
+
state: "done",
|
|
38209
|
+
at,
|
|
38210
|
+
apiKeyId: null,
|
|
38211
|
+
requestedModel: "",
|
|
38212
|
+
resolvedProvider: null,
|
|
38213
|
+
resolvedModel: null,
|
|
38214
|
+
credentialId: null,
|
|
38215
|
+
attempts: 0,
|
|
38216
|
+
status: 0,
|
|
38217
|
+
errorCode: null,
|
|
38218
|
+
inputTokens: 0,
|
|
38219
|
+
outputTokens: 0,
|
|
38220
|
+
cacheReadTokens: 0,
|
|
38221
|
+
cacheWriteTokens: 0,
|
|
38222
|
+
ttftMs: null,
|
|
38223
|
+
durationMs: 0,
|
|
38224
|
+
costUsd: 0,
|
|
38225
|
+
degradations: []
|
|
38226
|
+
};
|
|
38227
|
+
}
|
|
38228
|
+
function newCompletedRequestLog(id, at, overrides) {
|
|
38229
|
+
return { ...requestLogDefaults(id, at), ...overrides, id, state: "done", at };
|
|
38230
|
+
}
|
|
38231
|
+
function newPendingRequestLog(input) {
|
|
38232
|
+
const { id, at, requestedModel, resolvedProvider, resolvedModel, credentialId } = input;
|
|
38233
|
+
return {
|
|
38234
|
+
...requestLogDefaults(id, at),
|
|
38235
|
+
requestedModel,
|
|
38236
|
+
resolvedProvider,
|
|
38237
|
+
resolvedModel,
|
|
38238
|
+
credentialId,
|
|
38239
|
+
state: "pending",
|
|
38240
|
+
status: 0
|
|
38241
|
+
};
|
|
38242
|
+
}
|
|
38243
|
+
function report(what, requestId, error51) {
|
|
38244
|
+
console.error(what, {
|
|
38245
|
+
requestId,
|
|
38246
|
+
reason: error51 instanceof Error ? error51.message : "unknown"
|
|
38247
|
+
});
|
|
38248
|
+
}
|
|
38249
|
+
async function beginLog(store, log, keyId) {
|
|
38250
|
+
try {
|
|
38251
|
+
await store.usage.begin({ ...log, state: "pending", apiKeyId: keyId });
|
|
38252
|
+
} catch (error51) {
|
|
38253
|
+
report("failed to record request start", log.id, error51);
|
|
38254
|
+
}
|
|
38255
|
+
}
|
|
38256
|
+
async function routeLog(store, requestId, target) {
|
|
38257
|
+
try {
|
|
38258
|
+
await store.usage.route(requestId, target);
|
|
38259
|
+
} catch (error51) {
|
|
38260
|
+
report("failed to record request route", requestId, error51);
|
|
38261
|
+
}
|
|
38262
|
+
}
|
|
38263
|
+
async function finishLog(store, log, keyId) {
|
|
38264
|
+
try {
|
|
38265
|
+
await store.usage.append({ ...log, apiKeyId: keyId });
|
|
38266
|
+
} catch (error51) {
|
|
38267
|
+
report("failed to persist request log", log.id, error51);
|
|
38268
|
+
}
|
|
38269
|
+
}
|
|
38270
|
+
|
|
38172
38271
|
// apps/gateway/src/dispatch/attempt.ts
|
|
38173
38272
|
async function attempt(opts) {
|
|
38174
38273
|
const { candidate, adapter, http, now, signal, refresh, refreshLeadMs } = opts;
|
|
@@ -38222,30 +38321,14 @@ function classify(error51) {
|
|
|
38222
38321
|
}
|
|
38223
38322
|
|
|
38224
38323
|
// apps/gateway/src/dispatch/index.ts
|
|
38225
|
-
async function dispatch(request2, deps, signal) {
|
|
38324
|
+
async function dispatch(request2, deps, signal, requestId) {
|
|
38226
38325
|
const startedAt = deps.now();
|
|
38227
38326
|
const snapshot = await deps.snapshots.get(startedAt);
|
|
38228
38327
|
const deadlineAt = startedAt + snapshot.settings.requestDeadlineMs;
|
|
38229
|
-
const log = {
|
|
38230
|
-
id: crypto.randomUUID(),
|
|
38231
|
-
at: startedAt,
|
|
38232
|
-
apiKeyId: null,
|
|
38328
|
+
const log = newCompletedRequestLog(requestId, startedAt, {
|
|
38233
38329
|
requestedModel: request2.model,
|
|
38234
|
-
|
|
38235
|
-
|
|
38236
|
-
credentialId: null,
|
|
38237
|
-
attempts: 0,
|
|
38238
|
-
status: 200,
|
|
38239
|
-
errorCode: null,
|
|
38240
|
-
inputTokens: 0,
|
|
38241
|
-
outputTokens: 0,
|
|
38242
|
-
cacheReadTokens: 0,
|
|
38243
|
-
cacheWriteTokens: 0,
|
|
38244
|
-
ttftMs: null,
|
|
38245
|
-
durationMs: 0,
|
|
38246
|
-
costUsd: 0,
|
|
38247
|
-
degradations: []
|
|
38248
|
-
};
|
|
38330
|
+
status: 0
|
|
38331
|
+
});
|
|
38249
38332
|
const fail = (code, message) => {
|
|
38250
38333
|
log.errorCode = code;
|
|
38251
38334
|
log.status = HTTP_STATUS[code];
|
|
@@ -38319,6 +38402,11 @@ async function dispatch(request2, deps, signal) {
|
|
|
38319
38402
|
log.credentialId = candidate.credential.id;
|
|
38320
38403
|
log.resolvedProvider = candidate.target.provider;
|
|
38321
38404
|
log.resolvedModel = candidate.target.model;
|
|
38405
|
+
await deps.onRoute?.({
|
|
38406
|
+
provider: candidate.target.provider,
|
|
38407
|
+
model: candidate.target.model,
|
|
38408
|
+
credentialId: candidate.credential.id
|
|
38409
|
+
});
|
|
38322
38410
|
log.inputTokens = 0;
|
|
38323
38411
|
log.outputTokens = 0;
|
|
38324
38412
|
log.cacheReadTokens = 0;
|
|
@@ -39115,38 +39203,35 @@ function parseOpenAIRequest(body2) {
|
|
|
39115
39203
|
return validateRequest(request2);
|
|
39116
39204
|
}
|
|
39117
39205
|
|
|
39118
|
-
// apps/gateway/src/logging.ts
|
|
39119
|
-
async function finishLog(store, log, keyId) {
|
|
39120
|
-
try {
|
|
39121
|
-
await store.usage.append({ ...log, apiKeyId: keyId });
|
|
39122
|
-
} catch (error51) {
|
|
39123
|
-
console.error("failed to persist request log", {
|
|
39124
|
-
requestId: log.id,
|
|
39125
|
-
reason: error51 instanceof Error ? error51.message : "unknown"
|
|
39126
|
-
});
|
|
39127
|
-
}
|
|
39128
|
-
}
|
|
39129
|
-
|
|
39130
39206
|
// apps/gateway/src/routes/proxy.ts
|
|
39207
|
+
var KEEPALIVE_MS = 1e4;
|
|
39208
|
+
var KEEPALIVE = Symbol("keepalive");
|
|
39209
|
+
function withKeepalive(pending, ms) {
|
|
39210
|
+
let timer;
|
|
39211
|
+
const tick = new Promise((resolve) => {
|
|
39212
|
+
timer = setTimeout(() => resolve(KEEPALIVE), ms);
|
|
39213
|
+
});
|
|
39214
|
+
return Promise.race([pending, tick]).finally(() => clearTimeout(timer));
|
|
39215
|
+
}
|
|
39131
39216
|
var SSE_HEADERS = {
|
|
39132
39217
|
"content-type": "text/event-stream; charset=utf-8",
|
|
39133
39218
|
"cache-control": "no-cache, no-transform",
|
|
39134
39219
|
connection: "keep-alive",
|
|
39135
39220
|
"x-accel-buffering": "no"
|
|
39136
39221
|
};
|
|
39137
|
-
function
|
|
39222
|
+
function errorResponse(surface, code, message3) {
|
|
39138
39223
|
const body2 = surface === "anthropic" ? anthropicErrorBody(code, message3) : openaiErrorBody(code, message3);
|
|
39139
39224
|
return new Response(JSON.stringify(body2), {
|
|
39140
39225
|
status: HTTP_STATUS[code],
|
|
39141
39226
|
headers: { "content-type": "application/json" }
|
|
39142
39227
|
});
|
|
39143
39228
|
}
|
|
39144
|
-
function
|
|
39229
|
+
function asGatewayError(error51) {
|
|
39145
39230
|
if (error51 instanceof GatewayError)
|
|
39146
39231
|
return error51;
|
|
39147
39232
|
return new GatewayError("INTERNAL", error51 instanceof Error ? error51.message : "internal error");
|
|
39148
39233
|
}
|
|
39149
|
-
function sseResponse(frames, onDone) {
|
|
39234
|
+
function sseResponse(frames, onDone, keepaliveMs) {
|
|
39150
39235
|
const encoder2 = new TextEncoder;
|
|
39151
39236
|
let done = null;
|
|
39152
39237
|
const runOnce = () => {
|
|
@@ -39154,10 +39239,19 @@ function sseResponse(frames, onDone) {
|
|
|
39154
39239
|
done = onDone();
|
|
39155
39240
|
return done;
|
|
39156
39241
|
};
|
|
39242
|
+
let inflight = null;
|
|
39157
39243
|
const stream2 = new ReadableStream({
|
|
39158
39244
|
async pull(controller) {
|
|
39159
39245
|
try {
|
|
39160
|
-
|
|
39246
|
+
inflight ??= frames.next();
|
|
39247
|
+
const next = await withKeepalive(inflight, keepaliveMs);
|
|
39248
|
+
if (next === KEEPALIVE) {
|
|
39249
|
+
controller.enqueue(encoder2.encode(`: keepalive
|
|
39250
|
+
|
|
39251
|
+
`));
|
|
39252
|
+
return;
|
|
39253
|
+
}
|
|
39254
|
+
inflight = null;
|
|
39161
39255
|
if (next.done === true) {
|
|
39162
39256
|
controller.close();
|
|
39163
39257
|
await runOnce();
|
|
@@ -39182,7 +39276,9 @@ data: ${data}
|
|
|
39182
39276
|
}
|
|
39183
39277
|
async function handle(deps, rateLimiter, surface, request2) {
|
|
39184
39278
|
const requestId = deps.requestId();
|
|
39279
|
+
const startedAt = deps.now();
|
|
39185
39280
|
let keyId = null;
|
|
39281
|
+
let requestedModel = "";
|
|
39186
39282
|
try {
|
|
39187
39283
|
const key = await authenticateApiKey(deps.store, apiKeyHeader(request2.headers));
|
|
39188
39284
|
keyId = key.id;
|
|
@@ -39192,11 +39288,31 @@ async function handle(deps, rateLimiter, surface, request2) {
|
|
|
39192
39288
|
if (key.modelAllowlist !== null && !key.modelAllowlist.includes(chatRequest.model)) {
|
|
39193
39289
|
throw new GatewayError("AUTH", `model "${chatRequest.model}" is not allowed for this API key`);
|
|
39194
39290
|
}
|
|
39195
|
-
|
|
39196
|
-
|
|
39291
|
+
requestedModel = chatRequest.model;
|
|
39292
|
+
let began = false;
|
|
39293
|
+
const outcome = await dispatch(chatRequest, {
|
|
39294
|
+
...deps,
|
|
39295
|
+
async onRoute(target) {
|
|
39296
|
+
if (began) {
|
|
39297
|
+
await routeLog(deps.store, requestId, target);
|
|
39298
|
+
return;
|
|
39299
|
+
}
|
|
39300
|
+
began = true;
|
|
39301
|
+
const log2 = newPendingRequestLog({
|
|
39302
|
+
id: requestId,
|
|
39303
|
+
at: startedAt,
|
|
39304
|
+
requestedModel,
|
|
39305
|
+
resolvedProvider: target.provider,
|
|
39306
|
+
resolvedModel: target.model,
|
|
39307
|
+
credentialId: target.credentialId
|
|
39308
|
+
});
|
|
39309
|
+
await beginLog(deps.store, log2, keyId);
|
|
39310
|
+
}
|
|
39311
|
+
}, request2.signal, requestId);
|
|
39312
|
+
const log = () => finishLog(deps.store, outcome.log(), keyId);
|
|
39197
39313
|
if (chatRequest.stream) {
|
|
39198
39314
|
const frames = surface === "anthropic" ? anthropicStream(outcome.events, requestId) : openaiStream(outcome.events, requestId, Math.floor(deps.now() / 1000));
|
|
39199
|
-
return sseResponse(frames, log);
|
|
39315
|
+
return sseResponse(frames, log, deps.keepaliveMs);
|
|
39200
39316
|
}
|
|
39201
39317
|
const events = [];
|
|
39202
39318
|
for await (const event of outcome.events)
|
|
@@ -39204,7 +39320,7 @@ async function handle(deps, rateLimiter, surface, request2) {
|
|
|
39204
39320
|
await log();
|
|
39205
39321
|
const failure = events.find((e) => e.type === "error");
|
|
39206
39322
|
if (failure !== undefined)
|
|
39207
|
-
return
|
|
39323
|
+
return errorResponse(surface, failure.code, failure.message);
|
|
39208
39324
|
const collected = collect(events);
|
|
39209
39325
|
const responseBody = surface === "anthropic" ? anthropicResponse(collected, requestId) : openaiResponse(collected, requestId, Math.floor(deps.now() / 1000));
|
|
39210
39326
|
return new Response(JSON.stringify(responseBody), {
|
|
@@ -39212,35 +39328,21 @@ async function handle(deps, rateLimiter, surface, request2) {
|
|
|
39212
39328
|
headers: { "content-type": "application/json" }
|
|
39213
39329
|
});
|
|
39214
39330
|
} catch (error51) {
|
|
39215
|
-
const gatewayError =
|
|
39216
|
-
await finishLog(deps.store, {
|
|
39217
|
-
|
|
39218
|
-
at: deps.now(),
|
|
39219
|
-
apiKeyId: keyId,
|
|
39220
|
-
requestedModel: "",
|
|
39221
|
-
resolvedProvider: null,
|
|
39222
|
-
resolvedModel: null,
|
|
39223
|
-
credentialId: null,
|
|
39224
|
-
attempts: 0,
|
|
39331
|
+
const gatewayError = asGatewayError(error51);
|
|
39332
|
+
await finishLog(deps.store, newCompletedRequestLog(requestId, startedAt, {
|
|
39333
|
+
requestedModel,
|
|
39225
39334
|
status: HTTP_STATUS[gatewayError.code],
|
|
39226
|
-
errorCode: gatewayError.code
|
|
39227
|
-
|
|
39228
|
-
|
|
39229
|
-
cacheReadTokens: 0,
|
|
39230
|
-
cacheWriteTokens: 0,
|
|
39231
|
-
ttftMs: null,
|
|
39232
|
-
durationMs: 0,
|
|
39233
|
-
costUsd: 0,
|
|
39234
|
-
degradations: []
|
|
39235
|
-
}, keyId);
|
|
39236
|
-
return errorResponse2(surface, gatewayError.code, gatewayError.message);
|
|
39335
|
+
errorCode: gatewayError.code
|
|
39336
|
+
}), keyId);
|
|
39337
|
+
return errorResponse(surface, gatewayError.code, gatewayError.message);
|
|
39237
39338
|
}
|
|
39238
39339
|
}
|
|
39239
39340
|
function proxyRoutes(deps) {
|
|
39240
39341
|
const rateLimiter = deps.rateLimiter ?? new ApiKeyRateLimiter(deps.now);
|
|
39241
39342
|
const dispatchDeps = {
|
|
39242
39343
|
...deps,
|
|
39243
|
-
snapshots: deps.snapshots ?? createRoutingSnapshotCache(deps.store)
|
|
39344
|
+
snapshots: deps.snapshots ?? createRoutingSnapshotCache(deps.store),
|
|
39345
|
+
keepaliveMs: deps.keepaliveMs ?? KEEPALIVE_MS
|
|
39244
39346
|
};
|
|
39245
39347
|
return new Elysia().post("/v1/messages", ({ request: request2 }) => handle(dispatchDeps, rateLimiter, "anthropic", request2)).post("/v1/chat/completions", ({ request: request2 }) => handle(dispatchDeps, rateLimiter, "openai", request2)).get("/v1/models", async ({ request: request2 }) => {
|
|
39246
39348
|
try {
|
|
@@ -39257,12 +39359,12 @@ function proxyRoutes(deps) {
|
|
|
39257
39359
|
}))
|
|
39258
39360
|
});
|
|
39259
39361
|
} catch (error51) {
|
|
39260
|
-
const gatewayError =
|
|
39261
|
-
return
|
|
39362
|
+
const gatewayError = asGatewayError(error51);
|
|
39363
|
+
return errorResponse("anthropic", gatewayError.code, gatewayError.message);
|
|
39262
39364
|
}
|
|
39263
39365
|
}).onError(({ error: error51 }) => {
|
|
39264
|
-
const gatewayError =
|
|
39265
|
-
return
|
|
39366
|
+
const gatewayError = asGatewayError(error51);
|
|
39367
|
+
return errorResponse("anthropic", gatewayError.code, gatewayError.message);
|
|
39266
39368
|
});
|
|
39267
39369
|
}
|
|
39268
39370
|
|
|
@@ -39469,6 +39571,9 @@ var store = await createStore({
|
|
|
39469
39571
|
path: config2.databasePath,
|
|
39470
39572
|
encryptionKey
|
|
39471
39573
|
});
|
|
39574
|
+
var swept = await store.usage.sweepPending();
|
|
39575
|
+
if (swept > 0)
|
|
39576
|
+
console.log(`retired ${swept} request(s) interrupted by the last shutdown`);
|
|
39472
39577
|
var now = () => Date.now();
|
|
39473
39578
|
var http = nodeHttpClient();
|
|
39474
39579
|
var refresh = createRefresher({ store, providers: OAUTH_PROVIDERS, http, now });
|
|
@@ -39489,7 +39594,7 @@ var stopQuotaPoller = await startQuotaPoller({
|
|
|
39489
39594
|
refresh,
|
|
39490
39595
|
now
|
|
39491
39596
|
});
|
|
39492
|
-
app.listen({ port: config2.port, hostname: config2.host });
|
|
39597
|
+
app.listen({ port: config2.port, hostname: config2.host, idleTimeout: 255 });
|
|
39493
39598
|
console.log(`omnigateway listening on http://${config2.host}:${config2.port}`);
|
|
39494
39599
|
var shuttingDown = false;
|
|
39495
39600
|
function exitAfterClosingStore(code) {
|