omnigateway 0.1.2 → 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.
Files changed (34) hide show
  1. package/bin/omni.js +140 -51
  2. package/gateway.js +289 -87
  3. package/package.json +1 -1
  4. package/public/assets/{Chip-D0YiqYkz.js → Chip-Ccqsb_Mq.js} +2 -2
  5. package/public/assets/{CopyValue-bzKz2tiJ.js → CopyValue-BhGx7Gy0.js} +5 -5
  6. package/public/assets/{Field-DdscQ9OO.js → Field-Devm4K46.js} +8 -8
  7. package/public/assets/Lamp-BXSUIAtG.js +25 -0
  8. package/public/assets/{Meter-Bm1oQE2U.js → Meter-Fn8X7t60.js} +2 -2
  9. package/public/assets/{Modal-Crr8uyWt.js → Modal-DOQGdhoM.js} +8 -8
  10. package/public/assets/{Rack-uDxrjvaz.js → Rack-DnbGnBvz.js} +18 -18
  11. package/public/assets/{Readout-CYg6UOop.js → Readout-BKpOZIGX.js} +5 -5
  12. package/public/assets/{States-DPTTYUHL.js → States-DBzsiHjQ.js} +10 -10
  13. package/public/assets/{Table-LkPaHBsj.js → Table-DPDJeeVc.js} +1 -1
  14. package/public/assets/{Toggle-D0e4C94X.js → Toggle-CP3RvAAB.js} +3 -3
  15. package/public/assets/_app-D83powx4.js +1 -0
  16. package/public/assets/_app.accounts-DR-2t9-7.js +51 -0
  17. package/public/assets/_app.index-DimCB0Bo.js +61 -0
  18. package/public/assets/{_app.keys-BNEOjtbu.js → _app.keys-B7kl3CSe.js} +8 -8
  19. package/public/assets/_app.logs-BjtdzcDY.js +18 -0
  20. package/public/assets/_app.models-BEECjcLM.js +144 -0
  21. package/public/assets/{_app.settings-C1gB4w7c.js → _app.settings-DtDIhAAr.js} +6 -6
  22. package/public/assets/{_app.usage-B0iHKzmI.js → _app.usage-BChnSBbS.js} +28 -28
  23. package/public/assets/index-C53PiRfb.js +170 -0
  24. package/public/assets/{login-D-FsWNX8.js → login-BuvJY1Qd.js} +8 -8
  25. package/public/assets/{queries-vY5lJqBe.js → queries-BMwc6EvM.js} +1 -1
  26. package/public/assets/{trash-2-Cep_aVLx.js → trash-2-Cq7ldH9D.js} +1 -1
  27. package/public/index.html +2 -2
  28. package/public/assets/Lamp-WOotyOTd.js +0 -15
  29. package/public/assets/_app-OSvB-Dv9.js +0 -1
  30. package/public/assets/_app.accounts-BOE49WyS.js +0 -51
  31. package/public/assets/_app.index-CTRlqxOq.js +0 -61
  32. package/public/assets/_app.logs-BxxqeEhM.js +0 -18
  33. package/public/assets/_app.models-BJi-RGUO.js +0 -144
  34. package/public/assets/index--dA3FFSN.js +0 -170
package/bin/omni.js CHANGED
@@ -608,6 +608,7 @@ async function* decodeAnthropic(messages) {
608
608
  let cacheWriteTokens = 0;
609
609
  let outputTokens = 0;
610
610
  let stopReason = "endTurn";
611
+ let terminal = false;
611
612
  for await (const msg of messages) {
612
613
  const d = json(msg.data);
613
614
  if (d === null)
@@ -673,6 +674,7 @@ async function* decodeAnthropic(messages) {
673
674
  break;
674
675
  }
675
676
  case "message_stop":
677
+ terminal = true;
676
678
  yield {
677
679
  type: "end",
678
680
  stopReason,
@@ -680,6 +682,7 @@ async function* decodeAnthropic(messages) {
680
682
  };
681
683
  break;
682
684
  case "error": {
685
+ terminal = true;
683
686
  const code = ERROR_TYPE[String(d.error?.type)] ?? "UPSTREAM";
684
687
  yield {
685
688
  type: "error",
@@ -693,6 +696,14 @@ async function* decodeAnthropic(messages) {
693
696
  break;
694
697
  }
695
698
  }
699
+ if (!terminal) {
700
+ yield {
701
+ type: "error",
702
+ code: "UPSTREAM",
703
+ message: "upstream stream ended before message_stop",
704
+ retryable: RETRYABLE.UPSTREAM
705
+ };
706
+ }
696
707
  }
697
708
 
698
709
  // packages/providers/src/anthropic/wire.ts
@@ -805,9 +816,10 @@ var anthropicAdapter = {
805
816
  ["anthropic-version", API_VERSION],
806
817
  ["Accept", req.request.stream ? "text/event-stream" : "application/json"]
807
818
  ];
819
+ const betas = new Set(req.request.betas ?? []);
808
820
  if (oauth) {
809
821
  protocol.push(["Authorization", `Bearer ${req.credentials.accessToken}`]);
810
- protocol.push(["anthropic-beta", OAUTH_BETA]);
822
+ betas.add(OAUTH_BETA);
811
823
  } else if (req.credentials.apiKey !== null) {
812
824
  protocol.push(["x-api-key", req.credentials.apiKey]);
813
825
  } else {
@@ -815,6 +827,8 @@ var anthropicAdapter = {
815
827
  provider: "anthropic"
816
828
  });
817
829
  }
830
+ if (betas.size > 0)
831
+ protocol.push(["anthropic-beta", [...betas].join(",")]);
818
832
  const profile = PROFILES.anthropic;
819
833
  const headers = orderHeaders(mergeHeaders(profile.headers, protocol), profile.order);
820
834
  const bodyString = signAnthropicBody(JSON.stringify(orderFields(withSystem, BODY_ORDER.anthropic)));
@@ -1032,18 +1046,16 @@ async function* decodeChat(messages) {
1032
1046
  let started = false;
1033
1047
  let textOpen = false;
1034
1048
  let textIndex;
1035
- let ended = false;
1049
+ let done = false;
1036
1050
  let stopReason = "endTurn";
1037
1051
  let usage = { inputTokens: 0, outputTokens: 0, cacheReadTokens: 0, cacheWriteTokens: 0 };
1038
1052
  const toolIndex = new Map;
1039
1053
  let nextIndex = 0;
1040
- const emitEnd = () => {
1041
- ended = true;
1042
- return { type: "end", stopReason, usage };
1043
- };
1044
1054
  for await (const msg of messages) {
1045
- if (msg.data === "[DONE]")
1055
+ if (msg.data === "[DONE]") {
1056
+ done = true;
1046
1057
  break;
1058
+ }
1047
1059
  const d = json2(msg.data);
1048
1060
  if (d === null)
1049
1061
  continue;
@@ -1098,19 +1110,21 @@ async function* decodeChat(messages) {
1098
1110
  }
1099
1111
  if (typeof choice.finish_reason === "string") {
1100
1112
  stopReason = FINISH[choice.finish_reason] ?? "endTurn";
1101
- if (textOpen)
1102
- yield { type: "blockEnd", index: textIndex ?? 0 };
1103
- for (const index of toolIndex.values())
1104
- yield { type: "blockEnd", index };
1105
- yield emitEnd();
1106
1113
  }
1107
1114
  }
1108
- if (!ended) {
1115
+ if (done) {
1109
1116
  if (textOpen)
1110
1117
  yield { type: "blockEnd", index: textIndex ?? 0 };
1111
1118
  for (const index of toolIndex.values())
1112
1119
  yield { type: "blockEnd", index };
1113
- yield emitEnd();
1120
+ yield { type: "end", stopReason, usage };
1121
+ } else {
1122
+ yield {
1123
+ type: "error",
1124
+ code: "UPSTREAM",
1125
+ message: "upstream stream ended before [DONE]",
1126
+ retryable: RETRYABLE.UPSTREAM
1127
+ };
1114
1128
  }
1115
1129
  }
1116
1130
 
@@ -1265,6 +1279,7 @@ async function* decodeResponses(messages) {
1265
1279
  return assigned;
1266
1280
  };
1267
1281
  let sawToolCall = false;
1282
+ let terminal = false;
1268
1283
  const ownsBlock = new Set;
1269
1284
  for await (const msg of messages) {
1270
1285
  const d = json3(msg.data);
@@ -1340,6 +1355,7 @@ async function* decodeResponses(messages) {
1340
1355
  }
1341
1356
  case "response.completed":
1342
1357
  case "response.incomplete": {
1358
+ terminal = true;
1343
1359
  const r = d.response ?? {};
1344
1360
  const reason = r.incomplete_details?.reason;
1345
1361
  let stopReason = sawToolCall ? "toolUse" : "endTurn";
@@ -1361,6 +1377,7 @@ async function* decodeResponses(messages) {
1361
1377
  }
1362
1378
  case "response.failed":
1363
1379
  case "error": {
1380
+ terminal = true;
1364
1381
  const err = d.response?.error ?? d.error ?? {};
1365
1382
  const code = ERROR_CODE[String(err.code ?? err.type)] ?? "UPSTREAM";
1366
1383
  yield {
@@ -1375,6 +1392,14 @@ async function* decodeResponses(messages) {
1375
1392
  break;
1376
1393
  }
1377
1394
  }
1395
+ if (!terminal) {
1396
+ yield {
1397
+ type: "error",
1398
+ code: "UPSTREAM",
1399
+ message: "upstream stream ended before response completion",
1400
+ retryable: RETRYABLE.UPSTREAM
1401
+ };
1402
+ }
1378
1403
  }
1379
1404
 
1380
1405
  // packages/providers/src/openai/wire.ts
@@ -17310,6 +17335,27 @@ ALTER TABLE credentials ADD COLUMN disabled_reason TEXT;
17310
17335
  ALTER TABLE credentials ADD COLUMN disabled_at INTEGER;
17311
17336
  `;
17312
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
+
17313
17359
  // packages/store/src/sqlite/rollup.ts
17314
17360
  function startOfLocalDay(at) {
17315
17361
  const day = new Date(at);
@@ -17416,7 +17462,8 @@ function backfillDaily(db) {
17416
17462
  var MIGRATIONS = [
17417
17463
  { id: 1, sql: _001_init_default },
17418
17464
  { id: 2, sql: _002_usage_daily_default, after: backfillDaily },
17419
- { id: 3, sql: _003_quota_snapshot_default }
17465
+ { id: 3, sql: _003_quota_snapshot_default },
17466
+ { id: 4, sql: _004_request_state_default }
17420
17467
  ];
17421
17468
  function openDb(path) {
17422
17469
  const db = new Database(path, { create: true });
@@ -17488,6 +17535,7 @@ function createKeyRepo(db) {
17488
17535
  // packages/store/src/sqlite/usage.ts
17489
17536
  var toLog = (r) => ({
17490
17537
  id: r.id,
17538
+ state: r.state === "pending" ? "pending" : "done",
17491
17539
  at: r.at,
17492
17540
  apiKeyId: r.api_key_id,
17493
17541
  requestedModel: r.requested_model,
@@ -17546,37 +17594,78 @@ function label(value) {
17546
17594
  const text = String(value);
17547
17595
  return text.length === 0 ? "unknown" : text;
17548
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`;
17549
17644
  function createUsageRepo(db) {
17550
- const insert = db.transaction((log) => {
17551
- db.run(`INSERT INTO request_logs
17552
- (id, at, api_key_id, requested_model, resolved_provider, resolved_model, credential_id,
17553
- attempts, status, error_code, input_tokens, output_tokens, cache_read_tokens,
17554
- cache_write_tokens, ttft_ms, duration_ms, cost_usd, degradations)
17555
- VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)`, [
17556
- log.id,
17557
- log.at,
17558
- log.apiKeyId,
17559
- log.requestedModel,
17560
- log.resolvedProvider,
17561
- log.resolvedModel,
17562
- log.credentialId,
17563
- log.attempts,
17564
- log.status,
17565
- log.errorCode,
17566
- log.inputTokens,
17567
- log.outputTokens,
17568
- log.cacheReadTokens,
17569
- log.cacheWriteTokens,
17570
- log.ttftMs,
17571
- log.durationMs,
17572
- log.costUsd,
17573
- JSON.stringify(log.degradations)
17574
- ]);
17575
- 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));
17576
17650
  });
17577
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
+ },
17578
17660
  async append(log) {
17579
- insert(log);
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;
17580
17669
  },
17581
17670
  async recent(limit) {
17582
17671
  return db.query("SELECT * FROM request_logs ORDER BY at DESC LIMIT ?").all(limit).map(toLog);
@@ -17598,7 +17687,7 @@ function createUsageRepo(db) {
17598
17687
  COALESCE(SUM(cache_write_tokens), 0) AS cache_write_tokens,
17599
17688
  COALESCE(SUM(cost_usd), 0) AS cost_usd
17600
17689
  FROM ${daily ? "usage_daily" : "request_logs"}
17601
- WHERE ${timeColumn} >= ? AND ${timeColumn} <= ?
17690
+ WHERE ${daily ? "" : "state = 'done' AND "}${timeColumn} >= ? AND ${timeColumn} <= ?
17602
17691
  GROUP BY key${split === null ? "" : ", split"}
17603
17692
  ORDER BY requests DESC`).all(since, until);
17604
17693
  return rows.map((r) => {
@@ -18077,21 +18166,21 @@ function parse5(argv, options = {}) {
18077
18166
  throw new UsageError(error51 instanceof Error ? error51.message : "could not parse arguments");
18078
18167
  }
18079
18168
  }
18080
- function stringFlag(values, name) {
18081
- const value = values[name];
18169
+ function stringFlag(values2, name) {
18170
+ const value = values2[name];
18082
18171
  return typeof value === "string" ? value : undefined;
18083
18172
  }
18084
- function boolFlag(values, name) {
18085
- return values[name] === true;
18173
+ function boolFlag(values2, name) {
18174
+ return values2[name] === true;
18086
18175
  }
18087
- function listFlag(values, name) {
18088
- const value = values[name];
18176
+ function listFlag(values2, name) {
18177
+ const value = values2[name];
18089
18178
  if (Array.isArray(value))
18090
18179
  return value.filter((entry) => typeof entry === "string");
18091
18180
  return typeof value === "string" ? [value] : undefined;
18092
18181
  }
18093
- function numberFlag(values, name) {
18094
- const raw = stringFlag(values, name);
18182
+ function numberFlag(values2, name) {
18183
+ const raw = stringFlag(values2, name);
18095
18184
  if (raw === undefined)
18096
18185
  return;
18097
18186
  const value = Number(raw);