@jeffusion/bungee-core 6.3.0 → 6.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/dist/config-storage-worker.js +31 -1
  2. package/dist/main.js +79 -39
  3. package/dist/observability-worker.js +14 -1
  4. package/dist/plugins/anthropic-request-sanitizer/index.js.map +1 -1
  5. package/dist/plugins/anthropic-tool-name-transformer/index.js.map +1 -1
  6. package/dist/plugins/chatgpt-oauth/control.js.map +1 -1
  7. package/dist/plugins/chatgpt-oauth/index.js.map +1 -1
  8. package/dist/plugins/chatgpt-oauth/ui/AccountsPage.svelte +1 -1
  9. package/dist/plugins/codex-router/control.js.map +1 -1
  10. package/dist/plugins/codex-router/index.js.map +1 -1
  11. package/dist/plugins/deepseek-reasoning-fix/index.js.map +1 -1
  12. package/dist/plugins/key-access/control.js.map +1 -1
  13. package/dist/plugins/key-access/index.js.map +1 -1
  14. package/dist/plugins/key-access/ingress.js.map +1 -1
  15. package/dist/plugins/key-rate-limit/index.js.map +1 -1
  16. package/dist/plugins/llm-protocol-adapter/control.js.map +1 -1
  17. package/dist/plugins/llm-protocol-adapter/index.js.map +3 -3
  18. package/dist/plugins/llm-tps/control.js +23 -10
  19. package/dist/plugins/llm-tps/control.js.map +5 -5
  20. package/dist/plugins/llm-tps/index.js +17 -1
  21. package/dist/plugins/llm-tps/index.js.map +6 -6
  22. package/dist/plugins/llm-tps/ui/TpsWidget.svelte +29 -50
  23. package/dist/plugins/llm-tps/ui/presentation.ts +1 -15
  24. package/dist/plugins/llm-tps/ui/stats-resource.ts +2 -2
  25. package/dist/plugins/model-mapping/index.js.map +1 -1
  26. package/dist/plugins/models-dev/index.js.map +1 -1
  27. package/dist/plugins/signature-repair/index.js.map +1 -1
  28. package/dist/plugins/token-budget/control.js.map +1 -1
  29. package/dist/plugins/token-budget/index.js.map +2 -2
  30. package/dist/plugins/token-metering/index.js +7 -3
  31. package/dist/plugins/token-metering/index.js.map +5 -5
  32. package/dist/plugins/token-stats/control.js.map +1 -1
  33. package/dist/plugins/token-stats/index.js.map +2 -2
  34. package/dist/plugins/token-stats/ui/TokenStatsPage.svelte +2 -2
  35. package/dist/plugins/user-auth/control.js.map +1 -1
  36. package/dist/plugins/user-auth/index.js.map +1 -1
  37. 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,6 +25,10 @@ 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);
@@ -23,6 +36,8 @@ function validateSample(sample) {
23
36
  identifier(sample.model, "model");
24
37
  if (isUrlIdentity(sample.endpointId))
25
38
  throw new TypeError("llm-tps endpoint must be opaque");
39
+ if (sample.upstreamHost !== null)
40
+ validateUpstreamHost(sample.upstreamHost);
26
41
  safeCount(sample.finishedAtMs, "finishedAtMs");
27
42
  safeCount(sample.outputTokens, "outputTokens");
28
43
  if (!Number.isFinite(sample.generationDurationMs) || sample.generationDurationMs <= 0)
@@ -79,8 +94,8 @@ function createObservationAdapter(observation) {
79
94
  observation.withDatabase((db) => {
80
95
  db.transaction(() => {
81
96
  db.query(`INSERT OR IGNORE INTO llm_tps_samples
82
- (attempt_id, request_id, route_id, endpoint_id, finished_at_ms, output_tokens, generation_duration_ms, output_source, model)
83
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(sample.attemptId, sample.requestId, sample.routeId, sample.endpointId, sample.finishedAtMs, sample.outputTokens, sample.generationDurationMs, sample.outputSource, sample.model);
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);
84
99
  db.query(`DELETE FROM llm_tps_samples WHERE attempt_id IN (
85
100
  SELECT attempt_id FROM llm_tps_samples INDEXED BY idx_llm_tps_samples_finished
86
101
  WHERE finished_at_ms < ? ORDER BY finished_at_ms LIMIT 500
@@ -94,8 +109,8 @@ function createObservationAdapter(observation) {
94
109
  return observation.withDatabase((db) => db.transaction(() => {
95
110
  const where = "WHERE finished_at_ms >= ? AND finished_at_ms <= ?";
96
111
  const totalRow = db.query(`SELECT ${metrics} FROM llm_tps_samples ${where}`).get(start, input.asOfMs);
97
- const rows = db.query(`SELECT route_id AS routeId, endpoint_id AS endpointId, ${metrics}
98
- 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);
99
114
  const checked = (row) => {
100
115
  if (safeCount(row.invalidSamples, "invalid sample count") !== 0)
101
116
  throw new RangeError("invalid persisted llm-tps sample");
@@ -113,11 +128,9 @@ function createObservationAdapter(observation) {
113
128
  }));
114
129
  const endpoints = rows.map((row) => {
115
130
  identifier(row.routeId, "routeId");
116
- identifier(row.endpointId, "endpointId");
117
- if (isUrlIdentity(row.endpointId))
118
- throw new TypeError("invalid persisted llm-tps endpoint");
119
- return { routeId: row.routeId, endpointId: row.endpointId, ...checked(row) };
120
- }).sort((a, b) => b.tps - a.tps || compare(a.routeId, b.routeId) || compare(a.endpointId, b.endpointId));
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));
121
134
  const models = db.query(`SELECT route_id AS routeId, model, ${metrics}
122
135
  FROM llm_tps_samples ${where} AND model IS NOT NULL GROUP BY route_id, model`).all(start, input.asOfMs).map((row) => {
123
136
  identifier(row.routeId, "routeId");
@@ -226,4 +239,4 @@ export {
226
239
  control_default as default
227
240
  };
228
241
 
229
- //# debugId=71067BE438FF0C6064756E2164756E21
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 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 endpointId: 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",
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 (sample.model !== null) identifier(sample.model, 'model');\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 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, 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, model)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(sample.attemptId, sample.requestId, sample.routeId, sample.endpointId,\n sample.finishedAtMs, sample.outputTokens, sample.generationDurationMs, sample.outputSource, sample.model);\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 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'); 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 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",
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": ";;AA+CO,IAAM,mBAAmB,KAAK,KAAK,KAAK,KAAK;AAC7C,IAAM,+BAA+B;;;AC5CrC,SAAS,aAAa,CAAC,OAAwB;AAAA,EACpD,OAAO,+BAA+B,KAAK,KAAK;AAAA;AAS3C,SAAS,SAAS,CAAC,OAAgB,MAAsB;AAAA,EAC9D,IAAI,OAAO,UAAU,YAAY,CAAC,OAAO,cAAc,KAAK,KAAK,QAAQ;AAAA,IAAG,MAAM,IAAI,WAAW,mBAAmB,MAAM;AAAA,EAC1H,OAAO;AAAA;AAGF,SAAS,UAAU,CAAC,OAAgB,MAAuC;AAAA,EAChF,IAAI,OAAO,UAAU,YAAY,CAAC,MAAM,KAAK;AAAA,IAAG,MAAM,IAAI,UAAU,mBAAmB,MAAM;AAAA;AAGxF,SAAS,cAAc,CAAC,QAAyB;AAAA,EACtD,WAAW,OAAO,CAAC,aAAa,aAAa,WAAW,YAAY;AAAA,IAAY,WAAW,OAAO,MAAM,GAAG;AAAA,EAC3G,IAAI,OAAO,UAAU;AAAA,IAAM,WAAW,OAAO,OAAO,OAAO;AAAA,EAC3D,IAAI,cAAc,OAAO,UAAU;AAAA,IAAG,MAAM,IAAI,UAAU,iCAAiC;AAAA,EAC3F,UAAU,OAAO,cAAc,cAAc;AAAA,EAC7C,UAAU,OAAO,cAAc,cAAc;AAAA,EAC7C,IAAI,CAAC,OAAO,SAAS,OAAO,oBAAoB,KAAK,OAAO,wBAAwB;AAAA,IAAG,MAAM,IAAI,WAAW,0BAA0B;AAAA,EACtI,IAAI,OAAO,iBAAiB,cAAc,OAAO,iBAAiB;AAAA,IAAa,MAAM,IAAI,UAAU,+BAA+B;AAAA,EAClI,IAAI,CAAC,OAAO,SAAS,OAAO,eAAe,OAAO,uBAAuB,IAAI;AAAA,IAAG,MAAM,IAAI,WAAW,sBAAsB;AAAA;AAoBtH,SAAS,aAAa,CAAC,OAAyB;AAAA,EACrD,IAAI,UAAU;AAAA,IAAM,OAAO;AAAA,EAC3B,IAAI,UAAU;AAAA,IAAO,OAAO;AAAA,EAC5B,IAAI,UAAU;AAAA,IAAO,OAAO;AAAA,EAC5B,MAAM,IAAI,WAAW,uBAAuB;AAAA;AAGvC,SAAS,SAAS,CAAC,KAA4C;AAAA,EACpE,MAAM,eAAe,UAAU,IAAI,cAAc,kBAAkB;AAAA,EACnE,MAAM,cAAc,UAAU,IAAI,aAAa,cAAc;AAAA,EAC7D,MAAM,uBAAuB,UAAU,IAAI,sBAAsB,wBAAwB;AAAA,EACzF,MAAM,WAAW,IAAI;AAAA,EACrB,IAAI,OAAO,aAAa,YAAY,CAAC,OAAO,SAAS,QAAQ,KAAK,WAAW,KACvE,cAAc,KAAK,YAAY,KAAM,uBAAuB,eAC5D,gBAAgB,MAAM,iBAAiB,KAAK,aAAa,KAAK,yBAAyB;AAAA,IAAK,MAAM,IAAI,WAAW,2BAA2B;AAAA,EAClJ,MAAM,MAAM,gBAAgB,IAAI,OAAO,eAAe,WAAW;AAAA,EACjE,IAAI,QAAQ,SAAS,CAAC,OAAO,SAAS,GAAG,KAAK,MAAM;AAAA,IAAI,MAAM,IAAI,WAAW,gCAAgC;AAAA,EAC7G,OAAO,EAAE,KAAK,cAAc,sBAAsB,UAAU,aAAa,qBAAqB;AAAA;;;AC1DzF,SAAS,iBAAiB,CAAC,SAA8C;AAAA,EAC9E,IAAI,OAAO,QAAQ,sBAAsB;AAAA,IAAY,MAAM,IAAI,MAAM,4CAA4C;AAAA,EACjH,MAAM,SAAS,QAAQ,kBAAkB,KAAK,OAAO;AAAA,EACrD,OAAO;AAAA,IACL,cAAc,YAAU,OAAa,gBAAgB,CAAC,MAAM,CAAC;AAAA,IAC7D,YAAY,WAAS,OAAyB,cAAc,CAAC,KAAK,CAAC;AAAA,EACrE;AAAA;AAGF,IAAM,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAUT,SAAS,wBAAwB,CAAC,aAA6D;AAAA,EACpG,OAAO,OAAO,OAAO;AAAA,SACb,aAAY,CAAC,QAAkC;AAAA,MACnD,eAAe,MAAM;AAAA,MACrB,YAAY,aAAa,QAAM;AAAA,QAC7B,GAAG,YAAY,MAAM;AAAA,UACnB,GAAG,MAAM;AAAA;AAAA,+CAE4B,EAAE,IAAI,OAAO,WAAW,OAAO,WAAW,OAAO,SAAS,OAAO,YACpG,OAAO,cAAc,OAAO,cAAc,OAAO,sBAAsB,OAAO,cAAc,OAAO,KAAK;AAAA,UAC1G,GAAG,MAAM;AAAA;AAAA;AAAA,YAGP,EAAE,IAAI,KAAK,IAAI,IAAI,gBAAgB;AAAA,SACtC,EAAE,UAAU;AAAA,OACd;AAAA;AAAA,SAEG,WAAU,CAAC,OAAuE;AAAA,MACtF,UAAU,MAAM,QAAQ,QAAQ;AAAA,MAChC,MAAM,QAAQ,MAAM,SAAS,cAAc,MAAM,KAAK;AAAA,MACtD,OAAO,YAAY,aAAa,QAAM,GAAG,YAAY,MAAM;AAAA,QACzD,MAAM,QAAQ;AAAA,QACd,MAAM,WAAW,GAAG,MAAiD,UAAU,gCAAgC,OAAO,EAAE,IAAI,OAAO,MAAM,MAAM;AAAA,QAC/I,MAAM,OAAO,GAAG,MAAiD,0DAA0D;AAAA,iCAClG,sCAAsC,EAAE,IAAI,OAAO,MAAM,MAAM;AAAA,QACxF,MAAM,UAAU,CAAC,QAAiC;AAAA,UAChD,IAAI,UAAU,IAAI,gBAAgB,sBAAsB,MAAM;AAAA,YAAG,MAAM,IAAI,WAAW,kCAAkC;AAAA,UACxH,OAAO,UAAU,GAAG;AAAA;AAAA,QAEtB,MAAM,QAAQ,QAAQ,QAAQ;AAAA,QAC9B,MAAM,WAAW,MAAM,UAAU,OAAO,QAAS,MAAM,UAAU,QAAQ,UAAY;AAAA,QACrF,MAAM,cAAc,GAAG,MACrB,yDAAyD;AAAA,kCACjC,uCAC1B,EAAE,IAAI,UAAU,OAAO,MAAM,MAAM;AAAA,QACnC,MAAM,UAAU,IAAI,IAAI,YAAY,IAAI,SAAO,CAAC,UAAU,IAAI,QAAQ,QAAQ,GAAG,QAAQ,GAAG,CAAC,CAAC,CAAC;AAAA,QAC/F,MAAM,cAAc,KAAK,MAAM,KAAK,IAAI,GAAG,KAAK,IAAI,QAAQ,GAAG,aAAa,KAAK,MAAM,MAAM,SAAS,QAAQ;AAAA,QAC9G,MAAM,UAAU,MAAM,KAAK,EAAE,QAAQ,aAAa,cAAc,EAAE,GAAG,CAAC,GAAG,WAAW;AAAA,UAClF,gBAAgB,cAAc,SAAS;AAAA,aACnC,QAAQ,IAAI,cAAc,KAAK,KAAK,EAAE,KAAK,MAAM,cAAc,GAAG,sBAAsB,GAAG,aAAa,GAAG,sBAAsB,EAAE;AAAA,QACzI,EAAE;AAAA,QACF,MAAM,YAAY,KAAK,IAAI,SAAO;AAAA,UAChC,WAAW,IAAI,SAAS,SAAS;AAAA,UAAG,WAAW,IAAI,YAAY,YAAY;AAAA,UAC3E,IAAI,cAAc,IAAI,UAAU;AAAA,YAAG,MAAM,IAAI,UAAU,oCAAoC;AAAA,UAC3F,OAAO,EAAE,SAAS,IAAI,SAAS,YAAY,IAAI,eAAe,QAAQ,GAAG,EAAE;AAAA,SAC5E,EAAE,KAAK,CAAC,GAAG,MAAO,EAAE,MAAO,EAAE,OAAS,QAAQ,EAAE,SAAS,EAAE,OAAO,KAAK,QAAQ,EAAE,YAAY,EAAE,UAAU,CAAC;AAAA,QAC3G,MAAM,SAAS,GAAG,MAAiD,sCAAsC;AAAA,iCAChF,sDAAsD,EAAE,IAAI,OAAO,MAAM,MAAM,EAAE,IAAI,SAAO;AAAA,UACnH,WAAW,IAAI,SAAS,SAAS;AAAA,UAAG,WAAW,IAAI,OAAO,OAAO;AAAA,UACjE,OAAO,EAAE,SAAS,IAAI,SAAS,OAAO,IAAI,UAAU,QAAQ,GAAG,EAAE;AAAA,SAClE,EAAE,KAAK,CAAC,GAAG,MAAO,EAAE,MAAO,EAAE,OAAS,QAAQ,EAAE,SAAS,EAAE,OAAO,KAAK,QAAQ,EAAE,OAAO,EAAE,KAAK,CAAC;AAAA,QACjG,OAAO,EAAE,OAAO,MAAM,OAAO,QAAQ,MAAM,QAAQ,OAAO,WAAW,QAAQ,SAAS,qBAAqB,MAAM;AAAA,OAClH,EAAE,CAAC;AAAA;AAAA,EAER,CAAC;AAAA;AAGH,SAAS,OAAO,CAAC,GAAW,GAAmB;AAAA,EAAE,OAAO,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI;AAAA;;;ACjFjF,MAAM,qBAAqB,MAAM;AAAA,EACV;AAAA,EAArB,WAAW,CAAU,MAA0D;AAAA,IAAE,MAAM,IAAI;AAAA,IAAtE;AAAA;AACvB;AAEA,SAAS,SAAY,CAAC,WAAuB,SAA6C;AAAA,EACxF,OAAO,IAAI,QAAW,CAAC,SAAS,WAAW;AAAA,IACzC,MAAM,YAA+B,CAAC;AAAA,IACtC,MAAM,UAAU,MAAM,QAAQ,QAAQ,CAAC,QAAQ,UAAU,OAAO,oBAAoB,SAAS,UAAU,MAAO,CAAC;AAAA,IAC/G,MAAM,OAAO,CAAC,UAAkB;AAAA,MAAE,QAAQ;AAAA,MAAG,OAAO,IAAI,aAAa,QAAQ,IAAI,aAAa,mBAAmB,CAAC;AAAA;AAAA,IAClH,QAAQ,QAAQ,CAAC,QAAQ,UAAU;AAAA,MACjC,MAAM,WAAW,MAAM,KAAK,KAAK;AAAA,MAAG,UAAU,KAAK,QAAQ;AAAA,MAC3D,OAAO,iBAAiB,SAAS,UAAU,EAAE,MAAM,KAAK,CAAC;AAAA,KAC1D;AAAA,IACD,MAAM,YAAY,QAAQ,UAAU,YAAU,OAAO,OAAO;AAAA,IAC5D,IAAI,cAAc;AAAA,MAAI,KAAK,SAAS;AAAA,IACpC,UAAU,KAAK,WAAS;AAAA,MAAE,QAAQ;AAAA,MAAG,QAAQ,KAAK;AAAA,OAAM,WAAS;AAAA,MAAE,QAAQ;AAAA,MAAG,OAAO,KAAK;AAAA,KAAI;AAAA,GAC/F;AAAA;AAGI,SAAS,aAAa,CAAC,MAAyC;AAAA,EACrE,MAAM,SAAS,kBAAkB,KAAK,OAAO;AAAA,EAC7C,MAAM,WAAW,IAAI;AAAA,EACrB,IAAI,WAAW;AAAA,EACf,MAAM,cAAc,CAAC,YAAuC;AAAA,IAC1D,IAAI,YAAY,KAAK,OAAO,WAAW,SAAS,OAAO;AAAA,MAAS,MAAM,IAAI,aAAa,UAAU;AAAA,IACjG,IAAI,SAAS,cAAc,WAAW,SAAS,QAAQ,OAAO;AAAA,MAAS,MAAM,IAAI,aAAa,mBAAmB;AAAA;AAAA,EAEnH,MAAM,UAAU,MAAM;AAAA,IAAE,WAAW;AAAA,IAAM,SAAS,MAAM;AAAA,IAAG,KAAK,OAAO,oBAAoB,SAAS,OAAO;AAAA;AAAA,EAC3G,KAAK,OAAO,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAK,CAAC;AAAA,EAC7D,OAAO;AAAA,IACL,KAAK,CAAC,EAAE,MAAM,UAAU,SAAS,CAAC,KAAK,GAAG,SAAS,iBAAkB,OAAM,CAAC,SAAS;AAAA,MACnF,IAAI;AAAA,QACF,YAAY,OAAO;AAAA,QACnB,IAAI,QAAQ,QAAQ,WAAW;AAAA,UAAO,OAAO,SAAS,KAAK,EAAE,OAAO,qBAAqB,GAAG,EAAE,QAAQ,KAAK,SAAS,EAAE,OAAO,MAAM,EAAE,CAAC;AAAA,QACtI,MAAM,SAAS,IAAI,IAAI,QAAQ,QAAQ,GAAG,EAAE;AAAA,QAC5C,MAAM,QAAQ,OAAO,IAAI,OAAO,KAAK;AAAA,QACrC,IAAI,CAAC,GAAG,OAAO,KAAK,CAAC,EAAE,KAAK,SAAO,QAAQ,OAAO,KAAK,OAAO,OAAO,OAAO,EAAE,SAAS,KAClF,CAAC,CAAC,MAAM,OAAO,KAAK,EAAE,SAAS,KAAK;AAAA,UAAG,MAAM,IAAI,aAAa,eAAe;AAAA,QAClF,MAAM,iBAAiB,YAAY;AAAA,UACjC,MAAM,UAAU,KAAK;AAAA,UACrB,MAAM,SAAS,QAAQ,aAAa,MAAM,QAAQ,WAAoB,4BAA4B,IAAI;AAAA,UACtG,MAAM,QAAQ,WAAW,OAAO,MAAM,QAAQ,IAAa,4BAA4B,IAAI,OAAO,QAAQ,OAAO,QAAQ;AAAA,UACzH,IAAI,UAAU,QAAQ,UAAU,SAAS,UAAU;AAAA,YAAM,MAAM,IAAI,MAAM,0BAA0B;AAAA,UACnG,OAAO,UAAU;AAAA;AAAA,QAEnB,OAAO,OAAO,cAAc,MAAM,UAAU,QAAQ,IAAI;AAAA,UACtD,OAAO,WAAW,EAAE,OAA0B,QAAQ,KAAK,IAAI,EAAE,CAAC;AAAA,UAAG,eAAe;AAAA,QACtF,CAAC,GAAG,CAAC,SAAS,QAAQ,KAAK,QAAQ,QAAQ,QAAQ,QAAQ,eAAe,QAAQ,QAAQ,MAAM,CAAC;AAAA,QACjG,YAAY,OAAO;AAAA,QACnB,OAAO,SAAS,KAAK,KAAK,OAAO,qBAAqB,MAAM,uBAAuB,WAAW,CAAC;AAAA,QAC/F,OAAO,OAAO;AAAA,QACd,MAAM,OAAO,iBAAiB,eAAe,MAAM,OAAO;AAAA,QAC1D,MAAM,SAAS,SAAS,mBAAmB,SAAS,sBAAsB,MAAM,SAAS,aAAa,MAAM;AAAA,QAC5G,OAAO,SAAS,KAAK,EAAE,OAAO,KAAK,GAAG,EAAE,OAAO,CAAC;AAAA;AAAA,MAElD,CAAC;AAAA,IACH,KAAK,CAAC;AAAA,IACN,KAAK,GAAG;AAAA,MAAE,YAAY;AAAA;AAAA,IACtB;AAAA,EACF;AAAA;AAGF,IAAe,oBAAE,cAAc;",
11
- "debugId": "71067BE438FF0C6064756E2164756E21",
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,6 +164,10 @@ 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);
@@ -162,6 +175,8 @@ function validateSample(sample) {
162
175
  identifier(sample.model, "model");
163
176
  if (isUrlIdentity(sample.endpointId))
164
177
  throw new TypeError("llm-tps endpoint must be opaque");
178
+ if (sample.upstreamHost !== null)
179
+ validateUpstreamHost(sample.upstreamHost);
165
180
  safeCount(sample.finishedAtMs, "finishedAtMs");
166
181
  safeCount(sample.outputTokens, "outputTokens");
167
182
  if (!Number.isFinite(sample.generationDurationMs) || sample.generationDurationMs <= 0)
@@ -181,6 +196,7 @@ function sampleFromResult(result, routeId) {
181
196
  routeId: result.routeId,
182
197
  endpointId: endpointIdentity(result.upstreamId),
183
198
  finishedAtMs: result.finishedAtMs,
199
+ upstreamHost: result.upstreamHost ?? null,
184
200
  model: typeof result.model === "string" && result.model.trim() ? result.model : null,
185
201
  outputTokens: output.tokens,
186
202
  generationDurationMs: result.generationDurationMs,
@@ -286,4 +302,4 @@ export {
286
302
  server_default as default
287
303
  };
288
304
 
289
- //# debugId=406B122846E7E3AB64756E2164756E21
305
+ //# debugId=A9BFFA841EA2D74364756E2164756E21