omnigateway 0.1.3 → 0.1.4
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 +212 -69
- package/package.json +1 -1
- package/public/assets/{Chip-D0YiqYkz.js → Chip-Ccqsb_Mq.js} +2 -2
- package/public/assets/{CopyValue-bzKz2tiJ.js → CopyValue-BhGx7Gy0.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-DOQGdhoM.js} +8 -8
- package/public/assets/{Rack-uDxrjvaz.js → Rack-DnbGnBvz.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-CP3RvAAB.js} +3 -3
- package/public/assets/_app-D83powx4.js +1 -0
- package/public/assets/_app.accounts-DR-2t9-7.js +51 -0
- package/public/assets/_app.index-DimCB0Bo.js +61 -0
- package/public/assets/{_app.keys-BNEOjtbu.js → _app.keys-B7kl3CSe.js} +8 -8
- package/public/assets/_app.logs-BjtdzcDY.js +18 -0
- package/public/assets/_app.models-BEECjcLM.js +144 -0
- package/public/assets/{_app.settings-C1gB4w7c.js → _app.settings-DtDIhAAr.js} +6 -6
- package/public/assets/{_app.usage-B0iHKzmI.js → _app.usage-BChnSBbS.js} +28 -28
- package/public/assets/index-C53PiRfb.js +170 -0
- package/public/assets/{login-D-FsWNX8.js → login-BuvJY1Qd.js} +8 -8
- package/public/assets/{queries-vY5lJqBe.js → queries-BMwc6EvM.js} +1 -1
- package/public/assets/{trash-2-Cep_aVLx.js → trash-2-Cq7ldH9D.js} +1 -1
- package/public/index.html +2 -2
- 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/bin/omni.js
CHANGED
|
@@ -17335,6 +17335,27 @@ ALTER TABLE credentials ADD COLUMN disabled_reason TEXT;
|
|
|
17335
17335
|
ALTER TABLE credentials ADD COLUMN disabled_at INTEGER;
|
|
17336
17336
|
`;
|
|
17337
17337
|
|
|
17338
|
+
// packages/store/src/sqlite/migrations/004_request_state.sql
|
|
17339
|
+
var _004_request_state_default = `-- Whether a request is still running.
|
|
17340
|
+
--
|
|
17341
|
+
-- A row used to be written once, after the response stream drained, so a long
|
|
17342
|
+
-- stream was invisible for its whole life and the console showed an idle
|
|
17343
|
+
-- gateway. A row is now written twice: \`pending\` when dispatch starts, \`done\`
|
|
17344
|
+
-- when it finishes.
|
|
17345
|
+
--
|
|
17346
|
+
-- Existing rows default to 'done'. Every row that predates this migration
|
|
17347
|
+
-- describes a request that has already ended, by definition.
|
|
17348
|
+
ALTER TABLE request_logs ADD COLUMN state TEXT NOT NULL DEFAULT 'done';
|
|
17349
|
+
|
|
17350
|
+
-- \`status\` and \`duration_ms\` stay NOT NULL: making them nullable means a table
|
|
17351
|
+
-- rebuild in SQLite, and a pending row's zeros are never read. Readers key off
|
|
17352
|
+
-- \`state\` alone.
|
|
17353
|
+
--
|
|
17354
|
+
-- Partial, because pending rows are a handful at any moment while the table
|
|
17355
|
+
-- holds a month of finished ones.
|
|
17356
|
+
CREATE INDEX idx_request_logs_pending ON request_logs(state) WHERE state = 'pending';
|
|
17357
|
+
`;
|
|
17358
|
+
|
|
17338
17359
|
// packages/store/src/sqlite/rollup.ts
|
|
17339
17360
|
function startOfLocalDay(at) {
|
|
17340
17361
|
const day = new Date(at);
|
|
@@ -17441,7 +17462,8 @@ function backfillDaily(db) {
|
|
|
17441
17462
|
var MIGRATIONS = [
|
|
17442
17463
|
{ id: 1, sql: _001_init_default },
|
|
17443
17464
|
{ id: 2, sql: _002_usage_daily_default, after: backfillDaily },
|
|
17444
|
-
{ id: 3, sql: _003_quota_snapshot_default }
|
|
17465
|
+
{ id: 3, sql: _003_quota_snapshot_default },
|
|
17466
|
+
{ id: 4, sql: _004_request_state_default }
|
|
17445
17467
|
];
|
|
17446
17468
|
function openDb(path) {
|
|
17447
17469
|
const db = new Database(path, { create: true });
|
|
@@ -17513,6 +17535,7 @@ function createKeyRepo(db) {
|
|
|
17513
17535
|
// packages/store/src/sqlite/usage.ts
|
|
17514
17536
|
var toLog = (r) => ({
|
|
17515
17537
|
id: r.id,
|
|
17538
|
+
state: r.state === "pending" ? "pending" : "done",
|
|
17516
17539
|
at: r.at,
|
|
17517
17540
|
apiKeyId: r.api_key_id,
|
|
17518
17541
|
requestedModel: r.requested_model,
|
|
@@ -17571,37 +17594,78 @@ function label(value) {
|
|
|
17571
17594
|
const text = String(value);
|
|
17572
17595
|
return text.length === 0 ? "unknown" : text;
|
|
17573
17596
|
}
|
|
17597
|
+
var COLUMNS = `(id, state, at, api_key_id, requested_model, resolved_provider, resolved_model,
|
|
17598
|
+
credential_id, attempts, status, error_code, input_tokens, output_tokens,
|
|
17599
|
+
cache_read_tokens, cache_write_tokens, ttft_ms, duration_ms, cost_usd,
|
|
17600
|
+
degradations)`;
|
|
17601
|
+
var PLACEHOLDERS = "(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)";
|
|
17602
|
+
function values(log, state) {
|
|
17603
|
+
return [
|
|
17604
|
+
log.id,
|
|
17605
|
+
state,
|
|
17606
|
+
log.at,
|
|
17607
|
+
log.apiKeyId,
|
|
17608
|
+
log.requestedModel,
|
|
17609
|
+
log.resolvedProvider,
|
|
17610
|
+
log.resolvedModel,
|
|
17611
|
+
log.credentialId,
|
|
17612
|
+
log.attempts,
|
|
17613
|
+
log.status,
|
|
17614
|
+
log.errorCode,
|
|
17615
|
+
log.inputTokens,
|
|
17616
|
+
log.outputTokens,
|
|
17617
|
+
log.cacheReadTokens,
|
|
17618
|
+
log.cacheWriteTokens,
|
|
17619
|
+
log.ttftMs,
|
|
17620
|
+
log.durationMs,
|
|
17621
|
+
log.costUsd,
|
|
17622
|
+
JSON.stringify(log.degradations)
|
|
17623
|
+
];
|
|
17624
|
+
}
|
|
17625
|
+
var COMPLETE = `INSERT INTO request_logs ${COLUMNS} VALUES ${PLACEHOLDERS}
|
|
17626
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
17627
|
+
state = 'done',
|
|
17628
|
+
requested_model = COALESCE(NULLIF(excluded.requested_model, ''), request_logs.requested_model),
|
|
17629
|
+
api_key_id = COALESCE(excluded.api_key_id, request_logs.api_key_id),
|
|
17630
|
+
resolved_provider = excluded.resolved_provider,
|
|
17631
|
+
resolved_model = excluded.resolved_model,
|
|
17632
|
+
credential_id = excluded.credential_id,
|
|
17633
|
+
attempts = excluded.attempts,
|
|
17634
|
+
status = excluded.status,
|
|
17635
|
+
error_code = excluded.error_code,
|
|
17636
|
+
input_tokens = excluded.input_tokens,
|
|
17637
|
+
output_tokens = excluded.output_tokens,
|
|
17638
|
+
cache_read_tokens = excluded.cache_read_tokens,
|
|
17639
|
+
cache_write_tokens = excluded.cache_write_tokens,
|
|
17640
|
+
ttft_ms = excluded.ttft_ms,
|
|
17641
|
+
duration_ms = excluded.duration_ms,
|
|
17642
|
+
cost_usd = excluded.cost_usd,
|
|
17643
|
+
degradations = excluded.degradations`;
|
|
17574
17644
|
function createUsageRepo(db) {
|
|
17575
|
-
const
|
|
17576
|
-
db.run(
|
|
17577
|
-
|
|
17578
|
-
|
|
17579
|
-
|
|
17580
|
-
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)`, [
|
|
17581
|
-
log.id,
|
|
17582
|
-
log.at,
|
|
17583
|
-
log.apiKeyId,
|
|
17584
|
-
log.requestedModel,
|
|
17585
|
-
log.resolvedProvider,
|
|
17586
|
-
log.resolvedModel,
|
|
17587
|
-
log.credentialId,
|
|
17588
|
-
log.attempts,
|
|
17589
|
-
log.status,
|
|
17590
|
-
log.errorCode,
|
|
17591
|
-
log.inputTokens,
|
|
17592
|
-
log.outputTokens,
|
|
17593
|
-
log.cacheReadTokens,
|
|
17594
|
-
log.cacheWriteTokens,
|
|
17595
|
-
log.ttftMs,
|
|
17596
|
-
log.durationMs,
|
|
17597
|
-
log.costUsd,
|
|
17598
|
-
JSON.stringify(log.degradations)
|
|
17599
|
-
]);
|
|
17600
|
-
rollupLog(db, log);
|
|
17645
|
+
const complete = db.transaction((log) => {
|
|
17646
|
+
db.run(COMPLETE, values(log, "done"));
|
|
17647
|
+
const stored = db.query("SELECT * FROM request_logs WHERE id = ?").get(log.id);
|
|
17648
|
+
if (stored !== null)
|
|
17649
|
+
rollupLog(db, toLog(stored));
|
|
17601
17650
|
});
|
|
17602
17651
|
return {
|
|
17652
|
+
async begin(log) {
|
|
17653
|
+
db.run(`INSERT INTO request_logs ${COLUMNS} VALUES ${PLACEHOLDERS}`, values(log, "pending"));
|
|
17654
|
+
},
|
|
17655
|
+
async route(id, target) {
|
|
17656
|
+
db.run(`UPDATE request_logs
|
|
17657
|
+
SET resolved_provider = ?, resolved_model = ?, credential_id = ?
|
|
17658
|
+
WHERE id = ? AND state = 'pending'`, [target.provider, target.model, target.credentialId, id]);
|
|
17659
|
+
},
|
|
17603
17660
|
async append(log) {
|
|
17604
|
-
|
|
17661
|
+
complete(log);
|
|
17662
|
+
},
|
|
17663
|
+
async sweepPending() {
|
|
17664
|
+
const stale = db.query("SELECT * FROM request_logs WHERE state = 'pending'").all().map(toLog);
|
|
17665
|
+
for (const log of stale) {
|
|
17666
|
+
complete({ ...log, status: 499, errorCode: "interrupted", durationMs: 0 });
|
|
17667
|
+
}
|
|
17668
|
+
return stale.length;
|
|
17605
17669
|
},
|
|
17606
17670
|
async recent(limit) {
|
|
17607
17671
|
return db.query("SELECT * FROM request_logs ORDER BY at DESC LIMIT ?").all(limit).map(toLog);
|
|
@@ -17623,7 +17687,7 @@ function createUsageRepo(db) {
|
|
|
17623
17687
|
COALESCE(SUM(cache_write_tokens), 0) AS cache_write_tokens,
|
|
17624
17688
|
COALESCE(SUM(cost_usd), 0) AS cost_usd
|
|
17625
17689
|
FROM ${daily ? "usage_daily" : "request_logs"}
|
|
17626
|
-
WHERE ${timeColumn} >= ? AND ${timeColumn} <= ?
|
|
17690
|
+
WHERE ${daily ? "" : "state = 'done' AND "}${timeColumn} >= ? AND ${timeColumn} <= ?
|
|
17627
17691
|
GROUP BY key${split === null ? "" : ", split"}
|
|
17628
17692
|
ORDER BY requests DESC`).all(since, until);
|
|
17629
17693
|
return rows.map((r) => {
|
|
@@ -18102,21 +18166,21 @@ function parse5(argv, options = {}) {
|
|
|
18102
18166
|
throw new UsageError(error51 instanceof Error ? error51.message : "could not parse arguments");
|
|
18103
18167
|
}
|
|
18104
18168
|
}
|
|
18105
|
-
function stringFlag(
|
|
18106
|
-
const value =
|
|
18169
|
+
function stringFlag(values2, name) {
|
|
18170
|
+
const value = values2[name];
|
|
18107
18171
|
return typeof value === "string" ? value : undefined;
|
|
18108
18172
|
}
|
|
18109
|
-
function boolFlag(
|
|
18110
|
-
return
|
|
18173
|
+
function boolFlag(values2, name) {
|
|
18174
|
+
return values2[name] === true;
|
|
18111
18175
|
}
|
|
18112
|
-
function listFlag(
|
|
18113
|
-
const value =
|
|
18176
|
+
function listFlag(values2, name) {
|
|
18177
|
+
const value = values2[name];
|
|
18114
18178
|
if (Array.isArray(value))
|
|
18115
18179
|
return value.filter((entry) => typeof entry === "string");
|
|
18116
18180
|
return typeof value === "string" ? [value] : undefined;
|
|
18117
18181
|
}
|
|
18118
|
-
function numberFlag(
|
|
18119
|
-
const raw = stringFlag(
|
|
18182
|
+
function numberFlag(values2, name) {
|
|
18183
|
+
const raw = stringFlag(values2, name);
|
|
18120
18184
|
if (raw === undefined)
|
|
18121
18185
|
return;
|
|
18122
18186
|
const value = Number(raw);
|
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;
|
|
@@ -38228,6 +38292,7 @@ async function dispatch(request2, deps, signal) {
|
|
|
38228
38292
|
const deadlineAt = startedAt + snapshot.settings.requestDeadlineMs;
|
|
38229
38293
|
const log = {
|
|
38230
38294
|
id: crypto.randomUUID(),
|
|
38295
|
+
state: "done",
|
|
38231
38296
|
at: startedAt,
|
|
38232
38297
|
apiKeyId: null,
|
|
38233
38298
|
requestedModel: request2.model,
|
|
@@ -38319,6 +38384,11 @@ async function dispatch(request2, deps, signal) {
|
|
|
38319
38384
|
log.credentialId = candidate.credential.id;
|
|
38320
38385
|
log.resolvedProvider = candidate.target.provider;
|
|
38321
38386
|
log.resolvedModel = candidate.target.model;
|
|
38387
|
+
await deps.onRoute?.({
|
|
38388
|
+
provider: candidate.target.provider,
|
|
38389
|
+
model: candidate.target.model,
|
|
38390
|
+
credentialId: candidate.credential.id
|
|
38391
|
+
});
|
|
38322
38392
|
log.inputTokens = 0;
|
|
38323
38393
|
log.outputTokens = 0;
|
|
38324
38394
|
log.cacheReadTokens = 0;
|
|
@@ -39116,18 +39186,44 @@ function parseOpenAIRequest(body2) {
|
|
|
39116
39186
|
}
|
|
39117
39187
|
|
|
39118
39188
|
// apps/gateway/src/logging.ts
|
|
39189
|
+
function report(what, requestId, error51) {
|
|
39190
|
+
console.error(what, {
|
|
39191
|
+
requestId,
|
|
39192
|
+
reason: error51 instanceof Error ? error51.message : "unknown"
|
|
39193
|
+
});
|
|
39194
|
+
}
|
|
39195
|
+
async function beginLog(store, log, keyId) {
|
|
39196
|
+
try {
|
|
39197
|
+
await store.usage.begin({ ...log, state: "pending", apiKeyId: keyId });
|
|
39198
|
+
} catch (error51) {
|
|
39199
|
+
report("failed to record request start", log.id, error51);
|
|
39200
|
+
}
|
|
39201
|
+
}
|
|
39202
|
+
async function routeLog(store, requestId, target) {
|
|
39203
|
+
try {
|
|
39204
|
+
await store.usage.route(requestId, target);
|
|
39205
|
+
} catch (error51) {
|
|
39206
|
+
report("failed to record request route", requestId, error51);
|
|
39207
|
+
}
|
|
39208
|
+
}
|
|
39119
39209
|
async function finishLog(store, log, keyId) {
|
|
39120
39210
|
try {
|
|
39121
39211
|
await store.usage.append({ ...log, apiKeyId: keyId });
|
|
39122
39212
|
} catch (error51) {
|
|
39123
|
-
|
|
39124
|
-
requestId: log.id,
|
|
39125
|
-
reason: error51 instanceof Error ? error51.message : "unknown"
|
|
39126
|
-
});
|
|
39213
|
+
report("failed to persist request log", log.id, error51);
|
|
39127
39214
|
}
|
|
39128
39215
|
}
|
|
39129
39216
|
|
|
39130
39217
|
// apps/gateway/src/routes/proxy.ts
|
|
39218
|
+
var KEEPALIVE_MS = 1e4;
|
|
39219
|
+
var KEEPALIVE = Symbol("keepalive");
|
|
39220
|
+
function withKeepalive(pending, ms) {
|
|
39221
|
+
let timer;
|
|
39222
|
+
const tick = new Promise((resolve) => {
|
|
39223
|
+
timer = setTimeout(() => resolve(KEEPALIVE), ms);
|
|
39224
|
+
});
|
|
39225
|
+
return Promise.race([pending, tick]).finally(() => clearTimeout(timer));
|
|
39226
|
+
}
|
|
39131
39227
|
var SSE_HEADERS = {
|
|
39132
39228
|
"content-type": "text/event-stream; charset=utf-8",
|
|
39133
39229
|
"cache-control": "no-cache, no-transform",
|
|
@@ -39146,7 +39242,7 @@ function asGatewayError2(error51) {
|
|
|
39146
39242
|
return error51;
|
|
39147
39243
|
return new GatewayError("INTERNAL", error51 instanceof Error ? error51.message : "internal error");
|
|
39148
39244
|
}
|
|
39149
|
-
function sseResponse(frames, onDone) {
|
|
39245
|
+
function sseResponse(frames, onDone, keepaliveMs) {
|
|
39150
39246
|
const encoder2 = new TextEncoder;
|
|
39151
39247
|
let done = null;
|
|
39152
39248
|
const runOnce = () => {
|
|
@@ -39154,10 +39250,19 @@ function sseResponse(frames, onDone) {
|
|
|
39154
39250
|
done = onDone();
|
|
39155
39251
|
return done;
|
|
39156
39252
|
};
|
|
39253
|
+
let inflight = null;
|
|
39157
39254
|
const stream2 = new ReadableStream({
|
|
39158
39255
|
async pull(controller) {
|
|
39159
39256
|
try {
|
|
39160
|
-
|
|
39257
|
+
inflight ??= frames.next();
|
|
39258
|
+
const next = await withKeepalive(inflight, keepaliveMs);
|
|
39259
|
+
if (next === KEEPALIVE) {
|
|
39260
|
+
controller.enqueue(encoder2.encode(`: keepalive
|
|
39261
|
+
|
|
39262
|
+
`));
|
|
39263
|
+
return;
|
|
39264
|
+
}
|
|
39265
|
+
inflight = null;
|
|
39161
39266
|
if (next.done === true) {
|
|
39162
39267
|
controller.close();
|
|
39163
39268
|
await runOnce();
|
|
@@ -39182,7 +39287,9 @@ data: ${data}
|
|
|
39182
39287
|
}
|
|
39183
39288
|
async function handle(deps, rateLimiter, surface, request2) {
|
|
39184
39289
|
const requestId = deps.requestId();
|
|
39290
|
+
const startedAt = deps.now();
|
|
39185
39291
|
let keyId = null;
|
|
39292
|
+
let requestedModel = "";
|
|
39186
39293
|
try {
|
|
39187
39294
|
const key = await authenticateApiKey(deps.store, apiKeyHeader(request2.headers));
|
|
39188
39295
|
keyId = key.id;
|
|
@@ -39192,11 +39299,42 @@ async function handle(deps, rateLimiter, surface, request2) {
|
|
|
39192
39299
|
if (key.modelAllowlist !== null && !key.modelAllowlist.includes(chatRequest.model)) {
|
|
39193
39300
|
throw new GatewayError("AUTH", `model "${chatRequest.model}" is not allowed for this API key`);
|
|
39194
39301
|
}
|
|
39195
|
-
|
|
39302
|
+
requestedModel = chatRequest.model;
|
|
39303
|
+
let began = false;
|
|
39304
|
+
const outcome = await dispatch(chatRequest, {
|
|
39305
|
+
...deps,
|
|
39306
|
+
async onRoute(target) {
|
|
39307
|
+
if (began) {
|
|
39308
|
+
await routeLog(deps.store, requestId, target);
|
|
39309
|
+
return;
|
|
39310
|
+
}
|
|
39311
|
+
began = true;
|
|
39312
|
+
await beginLog(deps.store, {
|
|
39313
|
+
id: requestId,
|
|
39314
|
+
at: startedAt,
|
|
39315
|
+
apiKeyId: keyId,
|
|
39316
|
+
requestedModel,
|
|
39317
|
+
resolvedProvider: target.provider,
|
|
39318
|
+
resolvedModel: target.model,
|
|
39319
|
+
credentialId: target.credentialId,
|
|
39320
|
+
attempts: 0,
|
|
39321
|
+
status: 0,
|
|
39322
|
+
errorCode: null,
|
|
39323
|
+
inputTokens: 0,
|
|
39324
|
+
outputTokens: 0,
|
|
39325
|
+
cacheReadTokens: 0,
|
|
39326
|
+
cacheWriteTokens: 0,
|
|
39327
|
+
ttftMs: null,
|
|
39328
|
+
durationMs: 0,
|
|
39329
|
+
costUsd: 0,
|
|
39330
|
+
degradations: []
|
|
39331
|
+
}, keyId);
|
|
39332
|
+
}
|
|
39333
|
+
}, request2.signal);
|
|
39196
39334
|
const log = () => finishLog(deps.store, { ...outcome.log(), id: requestId }, keyId);
|
|
39197
39335
|
if (chatRequest.stream) {
|
|
39198
39336
|
const frames = surface === "anthropic" ? anthropicStream(outcome.events, requestId) : openaiStream(outcome.events, requestId, Math.floor(deps.now() / 1000));
|
|
39199
|
-
return sseResponse(frames, log);
|
|
39337
|
+
return sseResponse(frames, log, deps.keepaliveMs);
|
|
39200
39338
|
}
|
|
39201
39339
|
const events = [];
|
|
39202
39340
|
for await (const event of outcome.events)
|
|
@@ -39215,9 +39353,10 @@ async function handle(deps, rateLimiter, surface, request2) {
|
|
|
39215
39353
|
const gatewayError = asGatewayError2(error51);
|
|
39216
39354
|
await finishLog(deps.store, {
|
|
39217
39355
|
id: requestId,
|
|
39218
|
-
|
|
39356
|
+
state: "done",
|
|
39357
|
+
at: startedAt,
|
|
39219
39358
|
apiKeyId: keyId,
|
|
39220
|
-
requestedModel
|
|
39359
|
+
requestedModel,
|
|
39221
39360
|
resolvedProvider: null,
|
|
39222
39361
|
resolvedModel: null,
|
|
39223
39362
|
credentialId: null,
|
|
@@ -39240,7 +39379,8 @@ function proxyRoutes(deps) {
|
|
|
39240
39379
|
const rateLimiter = deps.rateLimiter ?? new ApiKeyRateLimiter(deps.now);
|
|
39241
39380
|
const dispatchDeps = {
|
|
39242
39381
|
...deps,
|
|
39243
|
-
snapshots: deps.snapshots ?? createRoutingSnapshotCache(deps.store)
|
|
39382
|
+
snapshots: deps.snapshots ?? createRoutingSnapshotCache(deps.store),
|
|
39383
|
+
keepaliveMs: deps.keepaliveMs ?? KEEPALIVE_MS
|
|
39244
39384
|
};
|
|
39245
39385
|
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
39386
|
try {
|
|
@@ -39469,6 +39609,9 @@ var store = await createStore({
|
|
|
39469
39609
|
path: config2.databasePath,
|
|
39470
39610
|
encryptionKey
|
|
39471
39611
|
});
|
|
39612
|
+
var swept = await store.usage.sweepPending();
|
|
39613
|
+
if (swept > 0)
|
|
39614
|
+
console.log(`retired ${swept} request(s) interrupted by the last shutdown`);
|
|
39472
39615
|
var now = () => Date.now();
|
|
39473
39616
|
var http = nodeHttpClient();
|
|
39474
39617
|
var refresh = createRefresher({ store, providers: OAUTH_PROVIDERS, http, now });
|
|
@@ -39489,7 +39632,7 @@ var stopQuotaPoller = await startQuotaPoller({
|
|
|
39489
39632
|
refresh,
|
|
39490
39633
|
now
|
|
39491
39634
|
});
|
|
39492
|
-
app.listen({ port: config2.port, hostname: config2.host });
|
|
39635
|
+
app.listen({ port: config2.port, hostname: config2.host, idleTimeout: 255 });
|
|
39493
39636
|
console.log(`omnigateway listening on http://${config2.host}:${config2.port}`);
|
|
39494
39637
|
var shuttingDown = false;
|
|
39495
39638
|
function exitAfterClosingStore(code) {
|
package/package.json
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{Ot as e,V as t}from"./queries-BMwc6EvM.js";var n=e(),r={neutral:{fg:`var(--ink-dim)`,bg:`transparent`},ok:{fg:`var(--ok)`,bg:`var(--ok-wash)`},warn:{fg:`var(--warn)`,bg:`var(--warn-wash)`},down:{fg:`var(--down)`,bg:`var(--down-wash)`},accent:{fg:`var(--accent)`,bg:`var(--accent-wash)`}},i=t.span`
|
|
2
2
|
display: inline-flex;
|
|
3
3
|
align-items: center;
|
|
4
4
|
gap: 4px;
|
|
@@ -12,7 +12,7 @@ import{B as e,Dt as t}from"./queries-vY5lJqBe.js";var n=t(),r={neutral:{fg:`var(
|
|
|
12
12
|
font-size: 10.5px;
|
|
13
13
|
letter-spacing: 0.02em;
|
|
14
14
|
white-space: nowrap;
|
|
15
|
-
`,a=
|
|
15
|
+
`,a=t.span`
|
|
16
16
|
display: inline-flex;
|
|
17
17
|
align-items: center;
|
|
18
18
|
gap: 5px;
|