@jeffusion/bungee-core 6.2.1 → 6.3.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.
@@ -17457,6 +17457,16 @@ var LLM_TPS_SAMPLES = {
17457
17457
  }
17458
17458
  };
17459
17459
 
17460
+ // src/migrations/llm-tps-model.ts
17461
+ var LLM_TPS_MODEL = {
17462
+ version: "003",
17463
+ name: "llm_tps_model",
17464
+ up(db) {
17465
+ db.run(`ALTER TABLE llm_tps_samples ADD COLUMN model TEXT
17466
+ CHECK (model IS NULL OR (typeof(model) = 'text' AND length(trim(model)) > 0))`);
17467
+ }
17468
+ };
17469
+
17460
17470
  // src/migrations/schema.ts
17461
17471
  var ACCESS_SCHEMA_STATEMENTS = [
17462
17472
  `CREATE TABLE access_logs (
@@ -18051,7 +18061,7 @@ class MigrationManager {
18051
18061
  }
18052
18062
 
18053
18063
  // src/migrations/index.ts
18054
- var migrations = [ACCESS_BASELINE, LLM_TPS_SAMPLES];
18064
+ var migrations = [ACCESS_BASELINE, LLM_TPS_SAMPLES, LLM_TPS_MODEL];
18055
18065
 
18056
18066
  // src/master-runtime/observability-worker.ts
18057
18067
  function headerEntries(headers) {
@@ -19,6 +19,8 @@ function identifier(value, name) {
19
19
  function validateSample(sample) {
20
20
  for (const key of ["attemptId", "requestId", "routeId", "endpointId"])
21
21
  identifier(sample[key], key);
22
+ if (sample.model !== null)
23
+ identifier(sample.model, "model");
22
24
  if (isUrlIdentity(sample.endpointId))
23
25
  throw new TypeError("llm-tps endpoint must be opaque");
24
26
  safeCount(sample.finishedAtMs, "finishedAtMs");
@@ -77,8 +79,8 @@ function createObservationAdapter(observation) {
77
79
  observation.withDatabase((db) => {
78
80
  db.transaction(() => {
79
81
  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);
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);
82
84
  db.query(`DELETE FROM llm_tps_samples WHERE attempt_id IN (
83
85
  SELECT attempt_id FROM llm_tps_samples INDEXED BY idx_llm_tps_samples_finished
84
86
  WHERE finished_at_ms < ? ORDER BY finished_at_ms LIMIT 500
@@ -100,6 +102,15 @@ function createObservationAdapter(observation) {
100
102
  return aggregate(row);
101
103
  };
102
104
  const total = checked(totalRow);
105
+ const bucketMs = input.range === "1h" ? 60000 : input.range === "12h" ? 1800000 : 3600000;
106
+ const historyRows = db.query(`SELECT CAST(finished_at_ms / ? AS INTEGER) AS bucket, ${metrics}
107
+ FROM llm_tps_samples ${where} GROUP BY bucket ORDER BY bucket`).all(bucketMs, start, input.asOfMs);
108
+ const buckets = new Map(historyRows.map((row) => [safeCount(row.bucket, "bucket"), checked(row)]));
109
+ const firstBucket = Math.floor(Math.max(0, start) / bucketMs), lastBucket = Math.floor(input.asOfMs / bucketMs);
110
+ const history = Array.from({ length: lastBucket - firstBucket + 1 }, (_, index) => ({
111
+ bucketStartMs: (firstBucket + index) * bucketMs,
112
+ ...buckets.get(firstBucket + index) ?? { tps: null, outputTokens: 0, generationDurationMs: 0, sampleCount: 0, estimatedSampleCount: 0 }
113
+ }));
103
114
  const endpoints = rows.map((row) => {
104
115
  identifier(row.routeId, "routeId");
105
116
  identifier(row.endpointId, "endpointId");
@@ -107,7 +118,13 @@ function createObservationAdapter(observation) {
107
118
  throw new TypeError("invalid persisted llm-tps endpoint");
108
119
  return { routeId: row.routeId, endpointId: row.endpointId, ...checked(row) };
109
120
  }).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 };
121
+ const models = db.query(`SELECT route_id AS routeId, model, ${metrics}
122
+ FROM llm_tps_samples ${where} AND model IS NOT NULL GROUP BY route_id, model`).all(start, input.asOfMs).map((row) => {
123
+ identifier(row.routeId, "routeId");
124
+ identifier(row.model, "model");
125
+ return { routeId: row.routeId, model: row.model, ...checked(row) };
126
+ }).sort((a, b) => b.tps - a.tps || compare(a.routeId, b.routeId) || compare(a.model, b.model));
127
+ return { range: input.range, asOfMs: input.asOfMs, total, endpoints, models, history, reportingIncomplete: false };
111
128
  })());
112
129
  }
113
130
  });
@@ -209,4 +226,4 @@ export {
209
226
  control_default as default
210
227
  };
211
228
 
212
- //# debugId=6D69678B8F3EC90164756E2164756E21
229
+ //# debugId=71067BE438FF0C6064756E2164756E21
@@ -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 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",
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": "71067BE438FF0C6064756E2164756E21",
12
12
  "names": []
13
13
  }
@@ -158,6 +158,8 @@ function identifier(value, name) {
158
158
  function validateSample(sample) {
159
159
  for (const key of ["attemptId", "requestId", "routeId", "endpointId"])
160
160
  identifier(sample[key], key);
161
+ if (sample.model !== null)
162
+ identifier(sample.model, "model");
161
163
  if (isUrlIdentity(sample.endpointId))
162
164
  throw new TypeError("llm-tps endpoint must be opaque");
163
165
  safeCount(sample.finishedAtMs, "finishedAtMs");
@@ -179,6 +181,7 @@ function sampleFromResult(result, routeId) {
179
181
  routeId: result.routeId,
180
182
  endpointId: endpointIdentity(result.upstreamId),
181
183
  finishedAtMs: result.finishedAtMs,
184
+ model: typeof result.model === "string" && result.model.trim() ? result.model : null,
182
185
  outputTokens: output.tokens,
183
186
  generationDurationMs: result.generationDurationMs,
184
187
  outputSource: output.source
@@ -283,4 +286,4 @@ export {
283
286
  server_default as default
284
287
  };
285
288
 
286
- //# debugId=32378BFF08D1414E64756E2164756E21
289
+ //# debugId=406B122846E7E3AB64756E2164756E21
@@ -16,12 +16,12 @@
16
16
  "/** Public same-process plugin services. Cross-process callers require the host RPC adapter. */\nimport type { PluginServiceDeclarations, PluginServiceProcess } from './plugin-services/contracts';\nimport { assertSupportedServiceDeclarations, isCrossProcessSelfSnapshot } from './plugin-services/contracts';\nimport { AsyncLocalStorage } from 'node:async_hooks';\nimport { HostRpcAdapter, RpcServiceError, type HostRpcAdapterOptions, type HostRpcLifecycleIdentity, type HostRpcOwnerHandle, type HostRpcInvocationContext, type HostRpcLeaseRequest, type HostRpcLeaseGrant, type HostRpcHandlerFrame } from './plugin-services/host-rpc';\nimport type { RpcCallPurpose, RpcMethodDefinition } from './plugin-services/wire-contract';\nimport type { HostChannelOperation, HostChannelOwnerHandle, PluginChannelCapabilities, PluginChannelOwnerInput, PluginEventServices, PluginSnapshotServices, PluginStreamServices } from './plugin-services/channels';\nexport { defineRpcService } from './plugin-services/wire-contract';\nexport { RpcServiceError } from './plugin-services/host-rpc';\nexport { HostSnapshotStore, type HostSnapshotStoreOptions } from './plugin-services/snapshot-store';\nexport type { AsyncRpcClient, HostRpcCommandCapabilities, RpcCallOptions, RpcServiceErrorCode } from './plugin-services/host-rpc';\nexport type { RpcHandlerMap, RpcHandlerContext } from './plugin-services/rpc-runtime';\nexport type { RpcDataSchema, RpcMethodDefinition, InferRpcData } from './plugin-services/wire-contract';\nexport type { PluginServiceDeclarations, PluginServicePublication, PluginServiceConsumption, PluginServiceProcess, PluginServiceKind, PluginServiceScope } from './plugin-services/contracts';\nexport type {\n PluginStreamServices, PluginSnapshotServices, PluginEventServices, PluginStreamContract, PluginSnapshotContract,\n PluginEventContract, PluginChannelPublication, PluginStreamReadInput, PluginStreamWriteInput, PluginStreamDuplexInput,\n PluginSnapshotView, PluginSnapshotStatus, PluginEventPublisher, PluginChannelCapabilities, HostChannelPublicationView,\n PluginSnapshotVersionSource, HostSnapshotStoreOptionsView,\n} from './plugin-services/channels';\nexport type { PluginChannelDuplexSession, PluginChannelDuplexResult } from './plugin-services/peer-channel-hub';\n\nconst CHANNEL_KINDS = new Set(['events', 'snapshot', 'stream']);\n\nexport interface PluginServices {\n /** Explicit owner-bound background invocation; never inherits a stale/request frame. */\n runBackground?<T>(run: () => T): T;\n onDispose(cleanup: () => void): void;\n publish<T extends object>(serviceId: string, contractVersion: number, implementation: T): void;\n consume<T extends object>(provider: string, serviceId: string, contractVersion: number): Readonly<T>;\n /** Typed asynchronous data-only services, when the host communication runtime is configured. */\n readonly rpc?: Pick<HostRpcOwnerHandle, 'publish' | 'consume'>;\n /** Bounded, cancelable chunked byte channels over the authenticated peer link. */\n readonly stream?: PluginStreamServices;\n /** Transient notifications and durable at-least-once topics. */\n readonly events?: PluginEventServices;\n /** Versioned immutable snapshots with digest verification. */\n readonly snapshot?: PluginSnapshotServices;\n /** Explicit deployment capabilities for the channel lanes (checked before declaring work). */\n readonly channelCapabilities?: PluginChannelCapabilities;\n}\n\nexport interface PluginServiceCommunications extends Omit<HostRpcAdapterOptions, 'process' | 'enterHostHandlerFrame'> {\n /** Real process/owner identity supplied by the runtime, never invented by the plugin. */\n readonly identity: (plugin: string, scope: string) => HostRpcLifecycleIdentity;\n /**\n * Trusted host hook, called for one declared RPC consumption before the\n * adapter resolves its route. It lets the host register the native remote\n * proxy for the consumer's own contract so `resolvePlacement` can select it.\n * A host that does not implement remote routing simply omits it.\n */\n readonly ensureRemoteRoute?: (input: {\n readonly plugin: string;\n readonly scope: string;\n readonly provider: string;\n readonly contract: { readonly id: string; readonly version: number; readonly methods: Record<string, RpcMethodDefinition> };\n }) => void;\n /**\n * Host-owned event/snapshot/stream adapter. It is created by the same process\n * composition that owns the peer link, so channel work rides the one\n * authenticated transport and the canonical owner lifecycle.\n */\n readonly channels?: (input: PluginChannelOwnerInput) => HostChannelOwnerHandle;\n}\nexport interface PluginServiceInvocation {\n readonly purpose: RpcCallPurpose;\n /** Exact pre-admission lease returned by this host; request/attempt require it. */\n readonly lease?: () => void;\n readonly callee?: unknown;\n readonly signal?: AbortSignal;\n readonly deadlineAt?: number;\n}\ninterface LeaseProof { readonly retained: ReadonlySet<Owner>; released: boolean }\ninterface InvocationFrame extends HostRpcInvocationContext { readonly proof?: LeaseProof; readonly callee?: unknown; readonly ownerAuthority?: Owner; readonly peerAuthenticated?: boolean }\ninterface RpcReference { readonly owner: Owner }\n\ntype Owner = {\n plugin: string; scope: string; dependencies: Readonly<Record<string, string>>;\n ready: boolean; retiring: boolean; revoked: boolean; cleaning: boolean; leases: number;\n pending: Set<Promise<unknown>>; waiters: Array<() => void>; cleanups: Set<() => void>;\n consumed: Set<Owner>;\n rpcConsumed: Map<string, RpcReference>;\n rpc?: HostRpcOwnerHandle;\n channels?: HostChannelOwnerHandle;\n};\ntype Publication = { owner: Owner; id: string; version: number; kind: 'local' | 'rpc'; implementation?: object };\n\n/** Cleanup failed after the context was fully revoked; runtime teardown must still run. */\nexport class PluginServiceCleanupError extends AggregateError {\n constructor(failures: readonly unknown[]) { super(failures, 'Plugin service cleanup failed'); }\n}\n\nexport class PluginServiceHost {\n readonly rpc?: HostRpcAdapter;\n private readonly frames = new AsyncLocalStorage<InvocationFrame>();\n private readonly leaseProofs = new WeakMap<() => void, LeaseProof>();\n private readonly liveInvocations = new WeakSet<object>();\n private readonly ownerTokens = new WeakSet<object>();\n private readonly disposals = new WeakMap<Owner, Promise<void>>();\n private readonly communications?: PluginServiceCommunications;\n constructor(readonly process: PluginServiceProcess = 'worker', communications?: PluginServiceCommunications) {\n this.communications = communications;\n if (communications) this.rpc = new HostRpcAdapter({\n ...communications, process,\n enterHostHandlerFrame: (hostFrame, run) => this.runHostHandlerFrame(hostFrame, run),\n });\n }\n private contracts?: ReadonlyMap<string, PluginServiceDeclarations>;\n setDeclarations(contracts: ReadonlyMap<string, PluginServiceDeclarations>): void {\n for (const declarations of contracts.values()) assertSupportedServiceDeclarations(declarations);\n this.contracts = new Map(contracts);\n }\n /** Trusted host view of the declared service graph; never exposed to plugins. */\n serviceDeclarations(): ReadonlyMap<string, PluginServiceDeclarations> { return this.contracts ?? new Map(); }\n private owners = new Map<string, Owner>();\n private contexts = new WeakMap<PluginServices, Owner>();\n private publications = new Map<string, Publication>();\n private key(plugin: string, scope: string): string { return `${plugin}\\0${scope}`; }\n private publicationKey(plugin: string, scope: string, id: string, version: number): string {\n return `${plugin}\\0${scope}\\0${id}\\0${version}`;\n }\n isReady(plugin: string, scope = 'global'): boolean {\n const owner = this.owners.get(this.key(plugin, scope));\n return !!owner?.ready && !owner.retiring && !owner.revoked;\n }\n /** Trusted runtime entry; not exposed through PluginServices. */\n runInInvocation<T>(context: PluginServices, invocation: PluginServiceInvocation, run: () => T): T {\n const owner = this.contexts.get(context);\n if (!owner || owner.revoked || this.owners.get(this.key(owner.plugin, owner.scope)) !== owner) throw new RpcServiceError('unauthorized');\n const proof = invocation.lease === undefined ? undefined : this.leaseProofs.get(invocation.lease);\n if (invocation.purpose === 'request' || invocation.purpose === 'attempt') {\n if (!proof || proof.released || !proof.retained.has(owner)) throw new RpcServiceError('unauthorized');\n } else if (owner.retiring) throw new RpcServiceError('retired');\n const frame: InvocationFrame = Object.freeze({ ...invocation, proof, token: Object.freeze({}) });\n this.liveInvocations.add(frame.token);\n return this.frames.run(frame, () => {\n try {\n const result = run();\n if (result && (typeof result === 'object' || typeof result === 'function') && typeof (result as { then?: unknown }).then === 'function') {\n return Promise.resolve(result).finally(() => this.liveInvocations.delete(frame.token)) as T;\n }\n this.liveInvocations.delete(frame.token);\n return result;\n } catch (error) { this.liveInvocations.delete(frame.token); throw error; }\n });\n }\n /** Broker-only entry after physical-peer and per-call identity projection. */\n runPeerInvocation<T>(purpose: 'request' | 'attempt', callee: unknown, run: () => T): T {\n const frame: InvocationFrame = Object.freeze({purpose, callee, peerAuthenticated: true, token: Object.freeze({})});\n this.liveInvocations.add(frame.token);\n return this.frames.run(frame, () => {\n try {\n const result = run();\n if (result && typeof (result as any).then === 'function') return Promise.resolve(result).finally(() => this.liveInvocations.delete(frame.token)) as T;\n this.liveInvocations.delete(frame.token); return result;\n } catch (error) { this.liveInvocations.delete(frame.token); throw error; }\n });\n }\n /** Read by the trusted callee resolver to carry management/request authority. */\n currentInvocation(): Readonly<HostRpcInvocationContext & { readonly callee?: unknown }> | null {\n const frame = this.frames.getStore();\n return frame === undefined || !this.liveInvocations.has(frame.token) ? null : Object.freeze({ purpose: frame.purpose, token: frame.token, signal: frame.signal, deadlineAt: frame.deadlineAt, callee: frame.callee });\n }\n /**\n * Enters one live host-handler frame bound to the exact published owner, so a\n * native RPC handler shares the SAME trusted invocation store as local\n * dispatch. The frame is live only for the handler's real duration; a stale\n * (settled) frame is therefore never observable.\n */\n private runHostHandlerFrame(host: HostRpcHandlerFrame, run: () => unknown): unknown {\n const owner = host.ownerToken !== null && this.ownerTokens.has(host.ownerToken) ? host.ownerToken as Owner : undefined;\n const frame: InvocationFrame = Object.freeze({\n purpose: host.purpose,\n token: Object.freeze({}),\n ...(host.signal === undefined ? {} : { signal: host.signal }),\n ...(host.deadlineAt === undefined ? {} : { deadlineAt: host.deadlineAt }),\n ...(owner === undefined ? {} : { ownerAuthority: owner }),\n });\n this.liveInvocations.add(frame.token);\n const settle = (): void => { this.liveInvocations.delete(frame.token); };\n return this.frames.run(frame, () => {\n try {\n const result = run();\n if (result !== null && (typeof result === 'object' || typeof result === 'function') && typeof (result as { then?: unknown }).then === 'function') {\n return Promise.resolve(result).finally(settle);\n }\n settle();\n return result;\n } catch (error) { settle(); throw error; }\n });\n }\n createContext(plugin: string, scope = 'global', dependencies: Readonly<Record<string, string>> = {}): PluginServices {\n const key = this.key(plugin, scope);\n if (this.owners.has(key)) throw new Error(`Service context already exists: ${plugin}/${scope}`);\n const declarations = this.contracts?.get(plugin);\n if (!this.rpc && (declarations?.provides?.some(service => service.process === this.process && service.kind === 'rpc')\n || declarations?.consumes?.some(service => service.process === this.process && service.kind === 'rpc'))) {\n throw new RpcServiceError('capability_unavailable');\n }\n const channelProvider = declarations?.provides?.some(service => service.process === this.process && CHANNEL_KINDS.has(service.kind ?? 'local')) === true;\n const channelConsumer = declarations?.consumes?.some(service => service.process === this.process && CHANNEL_KINDS.has(service.kind ?? 'local')) === true;\n // A declared channel provider or required consumer without the host channel\n // adapter is refused here (capability_unavailable), never silently degraded.\n if ((channelProvider || channelConsumer) && this.communications?.channels === undefined) {\n throw new RpcServiceError('capability_unavailable');\n }\n const owner: Owner = { plugin, scope, dependencies: Object.freeze({ ...dependencies }), consumed: new Set(), rpcConsumed: new Map(), ready: false, retiring: false, revoked: false, cleaning: false, leases: 0, pending: new Set(), waiters: [], cleanups: new Set() };\n this.owners.set(key, owner);\n this.ownerTokens.add(owner);\n try {\n if (this.rpc) {\n owner.rpc = this.rpc.createOwner({\n token: owner, plugin, scope, dependencies: owner.dependencies,\n declarations: this.contracts?.get(plugin) ?? {}, lifecycle: this.communications!.identity(plugin, scope),\n getLifecycleState: () => ({ ready: owner.ready, retiring: owner.retiring, revoked: owner.revoked }),\n acquireLease: request => this.acquireRpcLease(owner, request),\n resolveInvocationContext: () => this.currentInvocation(),\n registerPublication: publication => {\n const publicationKey = this.publicationKey(plugin, scope, publication.id, publication.version);\n if (this.publications.has(publicationKey)) throw new RpcServiceError('invalid_registration');\n this.publications.set(publicationKey, { owner, id: publication.id, version: publication.version, kind: 'rpc' });\n },\n registerConsumption: (declaration, providerToken) => {\n const referenceKey = this.publicationKey(declaration.plugin, 'global', declaration.id, declaration.version);\n if (providerToken) {\n const provider = [...this.owners.values()].find(candidate => candidate === providerToken);\n if (!provider || provider.revoked) throw new RpcServiceError('unavailable');\n owner.rpcConsumed.set(referenceKey, { owner: provider });\n } else owner.rpcConsumed.delete(referenceKey);\n },\n });\n }\n if (this.communications?.channels !== undefined && (channelProvider || channelConsumer)) {\n owner.channels = this.communications.channels({\n plugin, scope, declarations: this.contracts?.get(plugin) ?? {},\n dependencies: owner.dependencies,\n lifecycle: this.communications.identity(plugin, scope),\n getLifecycleState: () => ({ ready: owner.ready, retiring: owner.retiring, revoked: owner.revoked }),\n trackPending: (pending: Promise<unknown>) => {\n owner.pending.add(pending);\n void pending.finally(() => { owner.pending.delete(pending); this.notify(owner); }).catch(() => {});\n },\n beginOperation: (options) => this.beginChannelOperation(plugin, scope, options),\n });\n }\n } catch (error) { this.owners.delete(key); throw error; }\n const assertActive = (target = owner) => {\n if (target.revoked || (target.retiring && !target.cleaning && !owner.cleaning && target.leases === 0 && target.pending.size === 0)) throw new Error(`Plugin service handle revoked: ${target.plugin}`);\n };\n const signals = new WeakMap<AbortSignal, WeakMap<Owner, AbortSignal>>();\n const track = <T>(pending: Promise<T>, provider: Owner): Promise<T> => {\n owner.pending.add(pending); provider.pending.add(pending);\n void pending.finally(() => {\n owner.pending.delete(pending); provider.pending.delete(pending);\n this.notify(owner); this.notify(provider);\n }).catch(() => {});\n return pending;\n };\n // These handles stay in-process. RPC retains its separate data-only wire contract.\n const wrap = (value: unknown, provider: Owner, receiver?: object, argument = false): any => {\n const active = (): void => { assertActive(); assertActive(provider); };\n if (typeof value === 'function') return (...args: unknown[]) => {\n active();\n return wrap(Reflect.apply(value, receiver, args.map(arg => wrap(arg, provider, undefined, !argument))), provider, undefined, argument);\n };\n if (value instanceof AbortSignal) {\n active();\n let projections = signals.get(value);\n if (!projections) signals.set(value, projections = new WeakMap());\n const existing = projections.get(provider);\n if (existing) return existing;\n const controller = new AbortController();\n const detach = (): void => {\n value.removeEventListener('abort', abort);\n owner.cleanups.delete(revoke); provider.cleanups.delete(revoke);\n };\n const abort = (): void => { detach(); controller.abort(value.reason); };\n const revoke = (): void => { detach(); controller.abort(new Error('Plugin service handle revoked')); };\n projections.set(provider, controller.signal);\n if (value.aborted) abort();\n else {\n value.addEventListener('abort', abort, { once: true });\n owner.cleanups.add(revoke); provider.cleanups.add(revoke);\n }\n return controller.signal;\n }\n if (value instanceof Promise) {\n active();\n return track(value.then(output => { active(); return wrap(output, provider, undefined, argument); }), provider);\n }\n if (value instanceof Uint8Array) return new Uint8Array(value);\n if (value instanceof ArrayBuffer) return ArrayBuffer.prototype.slice.call(value,0);\n if (value && typeof value === 'object' && typeof (value as AsyncIterable<unknown>)[Symbol.asyncIterator] === 'function') {\n return Object.freeze({ [Symbol.asyncIterator]: () => {\n active();\n const iterator = (value as AsyncIterable<unknown>)[Symbol.asyncIterator]();\n const invoke = (method: 'next' | 'return' | 'throw', args: unknown[]): Promise<unknown> => {\n active();\n const operation = iterator[method];\n if (!operation) return method === 'throw' ? Promise.reject(args[0]) : Promise.resolve({ done: true, value: args[0] });\n return wrap(Promise.resolve(Reflect.apply(operation, iterator, args.map(arg => wrap(arg, provider, undefined, !argument)))), provider, undefined, argument);\n };\n const facade = Object.freeze({\n next: (...args: unknown[]) => invoke('next', args),\n return: (...args: unknown[]) => invoke('return', args),\n throw: (...args: unknown[]) => invoke('throw', args),\n [Symbol.asyncIterator]: () => { active(); return facade; },\n });\n return facade;\n } });\n }\n if (Array.isArray(value)) return Object.freeze(value.map(item => wrap(item, provider, undefined, argument)));\n if (value && typeof value === 'object') {\n if (argument) {\n const prototype = Object.getPrototypeOf(value);\n if (prototype !== Object.prototype && prototype !== null) throw new TypeError('Unsupported plugin service argument handle');\n return Object.freeze(Object.fromEntries(Object.entries(value).map(([key, item]) => [key, wrap(item, provider, value, true)])));\n }\n // A fresh facade prevents access to implementation identity or mutable provider fields.\n const facade: Record<string, unknown> = {};\n for (const key of Object.keys(value)) Object.defineProperty(facade, key, {\n enumerable: true, get: () => { active(); return wrap((value as any)[key], provider, value as object); },\n });\n return Object.freeze(facade);\n }\n return value;\n };\n const consume = <T extends object>(provider: string, id: string, version: number): Readonly<T> => {\n assertActive();\n const declaration = this.contracts?.get(plugin)?.consumes?.find(value => value.plugin === provider && value.id === id && value.version === version && value.process === this.process);\n if (this.contracts && !declaration) throw new Error(`Undeclared plugin service consumption: ${plugin} -> ${provider}/${id}`);\n if (declaration?.kind && declaration.kind !== 'local') throw new Error('Non-local services require their asynchronous host adapter');\n if (provider === plugin || !Object.hasOwn(owner.dependencies, provider)) throw new Error(`Undeclared plugin service dependency: ${plugin} -> ${provider}`);\n const targetScope = 'global';\n const publication = this.publications.get(this.publicationKey(provider, targetScope, id, version));\n if (!publication || !publication.owner.ready || publication.owner.retiring || publication.owner.revoked) {\n if ([...this.publications.values()].some(value => value.owner.plugin === provider && value.owner.scope === targetScope && value.id === id && value.version !== version)) throw new Error(`Plugin service contract mismatch: ${provider}/${id}`);\n throw new Error(`Plugin service not ready: ${provider}/${id}`);\n }\n owner.consumed.add(publication.owner);\n return wrap(publication.implementation, publication.owner);\n };\n const context: PluginServices = Object.freeze({\n runBackground: <T>(run: () => T): T => {\n if (this.contexts.get(context) !== owner || this.owners.get(key) !== owner\n || !owner.ready || owner.retiring || owner.revoked) throw new RpcServiceError('retired');\n const release = this.acquireLease(plugin, scope);\n try {\n const result = this.runInInvocation(context, { purpose: 'background' }, run);\n if (result && (typeof result === 'object' || typeof result === 'function') && typeof (result as { then?: unknown }).then === 'function') {\n return Promise.resolve(result).finally(release) as T;\n }\n release();\n return result;\n } catch (error) { release(); throw error; }\n },\n onDispose: (cleanup: () => void) => { assertActive(); owner.cleanups.add(cleanup); },\n publish: <T extends object>(id: string, version: number, implementation: T) => {\n assertActive();\n const candidates = this.contracts?.get(plugin)?.provides?.filter(value => value.id === id && value.version === version && value.process === this.process);\n const declaration = candidates?.[0];\n if (this.contracts && !declaration) throw new Error(`Undeclared plugin service publication: ${plugin}/${id}`);\n if (declaration?.kind && declaration.kind !== 'local') throw new Error('Non-local services require their asynchronous host adapter');\n if (owner.scope !== 'global') throw new Error('Only global providers may publish global services');\n if (!Number.isSafeInteger(version) || version < 1) throw new Error('Invalid service contract version');\n const publicationKey = this.publicationKey(plugin, owner.scope, id, version);\n if (this.publications.has(publicationKey)) throw new Error(`Service already published: ${plugin}/${id}`);\n this.publications.set(publicationKey, { owner, id, version, implementation, kind: 'local' });\n },\n consume: <T extends object>(provider: string, id: string, version: number): Readonly<T> => consume<T>(provider, id, version),\n rpc: owner.rpc === undefined ? undefined : (() => {\n const ownerHandle = owner.rpc!;\n const prepare = (provider: string, contract: { readonly id: string; readonly version: number; readonly methods: Record<string, RpcMethodDefinition> }): void => {\n try { this.communications?.ensureRemoteRoute?.({ plugin, scope: owner.scope, provider, contract }); }\n catch { /* an unroutable remote declaration stays unavailable, never guessed */ }\n };\n return Object.freeze({\n publish: ownerHandle.publish,\n consume: <const M extends Record<string, RpcMethodDefinition>>(provider: string, contract: { readonly id: string; readonly version: number; readonly methods: M }) => {\n prepare(provider, contract);\n return ownerHandle.consume(provider, contract);\n },\n });\n })(),\n stream: owner.channels?.stream,\n events: owner.channels?.events,\n snapshot: owner.channels?.snapshot,\n channelCapabilities: owner.channels?.capabilities,\n });\n this.contexts.set(context, owner);\n return context;\n }\n markReady(plugin: string, scope = 'global', expectedContext?: PluginServices): void {\n const owner = this.requireOwner(plugin, scope, expectedContext);\n if (owner.retiring || owner.revoked) throw new Error(`Plugin service disposed: ${plugin}`);\n for (const publication of this.contracts?.get(plugin)?.provides ?? []) {\n if (publication.kind === 'rpc' && publication.process === this.process && (publication.scope ?? 'global') === (scope === 'global' ? 'global' : 'binding')\n && this.publications.get(this.publicationKey(plugin, scope, publication.id, publication.version))?.kind !== 'rpc') throw new RpcServiceError('not_ready');\n }\n owner.rpc?.markReady();\n owner.channels?.markReady();\n owner.ready = true;\n }\n retire(plugin: string, scope = 'global', expectedContext?: PluginServices): void {\n const owner = this.requireOwner(plugin, scope, expectedContext);\n owner.retiring = true;\n owner.rpc?.retire();\n try { owner.channels?.retire(); } catch { /* host-owned */ }\n }\n /**\n * Host-only: retires admission for EVERY live owner at once. It is wired to\n * the real drain admission point, so after it no new bootstrap/background work\n * is admitted while every already-accepted lease, result and terminal keeps\n * draining normally (nothing is revoked here).\n */\n retireAll(): void {\n for (const owner of this.owners.values()) {\n if (owner.revoked) continue;\n owner.retiring = true;\n try { owner.rpc?.retire(); } catch { /* host-owned */ }\n try { owner.channels?.retire(); } catch { /* host-owned */ }\n }\n }\n /**\n * Host-only: begins ONE channel operation for an owner. It applies the exact\n * same invocation-frame rules as an RPC call (management only from a\n * management frame, request/attempt only with a live retained lease proof, no\n * new work while retiring, bootstrap-only before ready) and holds a real\n * owner lease for the operation's whole lifetime, so owner disposal can never\n * race a live lane task. `null` refuses the operation.\n */\n beginChannelOperation(\n plugin: string,\n scope = 'global',\n incoming?: {\n readonly purpose?: RpcCallPurpose;\n readonly deadlineAt?: number;\n readonly signal?: AbortSignal;\n readonly authenticated?: boolean;\n /**\n * Host-managed background work: the operation runs in an EXPLICIT\n * independent background context and deliberately ignores any frame the\n * caller happens to be inside (typically a closed bootstrap/init closure).\n */\n readonly background?: boolean;\n },\n ): HostChannelOperation | null {\n const owner = this.owners.get(this.key(plugin, scope));\n if (!owner || owner.revoked) return null;\n if (this.owners.get(this.key(plugin, scope)) !== owner) return null;\n const stored = this.frames.getStore();\n let frame: InvocationFrame | undefined;\n if (incoming?.background === true) {\n // Explicit Host background context: no inherited purpose/deadline/signal.\n frame = undefined;\n } else {\n // A stale (already settled) frame is REFUSED: a finished invocation must\n // never be reused as fresh authority nor laundered into another purpose.\n // Legitimate background work either runs outside a frame or asks for the\n // explicit Host background context above.\n if (stored !== undefined && !this.liveInvocations.has(stored.token)) return null;\n frame = stored;\n }\n const leaseBound = frame !== undefined\n && (frame.purpose === 'request' || frame.purpose === 'attempt')\n && ((frame.proof !== undefined && !frame.proof.released && frame.proof.retained.has(owner))\n || frame.ownerAuthority === owner);\n if (frame !== undefined && (frame.purpose === 'request' || frame.purpose === 'attempt') && !leaseBound) return null;\n // A peer-authenticated inbound channel request carries the caller host's real\n // purpose, but only request/attempt/background may cross that boundary: a peer\n // may never claim management or bootstrap authority for a provider lease.\n const peerPurpose = frame === undefined && incoming?.authenticated === true\n && (incoming.purpose === 'request' || incoming.purpose === 'attempt' || incoming.purpose === 'background')\n ? incoming.purpose\n : undefined;\n const purpose = frame?.purpose ?? peerPurpose ?? 'background';\n const requestBound = leaseBound || peerPurpose === 'request' || peerPurpose === 'attempt';\n // Retirement refuses NEW work; already-accepted request/attempt work finishes.\n if (owner.retiring && !requestBound) return null;\n if (!owner.ready && !requestBound && purpose !== 'bootstrap') return null;\n const deadlineAt = minDefined(frame?.deadlineAt, incoming?.deadlineAt);\n const signal = combineAbortSignals(frame?.signal, incoming?.signal);\n owner.leases += 1;\n let released = false;\n const release = () => { if (released) return; released = true; owner.leases -= 1; this.notify(owner); };\n return Object.freeze({\n purpose,\n ...(signal === undefined ? {} : { signal }),\n ...(deadlineAt === undefined ? {} : { deadlineAt }),\n release,\n });\n }\n\n /** Capture before admission; release only after all consumers finish old-request processing. */\n acquireLease(plugin: string, scope = 'global'): () => void {\n const owner = this.requireOwner(plugin, scope);\n if (!owner.ready || owner.retiring || owner.revoked) throw new Error(`Plugin service not ready: ${plugin}`);\n const retained = new Set<Owner>();\n const retain = (target: Owner): void => {\n if (retained.has(target)) return;\n if (!target.ready || target.retiring || target.revoked) throw new Error(`Plugin service not ready: ${target.plugin}`);\n retained.add(target);\n for (const provider of this.dependencyOwners(target)) retain(provider);\n for (const provider of target.consumed) if (provider.ready && !provider.retiring && !provider.revoked) retain(provider);\n // A declared in-process channel provider is an exact required\n // reference too, and its ready provider owner enters the lease proof so a\n // request/attempt frame can authorize real channel work on it.\n for (const provider of this.channelDependencyOwners(target)) {\n if (provider.ready && !provider.retiring && !provider.revoked) retain(provider);\n }\n for (const reference of target.rpcConsumed.values()) retain(reference.owner);\n };\n retain(owner);\n for (const target of retained) target.leases++;\n let released = false;\n const proof: LeaseProof = { retained, released: false };\n const release = () => { if (released) return; released = true; proof.released = true; for (const target of retained) { target.leases--; this.notify(target); } };\n this.leaseProofs.set(release, proof);\n return release;\n }\n\n references(plugin: string): ReadonlyArray<{ plugin: string; scope: string; leases: number }> {\n return [...this.owners.values()].filter(o => !o.revoked && (o.plugin === plugin || [...this.dependencyOwners(o), ...this.consumedOwners(o)].some(provider => provider.plugin === plugin)))\n .map(o => Object.freeze({ plugin: o.plugin, scope: o.scope, leases: o.leases }));\n }\n /** Stop admission immediately, preserve acquired handles until leases and calls drain. */\n dispose(plugin: string, scope = 'global', expectedContext?: PluginServices): Promise<void> {\n let owner: Owner;\n try { owner = this.requireOwner(plugin, scope, expectedContext); } catch (error) { return Promise.reject(error); }\n const existing = this.disposals.get(owner);\n if (existing) return existing;\n let resolve!: () => void;\n let reject!: (error: unknown) => void;\n const task = new Promise<void>((accept, fail) => { resolve = accept; reject = fail; });\n // Install before cleanup can synchronously re-enter disposal.\n this.disposals.set(owner, task);\n void this.disposeOwner(owner).then(() => {\n this.disposals.delete(owner); resolve();\n }, error => { this.disposals.delete(owner); reject(error); });\n return task;\n }\n private async disposeOwner(owner: Owner): Promise<void> {\n const { plugin, scope } = owner;\n const hasPublications = [...this.publications.values()].some(publication => publication.owner === owner);\n if (hasPublications) {\n const dependents = [...this.owners.values()].filter(candidate => candidate !== owner && !candidate.revoked && (this.dependencyOwners(candidate).includes(owner) || this.consumedOwners(candidate).includes(owner)));\n if (dependents.length) throw new Error(`Plugin service provider is referenced: ${plugin} <- ${dependents.map(candidate => `${candidate.plugin}/${candidate.scope}`).join(', ')}`);\n }\n owner.retiring = true;\n owner.rpc?.retire();\n try { owner.channels?.retire(); } catch { /* host-owned */ }\n if (owner.leases || owner.pending.size) await new Promise<void>(resolve => owner.waiters.push(resolve));\n if (owner.channels) {\n try { await owner.channels.dispose(); } catch { /* provider release is best-effort here */ }\n }\n if (owner.rpc) {\n const result = await owner.rpc.dispose();\n if (!result.drained) throw new RpcServiceError('timeout');\n }\n // Cleanup may invoke its cached unsubscribe handle after drain, before final revocation.\n owner.cleaning = true;\n const failures: unknown[] = [];\n try {\n for (const cleanup of owner.cleanups) { try { cleanup(); } catch (error) { failures.push(error); } }\n } finally {\n owner.cleanups.clear(); owner.consumed.clear(); owner.rpcConsumed.clear(); owner.cleaning = false;\n owner.revoked = true; owner.ready = false;\n for (const [key, publication] of this.publications) if (publication.owner === owner) this.publications.delete(key);\n if (this.owners.get(this.key(plugin, scope)) === owner) this.owners.delete(this.key(plugin, scope));\n }\n if (failures.length) throw new PluginServiceCleanupError(failures);\n }\n /** Actual local provider owners, not activation edges to another process/binding. */\n private dependencyOwners(owner: Owner): Owner[] {\n const result = new Set<Owner>();\n for (const dependency of Object.keys(owner.dependencies)) {\n const declarations = this.contracts?.get(owner.plugin)?.consumes?.filter(service => service.plugin === dependency && service.process === this.process);\n if (declarations?.length) {\n for (const service of declarations) {\n // RPC providers are retained through `rpcConsumed`/`acquireRpcLease`,\n // never here. A local channel provider (events/snapshot/stream) IS a\n // real provider owner, so a request/attempt lease must retain it too,\n // or `beginChannelOperation` would refuse exact in-process channel work.\n if ((service.kind ?? 'local') === 'rpc') continue;\n const provider = this.owners.get(this.key(dependency, 'global'));\n if (provider) result.add(provider);\n }\n } else {\n const provider = this.owners.get(this.key(dependency, 'global'));\n if (provider) result.add(provider);\n }\n }\n return [...result];\n }\n /** Local providers of this owner's declared channel consumptions. */\n private channelDependencyOwners(owner: Owner): Owner[] {\n const result = new Set<Owner>();\n for (const service of this.contracts?.get(owner.plugin)?.consumes ?? []) {\n if (service.process !== this.process) continue;\n if (!CHANNEL_KINDS.has(service.kind ?? 'local')) continue;\n if (typeof service.plugin !== 'string' || !service.plugin) continue;\n if (isCrossProcessSelfSnapshot(owner.plugin, service, this.contracts?.get(owner.plugin) ?? {})) continue;\n if (!Object.hasOwn(owner.dependencies, service.plugin)) continue;\n const provider = this.owners.get(this.key(service.plugin, 'global'));\n if (provider) result.add(provider);\n }\n return [...result];\n }\n /** Consumers release all actual references before providers. */\n disposalOrder(): ReadonlyArray<{ plugin: string; scope: string }> {\n const visited = new Set<Owner>(), visiting = new Set<Owner>(), ordered: Owner[] = [];\n const visit = (owner: Owner): void => {\n if (visited.has(owner)) return;\n if (visiting.has(owner)) throw new Error('Plugin service reference cycle during disposal');\n visiting.add(owner);\n for (const provider of [...this.dependencyOwners(owner), ...this.consumedOwners(owner)]) if (!provider.revoked) visit(provider);\n visiting.delete(owner); visited.add(owner); ordered.push(owner);\n };\n for (const owner of this.owners.values()) visit(owner);\n return ordered.reverse().map(owner => Object.freeze({ plugin: owner.plugin, scope: owner.scope }));\n }\n private requireOwner(plugin: string, scope: string, expectedContext?: PluginServices): Owner {\n const owner = this.owners.get(this.key(plugin, scope));\n if (!owner) throw new Error(`Missing plugin service context: ${plugin}/${scope}`);\n if (expectedContext && this.contexts.get(expectedContext) !== owner) throw new Error(`Stale plugin service context: ${plugin}/${scope}`);\n return owner;\n }\n private notify(owner: Owner): void {\n if (!owner.leases && !owner.pending.size) for (const resolve of owner.waiters.splice(0)) resolve();\n }\n private consumedOwners(owner: Owner): Owner[] {\n return [...new Set([...owner.consumed, ...[...owner.rpcConsumed.values()].map(reference => reference.owner)])];\n }\n /** Each real RPC task borrows the canonical owner until actual business work settles. */\n private acquireRpcLease(owner: Owner, request: HostRpcLeaseRequest): HostRpcLeaseGrant {\n if (owner.revoked || this.owners.get(this.key(owner.plugin, owner.scope)) !== owner) throw new RpcServiceError('revoked');\n const frame = this.frames.getStore();\n if (frame && !this.liveInvocations.has(frame.token)) throw new RpcServiceError('unauthorized');\n if (request.purpose === 'management' && frame?.purpose !== 'management') throw new RpcServiceError('unauthorized');\n const existing = (request.purpose === 'request' || request.purpose === 'attempt')\n && frame?.purpose === request.purpose && ((frame.proof !== undefined && !frame.proof.released && frame.proof.retained.has(owner)) || (request.role === 'callee' && frame.peerAuthenticated === true));\n if ((request.purpose === 'request' || request.purpose === 'attempt') && !existing) throw new RpcServiceError('unauthorized');\n if (owner.retiring && !existing) throw new RpcServiceError('retired');\n if (!owner.ready && request.purpose !== 'bootstrap') throw new RpcServiceError('not_ready');\n owner.leases++;\n let released = false;\n return { allowRetired: existing, release: () => { if (released) return; released = true; owner.leases--; this.notify(owner); } };\n }\n}\n\n/** Smallest defined deadline, or undefined when neither side has one. */\nfunction minDefined(left: number | undefined, right: number | undefined): number | undefined {\n if (left === undefined) return right;\n if (right === undefined) return left;\n return Math.min(left, right);\n}\n\n/** Combines local and peer-provided abort signals without dropping either. */\nfunction combineAbortSignals(...signals: ReadonlyArray<AbortSignal | undefined>): AbortSignal | undefined {\n const present = signals.filter((signal): signal is AbortSignal => signal !== undefined);\n if (present.length === 0) return undefined;\n if (present.length === 1) return present[0];\n const any = (AbortSignal as unknown as { any?: (list: readonly AbortSignal[]) => AbortSignal }).any;\n if (typeof any === 'function') return any.call(AbortSignal, present);\n const controller = new AbortController();\n const abort = (signal: AbortSignal) => { if (!controller.signal.aborted) controller.abort(signal.reason); };\n for (const signal of present) {\n if (signal.aborted) { abort(signal); break; }\n signal.addEventListener('abort', () => abort(signal), { once: true });\n }\n return controller.signal;\n}\n\nexport const TOKEN_METERING_SERVICE_ID = 'token-metering.v1';\nexport const TOKEN_METERING_CONTRACT_VERSION = 1;\nexport type TokenMeteringSource = 'official' | 'estimated' | 'partial' | 'none';\nexport type TokenMeteringAuthority = 'official' | 'local' | 'heuristic' | 'partial' | 'none';\nexport interface TokenMeteringResult {\n readonly requestId: string; readonly attemptId: string; readonly routeId: string; readonly upstreamId: string;\n readonly keyId?: string | null;\n readonly provider: string; readonly model?: string; readonly pricingProvider?: string;\n /** Complete observable stream: first output to protocol completion (or normal EOF), in ms.\n * Requires successful Host completion, no protocol failure/error or incomplete observation, and positive time.\n * Independent of accounting outcome (which may reject a missing protocol terminal). */\n readonly generationDurationMs?: number;\n /** Output measured for that successful generation, before accounting-only usage degradation. */\n readonly generationOutput?: Readonly<{ tokens: number; source: 'official' | 'estimated' }>;\n readonly inputTokens?: number; readonly outputTokens?: number; readonly cacheReadTokens?: number; readonly cacheWriteTokens?: number;\n readonly inputSource: TokenMeteringSource; readonly outputSource: TokenMeteringSource;\n readonly inputAuthority: TokenMeteringAuthority; readonly outputAuthority: TokenMeteringAuthority;\n readonly complete: boolean; readonly observationIncomplete: boolean;\n readonly outcome: 'completed' | 'failed' | 'aborted'; readonly finishedAtMs: number; readonly settlementVersion: number;\n}\nexport interface TokenMeteringSubscription {\n /** Omit for degradable all-request reporting; required consumers bind their admitted request. */\n readonly requestId?: string;\n readonly required?: boolean;\n readonly onResult: (result: TokenMeteringResult) => void | Promise<void>;\n readonly onFailure?: (error: unknown, result: TokenMeteringResult) => void | Promise<void>;\n}\nexport interface TokenMeteringService {\n /** Final completion boundary: await this request's required settlement/failure callbacks before releasing its host lease. */\n drainRequest(requestId: string): Promise<void>;\n subscribe(subscription: TokenMeteringSubscription): () => void;\n prepareAttempt(input: Readonly<{ requestId: string; attemptId: string; routeId: string; upstreamId: string; url: string; body: unknown }>): Readonly<{ supported: boolean; provider?: string; model?: string; pricingProvider?: string }>;\n /** Consumer calls before request admission to pin demand; framework init also captures report subscriptions. */\n prepareRequest(requestId: string): boolean;\n}\n\nexport const TOKEN_PRICING_SERVICE_ID = 'token-stats.pricing.v1';\nexport const TOKEN_PRICING_CONTRACT_VERSION = 1;\n/** Costs are quantized to safe integer nanoUSD (1 USD = 1e9 nanoUSD); unknown remains null. */\nexport interface TokenPricingService {\n canPrice(input: Readonly<{ model?: string; pricingProvider?: string }>): Promise<boolean>;\n price(result: TokenMeteringResult): Promise<Readonly<{ costUsd: number | null; costNanoUsd: number | null }>>;\n}\n",
17
17
  "/** Declarative repair request; the host owns the next upstream attempt. */\nconst retryActionBrand=Symbol.for('@jeffusion/bungee/request-retry-action');\nexport class RequestRetryAction extends Error {\n readonly body:unknown;\n constructor(body: unknown, readonly reason: string) {\n super('gateway request repair retry');\n this.name = 'RequestRetryAction';\n Object.defineProperty(this,retryActionBrand,{value:true});\n Object.defineProperty(this,'body',{value:body,enumerable:false});\n }\n /** Plugins and host may bundle the SDK independently in the same process. */\n static [Symbol.hasInstance](value:unknown):boolean {\n return value instanceof Error && (value as unknown as Record<symbol,unknown>)[retryActionBrand]===true;\n }\n}\n",
18
18
  "import type { TokenStatsRange } from './plugin.types';\n\nconst HOUR = 3_600_000;\nexport const TOKEN_STATS_RETENTION_MS = 31 * 24 * HOUR;\nexport const TOKEN_STATS_RANGES = ['1h', '12h', '24h', '1d', '7d', '30d', 'day', 'week', 'month'] as const;\n\n/** Recent page ranges are consecutive elapsed hours; calendar ranges use local dates. */\nexport function tokenStatsWindow(range: TokenStatsRange, asOfMs: number, timeZone = 'UTC') {\n if (range === '1h' || range === '12h' || range === '24h') {\n const duration = { '1h': HOUR, '12h': 12 * HOUR, '24h': 24 * HOUR };\n const bucketMs = { '1h': 300_000, '12h': HOUR, '24h': 2 * HOUR }[range];\n return { startMs: asOfMs - duration[range], endMs: asOfMs, bucketMs };\n }\n if (range === '1d' || range === '7d' || range === '30d') {\n const days = { '1d': 1, '7d': 7, '30d': 30 }[range];\n const bucketMs = range === '1d' ? HOUR : 24 * HOUR;\n const startMs = asOfMs - days * 24 * HOUR;\n const bucketStarts = Array.from({ length: range === '1d' ? 24 : days }, (_, i) => startMs + i * bucketMs);\n return { startMs, endMs: asOfMs, bucketMs, bucketStarts };\n }\n const format = new Intl.DateTimeFormat('en-US', {\n timeZone, year: 'numeric', month: 'numeric', day: 'numeric',\n hour: 'numeric', minute: 'numeric', second: 'numeric', hourCycle: 'h23',\n });\n const parts = (ms: number) => Object.fromEntries(format.formatToParts(ms)\n .filter(part => part.type !== 'literal').map(part => [part.type, Number(part.value)]));\n const today = parts(asOfMs);\n const day = Date.UTC(today.year!, today.month! - 1, today.day!);\n // Resolve each local midnight separately so DST days can contain 23 or 25 hours.\n const midnight = (date: number) => {\n let candidate = date;\n for (let i = 0; i < 4; i++) {\n const p = parts(candidate);\n const local = Date.UTC(p.year!, p.month! - 1, p.day!, p.hour!, p.minute!, p.second!);\n const next = candidate + date - local;\n if (next === candidate) return candidate;\n candidate = next;\n }\n return candidate;\n };\n if (range === 'day') {\n const startMs = midnight(day);\n const bucketEndMs = midnight(day + 24 * HOUR);\n const bucketStarts = Array.from({ length: Math.ceil((bucketEndMs - startMs) / HOUR) }, (_, i) => startMs + i * HOUR);\n return { startMs, endMs: asOfMs, bucketMs: HOUR, bucketStarts, bucketEndMs };\n }\n const firstDay = range === 'month' ? Date.UTC(today.year!, today.month! - 1, 1)\n : day - ((new Date(day).getUTCDay() + 6) % 7) * 24 * HOUR;\n const nextPeriod = range === 'month' ? Date.UTC(today.year!, today.month!, 1) : firstDay + 7 * 24 * HOUR;\n const days = (nextPeriod - firstDay) / (24 * HOUR);\n const bucketStarts = Array.from({ length: days }, (_, i) => midnight(firstDay + i * 24 * HOUR));\n return { startMs: bucketStarts[0]!, endMs: asOfMs, bucketMs: 24 * HOUR, bucketStarts, bucketEndMs: midnight(nextPeriod) };\n}\n",
19
- "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",
20
- "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",
21
- "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",
19
+ "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",
20
+ "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",
21
+ "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",
22
22
  "import { definePlugin, TOKEN_METERING_CONTRACT_VERSION, TOKEN_METERING_SERVICE_ID,\n type Plugin, type PluginHooks, type PluginInitContext, type TokenMeteringService } from '@jeffusion/bungee-core/plugin';\nimport { TPS_REPORTING_INCOMPLETE_KEY } from '../contract';\nimport { sampleFromResult } from './metrics';\nimport { observationClient } from './storage';\n\nconst MAX_PENDING = 256;\n\nexport const LlmTpsPlugin = definePlugin(class implements Plugin {\n static readonly name = 'llm-tps';\n static readonly version = '1.0.0';\n private unsubscribe?: () => void;\n private accepting = false;\n private pending = 0;\n private writes: Promise<void> = Promise.resolve();\n private reportingWrite?: Promise<void>;\n private reportingPersisted = false;\n\n bodyRequirements() { return { request: 'none' } as const; }\n register(_hooks: PluginHooks): void {}\n\n async init(context: PluginInitContext): Promise<void> {\n if (context.scope?.type !== 'route' || !context.scope.routeId?.trim()) throw new Error('llm-tps requires a route binding');\n if (!context.services) throw new Error('llm-tps requires token-metering service');\n const client = observationClient(context.storage);\n const storage = context.storage.uncached?.() ?? context.storage;\n const metering = context.services.consume<TokenMeteringService>('token-metering', TOKEN_METERING_SERVICE_ID, TOKEN_METERING_CONTRACT_VERSION);\n const routeId = context.scope.routeId;\n const incomplete = () => {\n if (this.reportingPersisted || this.reportingWrite) return;\n this.reportingWrite = Promise.resolve().then(() => storage.set(TPS_REPORTING_INCOMPLETE_KEY, true)).then(() => {\n this.reportingPersisted = true;\n }).catch(() => {\n try { context.logger.error('llm-tps could not persist incomplete reporting status'); } catch {}\n }).finally(() => { this.reportingWrite = undefined; });\n };\n this.accepting = true;\n this.unsubscribe = metering.subscribe({\n onResult: result => {\n if (!this.accepting || result.routeId !== routeId) return;\n try {\n if (result.observationIncomplete && result.generationDurationMs === undefined) incomplete();\n const sample = sampleFromResult(result, routeId);\n if (!sample) return;\n if (this.pending >= MAX_PENDING) { incomplete(); return; }\n this.pending++;\n this.writes = this.writes.then(() => client.recordSample(sample)).catch(incomplete).finally(() => { this.pending--; });\n } catch { incomplete(); }\n },\n onFailure: (_error, result) => { if (this.accepting && result.routeId === routeId) incomplete(); },\n });\n context.services.onDispose(() => this.stopAccepting());\n }\n\n private stopAccepting(): void {\n this.accepting = false;\n const unsubscribe = this.unsubscribe;\n this.unsubscribe = undefined;\n unsubscribe?.();\n }\n\n async onDestroy(): Promise<void> {\n this.stopAccepting();\n await this.writes;\n await this.reportingWrite;\n }\n});\n\nexport default LlmTpsPlugin;\n"
23
23
  ],
24
- "mappings": "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",
25
- "debugId": "32378BFF08D1414E64756E2164756E21",
24
+ "mappings": "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",
25
+ "debugId": "406B122846E7E3AB64756E2164756E21",
26
26
  "names": []
27
27
  }
@@ -31,6 +31,10 @@
31
31
  {
32
32
  "name": "TpsEndpoints",
33
33
  "entry": "ui/TpsEndpoints.svelte"
34
+ },
35
+ {
36
+ "name": "TpsModels",
37
+ "entry": "ui/TpsModels.svelte"
34
38
  }
35
39
  ]
36
40
  },
@@ -62,6 +66,12 @@
62
66
  "title": "widgets.endpoints.title",
63
67
  "size": "full",
64
68
  "component": "TpsEndpoints"
69
+ },
70
+ {
71
+ "id": "llm-tps-models",
72
+ "title": "widgets.models.title",
73
+ "size": "full",
74
+ "component": "TpsModels"
65
75
  }
66
76
  ],
67
77
  "api": [
@@ -88,28 +98,32 @@
88
98
  "plugin.description": "Measure streamed output generation speed for routes with this plugin bound.",
89
99
  "widgets.overview.title": "LLM Generation Speed",
90
100
  "widgets.endpoints.title": "Endpoint Generation Speed",
101
+ "widgets.models.title": "Model Generation Speed",
91
102
  "ui.refresh": "Refresh",
92
103
  "ui.loading": "Loading generation speed",
93
104
  "ui.loadFailed": "Unable to load generation speed. Refresh to retry.",
94
- "ui.noData": "No valid streaming samples in this range",
95
- "ui.weightedHint": "Time-weighted generation speed: output tokens divided by total time from first valid output to generation end.",
105
+ "ui.noData": "No data in this range",
106
+ "ui.sampleCount": "Samples",
107
+ "ui.estimatedCount": "Estimated",
96
108
  "ui.samples": "Valid samples: {count} · estimated: {estimated}",
97
109
  "ui.ranking": "Generation speed by route and endpoint, fastest first",
98
- "ui.incomplete": "Reporting is incomplete. Some samples may be missing."
110
+ "ui.modelRanking": "Generation speed by route and model, fastest first"
99
111
  },
100
112
  "zh-CN": {
101
113
  "metadata.name": "LLM 生成速度",
102
114
  "plugin.description": "统计绑定此插件的路由上的流式输出生成速度。",
103
115
  "widgets.overview.title": "LLM 生成速度",
104
116
  "widgets.endpoints.title": "端点生成速度",
117
+ "widgets.models.title": "模型生成速度",
105
118
  "ui.refresh": "刷新",
106
119
  "ui.loading": "正在加载生成速度",
107
120
  "ui.loadFailed": "生成速度加载失败,请刷新重试。",
108
- "ui.noData": "所选范围暂无有效流式样本",
109
- "ui.weightedHint": "耗时加权平均生成速度:输出 Token 总量除以从首个有效输出到生成结束的总耗时。",
121
+ "ui.noData": "所选范围暂无数据",
122
+ "ui.sampleCount": "样本",
123
+ "ui.estimatedCount": "估算",
110
124
  "ui.samples": "有效样本:{count} · 其中估算:{estimated}",
111
125
  "ui.ranking": "按路由与端点统计生成速度,由高到低排列",
112
- "ui.incomplete": "上报不完整,部分样本可能缺失。"
126
+ "ui.modelRanking": "按路由与模型统计生成速度,由高到低排列"
113
127
  }
114
128
  }
115
129
  }
@@ -0,0 +1,9 @@
1
+ <script lang="ts">
2
+ import type { TimeRange } from '@bungee/plugin-sdk';
3
+ import type { NativeWidgetHeaderChange } from '$components/native-widgets/widget-header';
4
+ import TpsWidget from './TpsWidget.svelte';
5
+ let { pluginName = 'llm-tps', selectedRange = '24h', onHeaderChange }:
6
+ { pluginName?: string; selectedRange?: TimeRange; onHeaderChange?: NativeWidgetHeaderChange } = $props();
7
+ </script>
8
+
9
+ <TpsWidget {pluginName} {selectedRange} {onHeaderChange} mode="models" />