@jeffusion/bungee-core 6.2.1 → 6.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/dist/main.js +46 -45
  2. package/dist/observability-worker.js +24 -1
  3. package/dist/plugins/anthropic-request-sanitizer/index.js.map +1 -1
  4. package/dist/plugins/anthropic-tool-name-transformer/index.js.map +1 -1
  5. package/dist/plugins/chatgpt-oauth/control.js.map +1 -1
  6. package/dist/plugins/chatgpt-oauth/index.js.map +1 -1
  7. package/dist/plugins/chatgpt-oauth/ui/AccountsPage.svelte +1 -1
  8. package/dist/plugins/codex-router/control.js.map +1 -1
  9. package/dist/plugins/codex-router/index.js.map +1 -1
  10. package/dist/plugins/deepseek-reasoning-fix/index.js.map +1 -1
  11. package/dist/plugins/key-access/control.js.map +1 -1
  12. package/dist/plugins/key-access/index.js.map +1 -1
  13. package/dist/plugins/key-access/ingress.js.map +1 -1
  14. package/dist/plugins/key-rate-limit/index.js.map +1 -1
  15. package/dist/plugins/llm-protocol-adapter/control.js.map +1 -1
  16. package/dist/plugins/llm-protocol-adapter/index.js.map +3 -3
  17. package/dist/plugins/llm-tps/control.js +41 -11
  18. package/dist/plugins/llm-tps/control.js.map +5 -5
  19. package/dist/plugins/llm-tps/index.js +20 -1
  20. package/dist/plugins/llm-tps/index.js.map +6 -6
  21. package/dist/plugins/llm-tps/manifest.json +20 -6
  22. package/dist/plugins/llm-tps/ui/TpsModels.svelte +9 -0
  23. package/dist/plugins/llm-tps/ui/TpsWidget.svelte +63 -58
  24. package/dist/plugins/llm-tps/ui/presentation.ts +6 -6
  25. package/dist/plugins/llm-tps/ui/stats-resource.ts +7 -3
  26. package/dist/plugins/model-mapping/index.js.map +1 -1
  27. package/dist/plugins/models-dev/index.js.map +1 -1
  28. package/dist/plugins/signature-repair/index.js.map +1 -1
  29. package/dist/plugins/token-budget/control.js.map +1 -1
  30. package/dist/plugins/token-budget/index.js.map +2 -2
  31. package/dist/plugins/token-metering/index.js +7 -3
  32. package/dist/plugins/token-metering/index.js.map +5 -5
  33. package/dist/plugins/token-stats/control.js.map +1 -1
  34. package/dist/plugins/token-stats/index.js.map +2 -2
  35. package/dist/plugins/token-stats/ui/TokenStatsPage.svelte +2 -2
  36. package/dist/plugins/user-auth/control.js.map +1 -1
  37. package/dist/plugins/user-auth/index.js.map +1 -1
  38. package/package.json +2 -2
@@ -2,6 +2,15 @@
2
2
  // plugins/llm-tps/contract.ts
3
3
  var TPS_RETENTION_MS = 31 * 24 * 60 * 60 * 1000;
4
4
  var TPS_REPORTING_INCOMPLETE_KEY = "reporting:incomplete";
5
+ function isUpstreamHost(value) {
6
+ if (typeof value !== "string" || !value || /[\s\u0000-\u001f\u007f/\\@?#]/.test(value))
7
+ return false;
8
+ try {
9
+ return new URL(`http://${value}`).host === value || new URL(`https://${value}`).host === value;
10
+ } catch {
11
+ return false;
12
+ }
13
+ }
5
14
 
6
15
  // plugins/llm-tps/server/metrics.ts
7
16
  function isUrlIdentity(value) {
@@ -16,11 +25,19 @@ function identifier(value, name) {
16
25
  if (typeof value !== "string" || !value.trim())
17
26
  throw new TypeError(`invalid llm-tps ${name}`);
18
27
  }
28
+ function validateUpstreamHost(value) {
29
+ if (!isUpstreamHost(value))
30
+ throw new TypeError("invalid llm-tps upstream host");
31
+ }
19
32
  function validateSample(sample) {
20
33
  for (const key of ["attemptId", "requestId", "routeId", "endpointId"])
21
34
  identifier(sample[key], key);
35
+ if (sample.model !== null)
36
+ identifier(sample.model, "model");
22
37
  if (isUrlIdentity(sample.endpointId))
23
38
  throw new TypeError("llm-tps endpoint must be opaque");
39
+ if (sample.upstreamHost !== null)
40
+ validateUpstreamHost(sample.upstreamHost);
24
41
  safeCount(sample.finishedAtMs, "finishedAtMs");
25
42
  safeCount(sample.outputTokens, "outputTokens");
26
43
  if (!Number.isFinite(sample.generationDurationMs) || sample.generationDurationMs <= 0)
@@ -77,8 +94,8 @@ function createObservationAdapter(observation) {
77
94
  observation.withDatabase((db) => {
78
95
  db.transaction(() => {
79
96
  db.query(`INSERT OR IGNORE INTO llm_tps_samples
80
- (attempt_id, request_id, route_id, endpoint_id, finished_at_ms, output_tokens, generation_duration_ms, output_source)
81
- VALUES (?, ?, ?, ?, ?, ?, ?, ?)`).run(sample.attemptId, sample.requestId, sample.routeId, sample.endpointId, sample.finishedAtMs, sample.outputTokens, sample.generationDurationMs, sample.outputSource);
97
+ (attempt_id, request_id, route_id, endpoint_id, finished_at_ms, output_tokens, generation_duration_ms, output_source, model, upstream_host)
98
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(sample.attemptId, sample.requestId, sample.routeId, sample.endpointId, sample.finishedAtMs, sample.outputTokens, sample.generationDurationMs, sample.outputSource, sample.model, sample.upstreamHost);
82
99
  db.query(`DELETE FROM llm_tps_samples WHERE attempt_id IN (
83
100
  SELECT attempt_id FROM llm_tps_samples INDEXED BY idx_llm_tps_samples_finished
84
101
  WHERE finished_at_ms < ? ORDER BY finished_at_ms LIMIT 500
@@ -92,22 +109,35 @@ function createObservationAdapter(observation) {
92
109
  return observation.withDatabase((db) => db.transaction(() => {
93
110
  const where = "WHERE finished_at_ms >= ? AND finished_at_ms <= ?";
94
111
  const totalRow = db.query(`SELECT ${metrics} FROM llm_tps_samples ${where}`).get(start, input.asOfMs);
95
- const rows = db.query(`SELECT route_id AS routeId, endpoint_id AS endpointId, ${metrics}
96
- FROM llm_tps_samples ${where} GROUP BY route_id, endpoint_id`).all(start, input.asOfMs);
112
+ const rows = db.query(`SELECT route_id AS routeId, upstream_host AS upstreamHost, ${metrics}
113
+ FROM llm_tps_samples ${where} AND upstream_host IS NOT NULL GROUP BY route_id, upstream_host`).all(start, input.asOfMs);
97
114
  const checked = (row) => {
98
115
  if (safeCount(row.invalidSamples, "invalid sample count") !== 0)
99
116
  throw new RangeError("invalid persisted llm-tps sample");
100
117
  return aggregate(row);
101
118
  };
102
119
  const total = checked(totalRow);
120
+ const bucketMs = input.range === "1h" ? 60000 : input.range === "12h" ? 1800000 : 3600000;
121
+ const historyRows = db.query(`SELECT CAST(finished_at_ms / ? AS INTEGER) AS bucket, ${metrics}
122
+ FROM llm_tps_samples ${where} GROUP BY bucket ORDER BY bucket`).all(bucketMs, start, input.asOfMs);
123
+ const buckets = new Map(historyRows.map((row) => [safeCount(row.bucket, "bucket"), checked(row)]));
124
+ const firstBucket = Math.floor(Math.max(0, start) / bucketMs), lastBucket = Math.floor(input.asOfMs / bucketMs);
125
+ const history = Array.from({ length: lastBucket - firstBucket + 1 }, (_, index) => ({
126
+ bucketStartMs: (firstBucket + index) * bucketMs,
127
+ ...buckets.get(firstBucket + index) ?? { tps: null, outputTokens: 0, generationDurationMs: 0, sampleCount: 0, estimatedSampleCount: 0 }
128
+ }));
103
129
  const endpoints = rows.map((row) => {
104
130
  identifier(row.routeId, "routeId");
105
- identifier(row.endpointId, "endpointId");
106
- if (isUrlIdentity(row.endpointId))
107
- throw new TypeError("invalid persisted llm-tps endpoint");
108
- return { routeId: row.routeId, endpointId: row.endpointId, ...checked(row) };
109
- }).sort((a, b) => b.tps - a.tps || compare(a.routeId, b.routeId) || compare(a.endpointId, b.endpointId));
110
- return { range: input.range, asOfMs: input.asOfMs, total, endpoints, reportingIncomplete: false };
131
+ validateUpstreamHost(row.upstreamHost);
132
+ return { routeId: row.routeId, upstreamHost: row.upstreamHost, ...checked(row) };
133
+ }).sort((a, b) => b.tps - a.tps || compare(a.routeId, b.routeId) || compare(a.upstreamHost, b.upstreamHost));
134
+ const models = db.query(`SELECT route_id AS routeId, model, ${metrics}
135
+ FROM llm_tps_samples ${where} AND model IS NOT NULL GROUP BY route_id, model`).all(start, input.asOfMs).map((row) => {
136
+ identifier(row.routeId, "routeId");
137
+ identifier(row.model, "model");
138
+ return { routeId: row.routeId, model: row.model, ...checked(row) };
139
+ }).sort((a, b) => b.tps - a.tps || compare(a.routeId, b.routeId) || compare(a.model, b.model));
140
+ return { range: input.range, asOfMs: input.asOfMs, total, endpoints, models, history, reportingIncomplete: false };
111
141
  })());
112
142
  }
113
143
  });
@@ -209,4 +239,4 @@ export {
209
239
  control_default as default
210
240
  };
211
241
 
212
- //# debugId=6D69678B8F3EC90164756E2164756E21
242
+ //# debugId=81BE1310B726085964756E2164756E21
@@ -2,12 +2,12 @@
2
2
  "version": 3,
3
3
  "sources": ["../../../../../plugins/llm-tps/contract.ts", "../../../../../plugins/llm-tps/server/metrics.ts", "../../../../../plugins/llm-tps/server/storage.ts", "../../../../../plugins/llm-tps/server/control.ts"],
4
4
  "sourcesContent": [
5
- "export type TpsRange = '1h' | '12h' | '24h';\n\nexport interface TpsSample {\n readonly attemptId: string;\n readonly requestId: string;\n readonly routeId: string;\n /** Configured endpoint ID, or an opaque hash when the host identity is a URL. */\n readonly endpointId: string;\n readonly finishedAtMs: number;\n readonly outputTokens: number;\n readonly generationDurationMs: number;\n readonly outputSource: 'official' | 'estimated';\n}\n\nexport interface TpsAggregate {\n readonly tps: number | null;\n readonly outputTokens: number;\n readonly generationDurationMs: number;\n readonly sampleCount: number;\n readonly estimatedSampleCount: number;\n}\n\nexport interface TpsEndpointStats extends TpsAggregate {\n readonly routeId: string;\n readonly endpointId: string;\n}\n\nexport interface TpsStatsResponse {\n readonly range: TpsRange;\n readonly asOfMs: number;\n readonly total: TpsAggregate;\n readonly endpoints: readonly TpsEndpointStats[];\n readonly reportingIncomplete: boolean;\n}\n\nexport const TPS_RETENTION_MS = 31 * 24 * 60 * 60 * 1000;\nexport const TPS_REPORTING_INCOMPLETE_KEY = 'reporting:incomplete';\n",
6
- "import { createHash } from 'node:crypto';\nimport type { TokenMeteringResult } from '@jeffusion/bungee-core/plugin';\nimport type { TpsAggregate, TpsRange, TpsSample } from '../contract';\n\nexport function isUrlIdentity(value: string): boolean {\n return /^(?:[a-z][a-z\\d+.-]*:)?\\/\\//i.test(value);\n}\n\nexport function endpointIdentity(upstreamId: string): string {\n return isUrlIdentity(upstreamId)\n ? `endpoint-${createHash('sha256').update(upstreamId).digest('hex').slice(0, 16)}`\n : upstreamId;\n}\n\nexport function safeCount(value: unknown, name: string): number {\n if (typeof value !== 'number' || !Number.isSafeInteger(value) || value < 0) throw new RangeError(`invalid llm-tps ${name}`);\n return value;\n}\n\nexport function identifier(value: unknown, name: string): asserts value is string {\n if (typeof value !== 'string' || !value.trim()) throw new TypeError(`invalid llm-tps ${name}`);\n}\n\nexport function validateSample(sample: TpsSample): void {\n for (const key of ['attemptId', 'requestId', 'routeId', 'endpointId'] as const) identifier(sample[key], key);\n if (isUrlIdentity(sample.endpointId)) throw new TypeError('llm-tps endpoint must be opaque');\n safeCount(sample.finishedAtMs, 'finishedAtMs');\n safeCount(sample.outputTokens, 'outputTokens');\n if (!Number.isFinite(sample.generationDurationMs) || sample.generationDurationMs <= 0) throw new RangeError('invalid llm-tps duration');\n if (sample.outputSource !== 'official' && sample.outputSource !== 'estimated') throw new TypeError('invalid llm-tps output source');\n if (!Number.isFinite(sample.outputTokens / sample.generationDurationMs * 1000)) throw new RangeError('invalid llm-tps rate');\n}\n\n/** Unknown input usage does not invalidate observable output usage. */\nexport function sampleFromResult(result: TokenMeteringResult, routeId: string): TpsSample | null {\n const output = result.generationOutput;\n if (result.routeId !== routeId || !output\n || (output.source !== 'official' && output.source !== 'estimated')\n || !Number.isSafeInteger(output.tokens) || output.tokens < 0\n || !Number.isFinite(result.generationDurationMs) || result.generationDurationMs! <= 0) return null;\n const sample: TpsSample = {\n attemptId: result.attemptId, requestId: result.requestId, routeId: result.routeId,\n endpointId: endpointIdentity(result.upstreamId), finishedAtMs: result.finishedAtMs,\n outputTokens: output.tokens, generationDurationMs: result.generationDurationMs!, outputSource: output.source,\n };\n validateSample(sample);\n return sample;\n}\n\nexport function rangeDuration(range: TpsRange): number {\n if (range === '1h') return 3_600_000;\n if (range === '12h') return 43_200_000;\n if (range === '24h') return 86_400_000;\n throw new RangeError('invalid llm-tps range');\n}\n\nexport function aggregate(row: Record<string, unknown>): TpsAggregate {\n const outputTokens = safeCount(row.outputTokens, 'aggregate tokens');\n const sampleCount = safeCount(row.sampleCount, 'sample count');\n const estimatedSampleCount = safeCount(row.estimatedSampleCount, 'estimated sample count');\n const duration = row.generationDurationMs;\n if (typeof duration !== 'number' || !Number.isFinite(duration) || duration < 0\n || (sampleCount > 0 && duration <= 0) || estimatedSampleCount > sampleCount\n || (sampleCount === 0 && (outputTokens !== 0 || duration !== 0 || estimatedSampleCount !== 0))) throw new RangeError('invalid llm-tps aggregate');\n const tps = sampleCount === 0 ? null : outputTokens / duration * 1000;\n if (tps !== null && (!Number.isFinite(tps) || tps < 0)) throw new RangeError('invalid llm-tps aggregate rate');\n return { tps, outputTokens, generationDurationMs: duration, sampleCount, estimatedSampleCount };\n}\n",
7
- "import type { PluginObservationStorage, PluginStorage } from '@jeffusion/bungee-core/plugin';\nimport { TPS_RETENTION_MS, type TpsRange, type TpsSample, type TpsStatsResponse } from '../contract';\nimport { aggregate, identifier, isUrlIdentity, rangeDuration, safeCount, validateSample } from './metrics';\n\nexport interface TpsObservationClient {\n recordSample(sample: TpsSample): Promise<void>;\n queryStats(input: { range: TpsRange; asOfMs: number }): Promise<TpsStatsResponse>;\n}\n\n/** Runtime and control use only the host's namespaced observation adapter. */\nexport function observationClient(storage: PluginStorage): TpsObservationClient {\n if (typeof storage.invokeObservation !== 'function') throw new Error('llm-tps observation storage is unavailable');\n const invoke = storage.invokeObservation.bind(storage);\n return {\n recordSample: sample => invoke<void>('recordSample', [sample]),\n queryStats: input => invoke<TpsStatsResponse>('queryStats', [input]),\n };\n}\n\nconst metrics = `COALESCE(SUM(output_tokens), 0) AS outputTokens,\n COALESCE(SUM(generation_duration_ms), 0) AS generationDurationMs,\n COUNT(*) AS sampleCount, COALESCE(SUM(output_source = 'estimated'), 0) AS estimatedSampleCount,\n COALESCE(SUM(CASE WHEN typeof(output_tokens) != 'integer' OR output_tokens < 0 OR output_tokens > 9007199254740991\n OR typeof(generation_duration_ms) NOT IN ('integer', 'real') OR NOT(generation_duration_ms > 0)\n OR generation_duration_ms > 1.7976931348623157e308 OR output_source NOT IN ('official','estimated')\n OR typeof(finished_at_ms) != 'integer' OR finished_at_ms < 0 OR finished_at_ms > 9007199254740991\n THEN 1 ELSE 0 END), 0) AS invalidSamples`;\n\n/** SQL is confined to the trusted access.db adapter callback. */\nexport function createObservationAdapter(observation: PluginObservationStorage): TpsObservationClient {\n return Object.freeze({\n async recordSample(sample: TpsSample): Promise<void> {\n validateSample(sample);\n observation.withDatabase(db => {\n db.transaction(() => {\n db.query(`INSERT OR IGNORE INTO llm_tps_samples\n (attempt_id, request_id, route_id, endpoint_id, finished_at_ms, output_tokens, generation_duration_ms, output_source)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?)`).run(sample.attemptId, sample.requestId, sample.routeId, sample.endpointId,\n sample.finishedAtMs, sample.outputTokens, sample.generationDurationMs, sample.outputSource);\n db.query(`DELETE FROM llm_tps_samples WHERE attempt_id IN (\n SELECT attempt_id FROM llm_tps_samples INDEXED BY idx_llm_tps_samples_finished\n WHERE finished_at_ms < ? ORDER BY finished_at_ms LIMIT 500\n )`).run(Date.now() - TPS_RETENTION_MS);\n }).immediate();\n });\n },\n async queryStats(input: { range: TpsRange; asOfMs: number }): Promise<TpsStatsResponse> {\n safeCount(input.asOfMs, 'asOfMs');\n const start = input.asOfMs - rangeDuration(input.range);\n return observation.withDatabase(db => db.transaction(() => {\n const where = 'WHERE finished_at_ms >= ? AND finished_at_ms <= ?';\n const totalRow = db.query<Record<string, unknown>, [number, number]>(`SELECT ${metrics} FROM llm_tps_samples ${where}`).get(start, input.asOfMs)!;\n const rows = db.query<Record<string, unknown>, [number, number]>(`SELECT route_id AS routeId, endpoint_id AS endpointId, ${metrics}\n FROM llm_tps_samples ${where} GROUP BY route_id, endpoint_id`).all(start, input.asOfMs);\n const checked = (row: Record<string, unknown>) => {\n if (safeCount(row.invalidSamples, 'invalid sample count') !== 0) throw new RangeError('invalid persisted llm-tps sample');\n return aggregate(row);\n };\n const total = checked(totalRow);\n const endpoints = rows.map(row => {\n identifier(row.routeId, 'routeId'); identifier(row.endpointId, 'endpointId');\n if (isUrlIdentity(row.endpointId)) throw new TypeError('invalid persisted llm-tps endpoint');\n return { routeId: row.routeId, endpointId: row.endpointId, ...checked(row) };\n }).sort((a, b) => (b.tps! - a.tps!) || compare(a.routeId, b.routeId) || compare(a.endpointId, b.endpointId));\n return { range: input.range, asOfMs: input.asOfMs, total, endpoints, reportingIncomplete: false };\n })());\n },\n });\n}\n\nfunction compare(a: string, b: string): number { return a < b ? -1 : a > b ? 1 : 0; }\n",
5
+ "export type TpsRange = '1h' | '12h' | '24h';\n\nexport interface TpsSample {\n readonly attemptId: string;\n readonly requestId: string;\n readonly routeId: string;\n /** Configured endpoint ID, or an opaque hash when the upstream identity is a URL. */\n readonly endpointId: string;\n /** Actual outbound URL.host, or null when unavailable. */\n readonly upstreamHost: string | null;\n readonly model: string | null;\n readonly finishedAtMs: number;\n readonly outputTokens: number;\n readonly generationDurationMs: number;\n readonly outputSource: 'official' | 'estimated';\n}\n\nexport interface TpsAggregate {\n readonly tps: number | null;\n readonly outputTokens: number;\n readonly generationDurationMs: number;\n readonly sampleCount: number;\n readonly estimatedSampleCount: number;\n}\n\nexport interface TpsEndpointStats extends TpsAggregate {\n readonly routeId: string;\n readonly upstreamHost: string;\n}\n\nexport interface TpsModelStats extends TpsAggregate {\n readonly routeId: string;\n readonly model: string;\n}\n\nexport interface TpsHistoryPoint extends TpsAggregate {\n readonly bucketStartMs: number;\n}\n\nexport interface TpsStatsResponse {\n readonly range: TpsRange;\n readonly asOfMs: number;\n readonly total: TpsAggregate;\n readonly endpoints: readonly TpsEndpointStats[];\n readonly models: readonly TpsModelStats[];\n readonly history: readonly TpsHistoryPoint[];\n readonly reportingIncomplete: boolean;\n}\n\nexport const TPS_RETENTION_MS = 31 * 24 * 60 * 60 * 1000;\nexport const TPS_REPORTING_INCOMPLETE_KEY = 'reporting:incomplete';\n\n/** Canonical URL.host only: no credentials, path, query or fragment. */\nexport function isUpstreamHost(value: unknown): value is string {\n if (typeof value !== 'string' || !value || /[\\s\\u0000-\\u001f\\u007f/\\\\@?#]/.test(value)) return false;\n try {\n // Either scheme preserves the other scheme's default port in a valid host.\n return new URL(`http://${value}`).host === value || new URL(`https://${value}`).host === value;\n } catch { return false; }\n}\n",
6
+ "import { createHash } from 'node:crypto';\nimport type { TokenMeteringResult } from '@jeffusion/bungee-core/plugin';\nimport { isUpstreamHost, type TpsAggregate, type TpsRange, type TpsSample } from '../contract';\n\nexport function isUrlIdentity(value: string): boolean {\n return /^(?:[a-z][a-z\\d+.-]*:)?\\/\\//i.test(value);\n}\n\nexport function endpointIdentity(upstreamId: string): string {\n return isUrlIdentity(upstreamId)\n ? `endpoint-${createHash('sha256').update(upstreamId).digest('hex').slice(0, 16)}`\n : upstreamId;\n}\n\nexport function safeCount(value: unknown, name: string): number {\n if (typeof value !== 'number' || !Number.isSafeInteger(value) || value < 0) throw new RangeError(`invalid llm-tps ${name}`);\n return value;\n}\n\nexport function identifier(value: unknown, name: string): asserts value is string {\n if (typeof value !== 'string' || !value.trim()) throw new TypeError(`invalid llm-tps ${name}`);\n}\n\n/** Accept only a canonical URL.host, never URL credentials, path, query or fragment. */\nexport function validateUpstreamHost(value: unknown): asserts value is string {\n if (!isUpstreamHost(value)) throw new TypeError('invalid llm-tps upstream host');\n}\n\nexport function validateSample(sample: TpsSample): void {\n for (const key of ['attemptId', 'requestId', 'routeId', 'endpointId'] as const) identifier(sample[key], key);\n if (sample.model !== null) identifier(sample.model, 'model');\n if (isUrlIdentity(sample.endpointId)) throw new TypeError('llm-tps endpoint must be opaque');\n if (sample.upstreamHost !== null) validateUpstreamHost(sample.upstreamHost);\n safeCount(sample.finishedAtMs, 'finishedAtMs');\n safeCount(sample.outputTokens, 'outputTokens');\n if (!Number.isFinite(sample.generationDurationMs) || sample.generationDurationMs <= 0) throw new RangeError('invalid llm-tps duration');\n if (sample.outputSource !== 'official' && sample.outputSource !== 'estimated') throw new TypeError('invalid llm-tps output source');\n if (!Number.isFinite(sample.outputTokens / sample.generationDurationMs * 1000)) throw new RangeError('invalid llm-tps rate');\n}\n\n/** Unknown input usage does not invalidate observable output usage. */\nexport function sampleFromResult(result: TokenMeteringResult, routeId: string): TpsSample | null {\n const output = result.generationOutput;\n if (result.routeId !== routeId || !output\n || (output.source !== 'official' && output.source !== 'estimated')\n || !Number.isSafeInteger(output.tokens) || output.tokens < 0\n || !Number.isFinite(result.generationDurationMs) || result.generationDurationMs! <= 0) return null;\n const sample: TpsSample = {\n attemptId: result.attemptId, requestId: result.requestId, routeId: result.routeId,\n endpointId: endpointIdentity(result.upstreamId), finishedAtMs: result.finishedAtMs,\n upstreamHost: result.upstreamHost ?? null,\n model: typeof result.model === 'string' && result.model.trim() ? result.model : null,\n outputTokens: output.tokens, generationDurationMs: result.generationDurationMs!, outputSource: output.source,\n };\n validateSample(sample);\n return sample;\n}\n\nexport function rangeDuration(range: TpsRange): number {\n if (range === '1h') return 3_600_000;\n if (range === '12h') return 43_200_000;\n if (range === '24h') return 86_400_000;\n throw new RangeError('invalid llm-tps range');\n}\n\nexport function aggregate(row: Record<string, unknown>): TpsAggregate {\n const outputTokens = safeCount(row.outputTokens, 'aggregate tokens');\n const sampleCount = safeCount(row.sampleCount, 'sample count');\n const estimatedSampleCount = safeCount(row.estimatedSampleCount, 'estimated sample count');\n const duration = row.generationDurationMs;\n if (typeof duration !== 'number' || !Number.isFinite(duration) || duration < 0\n || (sampleCount > 0 && duration <= 0) || estimatedSampleCount > sampleCount\n || (sampleCount === 0 && (outputTokens !== 0 || duration !== 0 || estimatedSampleCount !== 0))) throw new RangeError('invalid llm-tps aggregate');\n const tps = sampleCount === 0 ? null : outputTokens / duration * 1000;\n if (tps !== null && (!Number.isFinite(tps) || tps < 0)) throw new RangeError('invalid llm-tps aggregate rate');\n return { tps, outputTokens, generationDurationMs: duration, sampleCount, estimatedSampleCount };\n}\n",
7
+ "import type { PluginObservationStorage, PluginStorage } from '@jeffusion/bungee-core/plugin';\nimport { TPS_RETENTION_MS, type TpsRange, type TpsSample, type TpsStatsResponse } from '../contract';\nimport { aggregate, identifier, rangeDuration, safeCount, validateSample, validateUpstreamHost } from './metrics';\n\nexport interface TpsObservationClient {\n recordSample(sample: TpsSample): Promise<void>;\n queryStats(input: { range: TpsRange; asOfMs: number }): Promise<TpsStatsResponse>;\n}\n\n/** Runtime and control use only the host's namespaced observation adapter. */\nexport function observationClient(storage: PluginStorage): TpsObservationClient {\n if (typeof storage.invokeObservation !== 'function') throw new Error('llm-tps observation storage is unavailable');\n const invoke = storage.invokeObservation.bind(storage);\n return {\n recordSample: sample => invoke<void>('recordSample', [sample]),\n queryStats: input => invoke<TpsStatsResponse>('queryStats', [input]),\n };\n}\n\nconst metrics = `COALESCE(SUM(output_tokens), 0) AS outputTokens,\n COALESCE(SUM(generation_duration_ms), 0) AS generationDurationMs,\n COUNT(*) AS sampleCount, COALESCE(SUM(output_source = 'estimated'), 0) AS estimatedSampleCount,\n COALESCE(SUM(CASE WHEN typeof(output_tokens) != 'integer' OR output_tokens < 0 OR output_tokens > 9007199254740991\n OR typeof(generation_duration_ms) NOT IN ('integer', 'real') OR NOT(generation_duration_ms > 0)\n OR generation_duration_ms > 1.7976931348623157e308 OR output_source NOT IN ('official','estimated')\n OR typeof(finished_at_ms) != 'integer' OR finished_at_ms < 0 OR finished_at_ms > 9007199254740991\n THEN 1 ELSE 0 END), 0) AS invalidSamples`;\n\n/** SQL is confined to the trusted access.db adapter callback. */\nexport function createObservationAdapter(observation: PluginObservationStorage): TpsObservationClient {\n return Object.freeze({\n async recordSample(sample: TpsSample): Promise<void> {\n validateSample(sample);\n observation.withDatabase(db => {\n db.transaction(() => {\n db.query(`INSERT OR IGNORE INTO llm_tps_samples\n (attempt_id, request_id, route_id, endpoint_id, finished_at_ms, output_tokens, generation_duration_ms, output_source, model, upstream_host)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(sample.attemptId, sample.requestId, sample.routeId, sample.endpointId,\n sample.finishedAtMs, sample.outputTokens, sample.generationDurationMs, sample.outputSource, sample.model, sample.upstreamHost);\n db.query(`DELETE FROM llm_tps_samples WHERE attempt_id IN (\n SELECT attempt_id FROM llm_tps_samples INDEXED BY idx_llm_tps_samples_finished\n WHERE finished_at_ms < ? ORDER BY finished_at_ms LIMIT 500\n )`).run(Date.now() - TPS_RETENTION_MS);\n }).immediate();\n });\n },\n async queryStats(input: { range: TpsRange; asOfMs: number }): Promise<TpsStatsResponse> {\n safeCount(input.asOfMs, 'asOfMs');\n const start = input.asOfMs - rangeDuration(input.range);\n return observation.withDatabase(db => db.transaction(() => {\n const where = 'WHERE finished_at_ms >= ? AND finished_at_ms <= ?';\n const totalRow = db.query<Record<string, unknown>, [number, number]>(`SELECT ${metrics} FROM llm_tps_samples ${where}`).get(start, input.asOfMs)!;\n const rows = db.query<Record<string, unknown>, [number, number]>(`SELECT route_id AS routeId, upstream_host AS upstreamHost, ${metrics}\n FROM llm_tps_samples ${where} AND upstream_host IS NOT NULL GROUP BY route_id, upstream_host`).all(start, input.asOfMs);\n const checked = (row: Record<string, unknown>) => {\n if (safeCount(row.invalidSamples, 'invalid sample count') !== 0) throw new RangeError('invalid persisted llm-tps sample');\n return aggregate(row);\n };\n const total = checked(totalRow);\n const bucketMs = input.range === '1h' ? 60_000 : input.range === '12h' ? 1_800_000 : 3_600_000;\n const historyRows = db.query<Record<string, unknown>, [number, number, number]>(\n `SELECT CAST(finished_at_ms / ? AS INTEGER) AS bucket, ${metrics}\n FROM llm_tps_samples ${where} GROUP BY bucket ORDER BY bucket`,\n ).all(bucketMs, start, input.asOfMs);\n const buckets = new Map(historyRows.map(row => [safeCount(row.bucket, 'bucket'), checked(row)]));\n const firstBucket = Math.floor(Math.max(0, start) / bucketMs), lastBucket = Math.floor(input.asOfMs / bucketMs);\n const history = Array.from({ length: lastBucket - firstBucket + 1 }, (_, index) => ({\n bucketStartMs: (firstBucket + index) * bucketMs,\n ...(buckets.get(firstBucket + index) ?? { tps: null, outputTokens: 0, generationDurationMs: 0, sampleCount: 0, estimatedSampleCount: 0 }),\n }));\n const endpoints = rows.map(row => {\n identifier(row.routeId, 'routeId'); validateUpstreamHost(row.upstreamHost);\n return { routeId: row.routeId, upstreamHost: row.upstreamHost, ...checked(row) };\n }).sort((a, b) => (b.tps! - a.tps!) || compare(a.routeId, b.routeId) || compare(a.upstreamHost, b.upstreamHost));\n const models = db.query<Record<string, unknown>, [number, number]>(`SELECT route_id AS routeId, model, ${metrics}\n FROM llm_tps_samples ${where} AND model IS NOT NULL GROUP BY route_id, model`).all(start, input.asOfMs).map(row => {\n identifier(row.routeId, 'routeId'); identifier(row.model, 'model');\n return { routeId: row.routeId, model: row.model, ...checked(row) };\n }).sort((a, b) => (b.tps! - a.tps!) || compare(a.routeId, b.routeId) || compare(a.model, b.model));\n return { range: input.range, asOfMs: input.asOfMs, total, endpoints, models, history, reportingIncomplete: false };\n })());\n },\n });\n}\n\nfunction compare(a: string, b: string): number { return a < b ? -1 : a > b ? 1 : 0; }\n",
8
8
  "import type { ControlApiHandlerContext, ControlHostContext, ControlPlugin, PluginControl } from '@jeffusion/bungee-core/plugin';\nimport { TPS_REPORTING_INCOMPLETE_KEY, type TpsRange } from '../contract';\nimport { observationClient } from './storage';\nexport { createObservationAdapter } from './storage';\n\nclass ControlError extends Error {\n constructor(readonly code: 'disposed' | 'request_cancelled' | 'invalid_input') { super(code); }\n}\n\nfunction abortable<T>(operation: Promise<T>, signals: readonly AbortSignal[]): Promise<T> {\n return new Promise<T>((resolve, reject) => {\n const listeners: Array<() => void> = [];\n const cleanup = () => signals.forEach((signal, index) => signal.removeEventListener('abort', listeners[index]!));\n const fail = (index: number) => { cleanup(); reject(new ControlError(index < 3 ? 'disposed' : 'request_cancelled')); };\n signals.forEach((signal, index) => {\n const listener = () => fail(index); listeners.push(listener);\n signal.addEventListener('abort', listener, { once: true });\n });\n const cancelled = signals.findIndex(signal => signal.aborted);\n if (cancelled !== -1) fail(cancelled);\n operation.then(value => { cleanup(); resolve(value); }, error => { cleanup(); reject(error); });\n });\n}\n\nexport function createControl(host: ControlHostContext): PluginControl {\n const client = observationClient(host.storage);\n const disposal = new AbortController();\n let disposed = false;\n const assertAlive = (context?: ControlApiHandlerContext) => {\n if (disposed || host.signal.aborted || context?.signal.aborted) throw new ControlError('disposed');\n if (context?.requestSignal.aborted || context?.request.signal.aborted) throw new ControlError('request_cancelled');\n };\n const dispose = () => { disposed = true; disposal.abort(); host.signal.removeEventListener('abort', dispose); };\n host.signal.addEventListener('abort', dispose, { once: true });\n return {\n api: [{ path: '/stats', methods: ['GET'], handler: 'getStats', async invoke(context) {\n try {\n assertAlive(context);\n if (context.request.method !== 'GET') return Response.json({ error: 'method_not_allowed' }, { status: 405, headers: { allow: 'GET' } });\n const params = new URL(context.request.url).searchParams;\n const range = params.get('range') ?? '24h';\n if ([...params.keys()].some(key => key !== 'range') || params.getAll('range').length > 1\n || !['1h', '12h', '24h'].includes(range)) throw new ControlError('invalid_input');\n const readIncomplete = async () => {\n const storage = host.storage;\n const strict = storage.readStrict ? await storage.readStrict<unknown>(TPS_REPORTING_INCOMPLETE_KEY) : null;\n const value = strict === null ? await storage.get<unknown>(TPS_REPORTING_INCOMPLETE_KEY) : strict.found ? strict.value : null;\n if (value !== null && value !== false && value !== true) throw new Error('invalid reporting status');\n return value === true;\n };\n const [stats, incomplete] = await abortable(Promise.all([\n client.queryStats({ range: range as TpsRange, asOfMs: Date.now() }), readIncomplete(),\n ]), [disposal.signal, host.signal, context.signal, context.requestSignal, context.request.signal]);\n assertAlive(context);\n return Response.json({ ...stats, reportingIncomplete: stats.reportingIncomplete || incomplete });\n } catch (error) {\n const code = error instanceof ControlError ? error.code : 'storage_unavailable';\n const status = code === 'invalid_input' || code === 'request_cancelled' ? 400 : code === 'disposed' ? 409 : 503;\n return Response.json({ error: code }, { status });\n }\n } }],\n rpc: [],\n start() { assertAlive(); },\n dispose,\n };\n}\n\nexport default { createControl } satisfies ControlPlugin;\n"
9
9
  ],
10
- "mappings": "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",
11
- "debugId": "6D69678B8F3EC90164756E2164756E21",
10
+ "mappings": "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",
11
+ "debugId": "81BE1310B726085964756E2164756E21",
12
12
  "names": []
13
13
  }
@@ -137,6 +137,15 @@ var TOKEN_STATS_RETENTION_MS = 31 * 24 * HOUR;
137
137
  // plugins/llm-tps/contract.ts
138
138
  var TPS_RETENTION_MS = 31 * 24 * 60 * 60 * 1000;
139
139
  var TPS_REPORTING_INCOMPLETE_KEY = "reporting:incomplete";
140
+ function isUpstreamHost(value) {
141
+ if (typeof value !== "string" || !value || /[\s\u0000-\u001f\u007f/\\@?#]/.test(value))
142
+ return false;
143
+ try {
144
+ return new URL(`http://${value}`).host === value || new URL(`https://${value}`).host === value;
145
+ } catch {
146
+ return false;
147
+ }
148
+ }
140
149
 
141
150
  // plugins/llm-tps/server/metrics.ts
142
151
  import { createHash } from "crypto";
@@ -155,11 +164,19 @@ function identifier(value, name) {
155
164
  if (typeof value !== "string" || !value.trim())
156
165
  throw new TypeError(`invalid llm-tps ${name}`);
157
166
  }
167
+ function validateUpstreamHost(value) {
168
+ if (!isUpstreamHost(value))
169
+ throw new TypeError("invalid llm-tps upstream host");
170
+ }
158
171
  function validateSample(sample) {
159
172
  for (const key of ["attemptId", "requestId", "routeId", "endpointId"])
160
173
  identifier(sample[key], key);
174
+ if (sample.model !== null)
175
+ identifier(sample.model, "model");
161
176
  if (isUrlIdentity(sample.endpointId))
162
177
  throw new TypeError("llm-tps endpoint must be opaque");
178
+ if (sample.upstreamHost !== null)
179
+ validateUpstreamHost(sample.upstreamHost);
163
180
  safeCount(sample.finishedAtMs, "finishedAtMs");
164
181
  safeCount(sample.outputTokens, "outputTokens");
165
182
  if (!Number.isFinite(sample.generationDurationMs) || sample.generationDurationMs <= 0)
@@ -179,6 +196,8 @@ function sampleFromResult(result, routeId) {
179
196
  routeId: result.routeId,
180
197
  endpointId: endpointIdentity(result.upstreamId),
181
198
  finishedAtMs: result.finishedAtMs,
199
+ upstreamHost: result.upstreamHost ?? null,
200
+ model: typeof result.model === "string" && result.model.trim() ? result.model : null,
182
201
  outputTokens: output.tokens,
183
202
  generationDurationMs: result.generationDurationMs,
184
203
  outputSource: output.source
@@ -283,4 +302,4 @@ export {
283
302
  server_default as default
284
303
  };
285
304
 
286
- //# debugId=32378BFF08D1414E64756E2164756E21
305
+ //# debugId=A9BFFA841EA2D74364756E2164756E21