@relayplane/proxy 1.2.0 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/README.md +94 -28
  2. package/dist/circuit-breaker.d.ts +39 -0
  3. package/dist/circuit-breaker.d.ts.map +1 -0
  4. package/dist/circuit-breaker.js +97 -0
  5. package/dist/circuit-breaker.js.map +1 -0
  6. package/dist/cli.d.ts +1 -1
  7. package/dist/cli.js +20 -12
  8. package/dist/cli.js.map +1 -1
  9. package/dist/health.d.ts +16 -0
  10. package/dist/health.d.ts.map +1 -0
  11. package/dist/health.js +80 -0
  12. package/dist/health.js.map +1 -0
  13. package/dist/index.d.ts +12 -0
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +19 -1
  16. package/dist/index.js.map +1 -1
  17. package/dist/launcher.d.ts +10 -0
  18. package/dist/launcher.d.ts.map +1 -0
  19. package/dist/launcher.js +73 -0
  20. package/dist/launcher.js.map +1 -0
  21. package/dist/logger.d.ts +11 -0
  22. package/dist/logger.d.ts.map +1 -0
  23. package/dist/logger.js +13 -0
  24. package/dist/logger.js.map +1 -0
  25. package/dist/middleware.d.ts +60 -0
  26. package/dist/middleware.d.ts.map +1 -0
  27. package/dist/middleware.js +254 -0
  28. package/dist/middleware.js.map +1 -0
  29. package/dist/process-manager.d.ts +61 -0
  30. package/dist/process-manager.d.ts.map +1 -0
  31. package/dist/process-manager.js +173 -0
  32. package/dist/process-manager.js.map +1 -0
  33. package/dist/relay-config.d.ts +19 -0
  34. package/dist/relay-config.d.ts.map +1 -0
  35. package/dist/relay-config.js +32 -0
  36. package/dist/relay-config.js.map +1 -0
  37. package/dist/standalone-proxy.d.ts.map +1 -1
  38. package/dist/standalone-proxy.js +98 -7
  39. package/dist/standalone-proxy.js.map +1 -1
  40. package/dist/stats.d.ts +49 -0
  41. package/dist/stats.d.ts.map +1 -0
  42. package/dist/stats.js +72 -0
  43. package/dist/stats.js.map +1 -0
  44. package/dist/status.d.ts +46 -0
  45. package/dist/status.d.ts.map +1 -0
  46. package/dist/status.js +78 -0
  47. package/dist/status.js.map +1 -0
  48. package/dist/telemetry.d.ts +6 -0
  49. package/dist/telemetry.d.ts.map +1 -1
  50. package/dist/telemetry.js +14 -2
  51. package/dist/telemetry.js.map +1 -1
  52. package/package.json +1 -1
@@ -0,0 +1,173 @@
1
+ "use strict";
2
+ /**
3
+ * Process Manager for RelayPlane Proxy
4
+ *
5
+ * Spawns the proxy as a child process, monitors health,
6
+ * handles crashes with exponential backoff restarts.
7
+ *
8
+ * @packageDocumentation
9
+ */
10
+ var __importDefault = (this && this.__importDefault) || function (mod) {
11
+ return (mod && mod.__esModule) ? mod : { "default": mod };
12
+ };
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.ProcessManager = void 0;
15
+ const node_child_process_1 = require("node:child_process");
16
+ const node_path_1 = __importDefault(require("node:path"));
17
+ const node_events_1 = require("node:events");
18
+ class ProcessManager extends node_events_1.EventEmitter {
19
+ child = null;
20
+ command;
21
+ args;
22
+ env;
23
+ restartDelayMs;
24
+ maxRestartDelayMs;
25
+ maxRestartAttempts;
26
+ restartWindowMs;
27
+ circuitBreaker;
28
+ restartTimestamps = [];
29
+ currentDelay;
30
+ restartTimer = null;
31
+ stopping = false;
32
+ started = false;
33
+ constructor(opts = {}) {
34
+ super();
35
+ this.command = opts.command ?? process.execPath;
36
+ this.args = opts.args ?? [node_path_1.default.join(__dirname, 'launcher.js')];
37
+ this.env = opts.env ?? {};
38
+ this.restartDelayMs = opts.restartDelayMs ?? 60_000;
39
+ this.maxRestartDelayMs = opts.maxRestartDelayMs ?? 300_000;
40
+ this.maxRestartAttempts = opts.maxRestartAttempts ?? 5;
41
+ this.restartWindowMs = opts.restartWindowMs ?? 600_000;
42
+ this.circuitBreaker = opts.circuitBreaker;
43
+ this.currentDelay = this.restartDelayMs;
44
+ }
45
+ /** Start the proxy child process. */
46
+ start() {
47
+ if (this.child)
48
+ return;
49
+ this.stopping = false;
50
+ this.started = true;
51
+ this.spawnChild();
52
+ }
53
+ /** Stop the proxy child process. */
54
+ stop() {
55
+ this.stopping = true;
56
+ this.started = false;
57
+ this.clearRestartTimer();
58
+ if (this.child) {
59
+ this.child.removeAllListeners();
60
+ this.child.kill('SIGTERM');
61
+ this.child = null;
62
+ }
63
+ this.emit('stopped');
64
+ }
65
+ /** Restart the proxy (stop then start). */
66
+ restart() {
67
+ this.stop();
68
+ this.start();
69
+ }
70
+ /** Whether the child process is currently running. */
71
+ isRunning() {
72
+ return this.child !== null && this.child.exitCode === null && !this.child.killed;
73
+ }
74
+ /** Get the PID of the child process, or null if not running. */
75
+ getPid() {
76
+ return this.child?.pid ?? null;
77
+ }
78
+ /** Clean up resources. */
79
+ destroy() {
80
+ this.stop();
81
+ this.removeAllListeners();
82
+ }
83
+ spawnChild() {
84
+ try {
85
+ const child = (0, node_child_process_1.spawn)(this.command, this.args, {
86
+ stdio: ['ignore', 'pipe', 'pipe'],
87
+ env: { ...process.env, ...this.env },
88
+ });
89
+ // Handle spawn error (command not found, etc.)
90
+ child.on('error', (err) => {
91
+ console.error(`[relayplane] Failed to spawn proxy: ${err.message}`);
92
+ this.child = null;
93
+ this.circuitBreaker?.recordFailure();
94
+ this.emit('error', err);
95
+ if (!this.stopping) {
96
+ this.scheduleRestart();
97
+ }
98
+ });
99
+ // Pipe stdout with prefix
100
+ child.stdout?.on('data', (chunk) => {
101
+ const lines = chunk.toString().split('\n').filter(Boolean);
102
+ for (const line of lines) {
103
+ console.log(`[relayplane] ${line}`);
104
+ }
105
+ });
106
+ // Pipe stderr with prefix
107
+ child.stderr?.on('data', (chunk) => {
108
+ const lines = chunk.toString().split('\n').filter(Boolean);
109
+ for (const line of lines) {
110
+ console.error(`[relayplane] ${line}`);
111
+ }
112
+ });
113
+ // Handle exit
114
+ child.on('exit', (code, signal) => {
115
+ this.child = null;
116
+ if (this.stopping)
117
+ return;
118
+ console.error(`[relayplane] Proxy exited (code=${code}, signal=${signal})`);
119
+ // Mark circuit breaker OPEN on crash
120
+ this.circuitBreaker?.recordFailure();
121
+ this.emit('crash', { code, signal });
122
+ this.scheduleRestart();
123
+ });
124
+ this.child = child;
125
+ this.emit('started', { pid: child.pid });
126
+ }
127
+ catch (err) {
128
+ const error = err instanceof Error ? err : new Error(String(err));
129
+ console.error(`[relayplane] Failed to spawn proxy: ${error.message}`);
130
+ this.child = null;
131
+ this.circuitBreaker?.recordFailure();
132
+ this.emit('error', error);
133
+ if (!this.stopping) {
134
+ this.scheduleRestart();
135
+ }
136
+ }
137
+ }
138
+ scheduleRestart() {
139
+ if (this.stopping)
140
+ return;
141
+ const now = Date.now();
142
+ // Prune old timestamps outside the window
143
+ this.restartTimestamps = this.restartTimestamps.filter((t) => now - t < this.restartWindowMs);
144
+ if (this.restartTimestamps.length >= this.maxRestartAttempts) {
145
+ console.error(`[relayplane] Max restart attempts (${this.maxRestartAttempts}) reached within ${this.restartWindowMs / 1000}s window. Giving up.`);
146
+ this.emit('maxRestartsExceeded');
147
+ return;
148
+ }
149
+ this.restartTimestamps.push(now);
150
+ console.log(`[relayplane] Scheduling restart in ${this.currentDelay / 1000}s`);
151
+ this.restartTimer = setTimeout(() => {
152
+ this.restartTimer = null;
153
+ if (!this.stopping) {
154
+ this.spawnChild();
155
+ }
156
+ }, this.currentDelay);
157
+ if (this.restartTimer && typeof this.restartTimer === 'object' && 'unref' in this.restartTimer) {
158
+ this.restartTimer.unref();
159
+ }
160
+ // Exponential backoff
161
+ this.currentDelay = Math.min(this.currentDelay * 2, this.maxRestartDelayMs);
162
+ }
163
+ clearRestartTimer() {
164
+ if (this.restartTimer) {
165
+ clearTimeout(this.restartTimer);
166
+ this.restartTimer = null;
167
+ }
168
+ // Reset backoff on manual stop/restart
169
+ this.currentDelay = this.restartDelayMs;
170
+ }
171
+ }
172
+ exports.ProcessManager = ProcessManager;
173
+ //# sourceMappingURL=process-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"process-manager.js","sourceRoot":"","sources":["../src/process-manager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * RelayPlane integration configuration types.
3
+ * @packageDocumentation
4
+ */
5
+ export interface RelayPlaneConfig {
6
+ enabled: boolean;
7
+ /** Proxy URL (default: http://127.0.0.1:4100) */
8
+ proxyUrl?: string;
9
+ circuitBreaker?: {
10
+ failureThreshold?: number;
11
+ resetTimeoutMs?: number;
12
+ requestTimeoutMs?: number;
13
+ };
14
+ /** Auto-start proxy process (Phase 2, default: true) */
15
+ autoStart?: boolean;
16
+ }
17
+ export declare const DEFAULT_RELAY_CONFIG: Required<RelayPlaneConfig>;
18
+ export declare function resolveConfig(partial?: Partial<RelayPlaneConfig>): Required<RelayPlaneConfig>;
19
+ //# sourceMappingURL=relay-config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relay-config.d.ts","sourceRoot":"","sources":["../src/relay-config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,iDAAiD;IACjD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE;QACf,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAC1B,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,gBAAgB,CAAC,EAAE,MAAM,CAAC;KAC3B,CAAC;IACF,wDAAwD;IACxD,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,eAAO,MAAM,oBAAoB,EAAE,QAAQ,CAAC,gBAAgB,CAS3D,CAAC;AAEF,wBAAgB,aAAa,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC,GAAG,QAAQ,CAAC,gBAAgB,CAAC,CAW7F"}
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ /**
3
+ * RelayPlane integration configuration types.
4
+ * @packageDocumentation
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.DEFAULT_RELAY_CONFIG = void 0;
8
+ exports.resolveConfig = resolveConfig;
9
+ exports.DEFAULT_RELAY_CONFIG = {
10
+ enabled: false,
11
+ proxyUrl: 'http://127.0.0.1:4100',
12
+ circuitBreaker: {
13
+ failureThreshold: 3,
14
+ resetTimeoutMs: 30_000,
15
+ requestTimeoutMs: 3_000,
16
+ },
17
+ autoStart: true,
18
+ };
19
+ function resolveConfig(partial) {
20
+ if (!partial)
21
+ return { ...exports.DEFAULT_RELAY_CONFIG };
22
+ return {
23
+ enabled: partial.enabled ?? exports.DEFAULT_RELAY_CONFIG.enabled,
24
+ proxyUrl: partial.proxyUrl ?? exports.DEFAULT_RELAY_CONFIG.proxyUrl,
25
+ circuitBreaker: {
26
+ ...exports.DEFAULT_RELAY_CONFIG.circuitBreaker,
27
+ ...partial.circuitBreaker,
28
+ },
29
+ autoStart: partial.autoStart ?? exports.DEFAULT_RELAY_CONFIG.autoStart,
30
+ };
31
+ }
32
+ //# sourceMappingURL=relay-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relay-config.js","sourceRoot":"","sources":["../src/relay-config.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AA0BH,sCAWC;AAtBY,QAAA,oBAAoB,GAA+B;IAC9D,OAAO,EAAE,KAAK;IACd,QAAQ,EAAE,uBAAuB;IACjC,cAAc,EAAE;QACd,gBAAgB,EAAE,CAAC;QACnB,cAAc,EAAE,MAAM;QACtB,gBAAgB,EAAE,KAAK;KACxB;IACD,SAAS,EAAE,IAAI;CAChB,CAAC;AAEF,SAAgB,aAAa,CAAC,OAAmC;IAC/D,IAAI,CAAC,OAAO;QAAE,OAAO,EAAE,GAAG,4BAAoB,EAAE,CAAC;IACjD,OAAO;QACL,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,4BAAoB,CAAC,OAAO;QACxD,QAAQ,EAAE,OAAO,CAAC,QAAQ,IAAI,4BAAoB,CAAC,QAAQ;QAC3D,cAAc,EAAE;YACd,GAAG,4BAAoB,CAAC,cAAc;YACtC,GAAG,OAAO,CAAC,cAAc;SAC1B;QACD,SAAS,EAAE,OAAO,CAAC,SAAS,IAAI,4BAAoB,CAAC,SAAS;KAC/D,CAAC;AACJ,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"standalone-proxy.d.ts","sourceRoot":"","sources":["../src/standalone-proxy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAKlC,OAAO,KAAK,EAAE,QAAQ,EAAY,MAAM,kBAAkB,CAAC;AAI3D;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,eAAO,MAAM,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAqB9D,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE;IAAE,QAAQ,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAa/E,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAGrD,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE;IAAE,QAAQ,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAQ/E,CAAC;AA+BF;;GAEG;AACH,wBAAgB,sBAAsB,IAAI,MAAM,EAAE,CAWjD;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAMvD;AAkBD,KAAK,aAAa,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;AAEjD,UAAU,WAAW;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,aAAa,GAAG,IAAI,CAAC;CAC9B;AAcD,UAAU,aAAa;IACrB,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,UAAU,EAAE,aAAa,GAAG,SAAS,GAAG,OAAO,CAAC;IAChD,cAAc,EAAE,MAAM,CAAC;CACxB;AAmBD,KAAK,UAAU,GAAG,QAAQ,GAAG,UAAU,GAAG,SAAS,CAAC;AA6EpD;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;;;OAKG;IACH,aAAa,CAAC,EAAE,aAAa,GAAG,KAAK,GAAG,MAAM,CAAC;CAChD;AAqOD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,WAAW,CAe3D;AAuDD,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,KAAK,CAAC;IAAE,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,GAAG,UAAU,CAiBrF;AAED,wBAAgB,cAAc,CAAC,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,CAAC,YAAY,CAAC,GAAG,OAAO,CAIlG;AAs0CD;;GAEG;AACH,wBAAsB,UAAU,CAAC,MAAM,GAAE,WAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAk7B/E"}
1
+ {"version":3,"file":"standalone-proxy.d.ts","sourceRoot":"","sources":["../src/standalone-proxy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAKlC,OAAO,KAAK,EAAE,QAAQ,EAAY,MAAM,kBAAkB,CAAC;AAI3D;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,eAAO,MAAM,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAqB9D,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE;IAAE,QAAQ,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAa/E,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAGrD,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE;IAAE,QAAQ,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAQ/E,CAAC;AAiCF;;GAEG;AACH,wBAAgB,sBAAsB,IAAI,MAAM,EAAE,CAWjD;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAMvD;AAkBD,KAAK,aAAa,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;AAEjD,UAAU,WAAW;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,aAAa,GAAG,IAAI,CAAC;CAC9B;AAcD,UAAU,aAAa;IACrB,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,UAAU,EAAE,aAAa,GAAG,SAAS,GAAG,OAAO,CAAC;IAChD,cAAc,EAAE,MAAM,CAAC;CACxB;AAmBD,KAAK,UAAU,GAAG,QAAQ,GAAG,UAAU,GAAG,SAAS,CAAC;AA6EpD;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;;;OAKG;IACH,aAAa,CAAC,EAAE,aAAa,GAAG,KAAK,GAAG,MAAM,CAAC;CAChD;AAqOD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,WAAW,CAe3D;AAuDD,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,KAAK,CAAC;IAAE,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,GAAG,UAAU,CAiBrF;AAED,wBAAgB,cAAc,CAAC,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,CAAC,YAAY,CAAC,GAAG,OAAO,CAIlG;AAo1CD;;GAEG;AACH,wBAAsB,UAAU,CAAC,MAAM,GAAE,WAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAk9B/E"}
@@ -134,7 +134,7 @@ exports.SMART_ALIASES = {
134
134
  * Send a telemetry event to the cloud (anonymous or authenticated).
135
135
  * Non-blocking — errors are silently swallowed.
136
136
  */
137
- function sendCloudTelemetry(taskType, model, tokensIn, tokensOut, latencyMs, success, costUsd) {
137
+ function sendCloudTelemetry(taskType, model, tokensIn, tokensOut, latencyMs, success, costUsd, requestedModel) {
138
138
  try {
139
139
  const cost = costUsd ?? (0, telemetry_js_1.estimateCost)(model, tokensIn, tokensOut);
140
140
  (0, telemetry_js_1.recordTelemetry)({
@@ -145,6 +145,7 @@ function sendCloudTelemetry(taskType, model, tokensIn, tokensOut, latencyMs, suc
145
145
  latency_ms: Math.round(latencyMs),
146
146
  success,
147
147
  cost_usd: cost,
148
+ requested_model: requestedModel,
148
149
  });
149
150
  }
150
151
  catch {
@@ -1146,10 +1147,17 @@ function convertAnthropicStreamEvent(eventType, eventData, messageId, model, too
1146
1147
  };
1147
1148
  switch (eventType) {
1148
1149
  case 'message_start': {
1149
- // First chunk: include role
1150
+ // First chunk: include role and input token usage
1150
1151
  const msg = eventData['message'];
1151
1152
  baseChunk.id = msg?.['id'] || messageId;
1152
1153
  choice.delta = { role: 'assistant', content: '' };
1154
+ // Pass through input token count from message_start
1155
+ const msgUsage = msg?.['usage'];
1156
+ if (msgUsage) {
1157
+ baseChunk['usage'] = {
1158
+ prompt_tokens: msgUsage['input_tokens'] ?? 0,
1159
+ };
1160
+ }
1153
1161
  return `data: ${JSON.stringify(baseChunk)}\n\n`;
1154
1162
  }
1155
1163
  case 'content_block_start': {
@@ -1204,9 +1212,10 @@ function convertAnthropicStreamEvent(eventType, eventData, messageId, model, too
1204
1212
  return null;
1205
1213
  }
1206
1214
  case 'message_delta': {
1207
- // Final chunk with stop reason
1215
+ // Final chunk with stop reason and usage
1208
1216
  const delta = eventData['delta'];
1209
1217
  const stopReason = delta?.['stop_reason'];
1218
+ const usage = eventData['usage'];
1210
1219
  if (stopReason === 'tool_use') {
1211
1220
  choice.finish_reason = 'tool_calls';
1212
1221
  }
@@ -1217,6 +1226,12 @@ function convertAnthropicStreamEvent(eventType, eventData, messageId, model, too
1217
1226
  choice.finish_reason = stopReason || 'stop';
1218
1227
  }
1219
1228
  choice.delta = {};
1229
+ // Pass through usage data (output_tokens from message_delta)
1230
+ if (usage) {
1231
+ baseChunk['usage'] = {
1232
+ completion_tokens: usage['output_tokens'] ?? 0,
1233
+ };
1234
+ }
1220
1235
  return `data: ${JSON.stringify(baseChunk)}\n\n`;
1221
1236
  }
1222
1237
  case 'message_stop': {
@@ -1975,20 +1990,53 @@ async function startProxy(config = {}) {
1975
1990
  'Connection': 'keep-alive',
1976
1991
  });
1977
1992
  const reader = providerResponse.body?.getReader();
1993
+ let streamTokensIn = 0;
1994
+ let streamTokensOut = 0;
1978
1995
  if (reader) {
1979
1996
  const decoder = new TextDecoder();
1997
+ let sseBuffer = '';
1980
1998
  try {
1981
1999
  while (true) {
1982
2000
  const { done, value } = await reader.read();
1983
2001
  if (done)
1984
2002
  break;
1985
- res.write(decoder.decode(value, { stream: true }));
2003
+ const chunk = decoder.decode(value, { stream: true });
2004
+ res.write(chunk);
2005
+ // Parse SSE events to extract usage from message_delta / message_stop
2006
+ sseBuffer += chunk;
2007
+ const lines = sseBuffer.split('\n');
2008
+ sseBuffer = lines.pop() ?? '';
2009
+ for (const line of lines) {
2010
+ if (line.startsWith('data: ')) {
2011
+ try {
2012
+ const evt = JSON.parse(line.slice(6));
2013
+ // Anthropic: message_delta has usage.output_tokens
2014
+ if (evt.type === 'message_delta' && evt.usage) {
2015
+ streamTokensOut = evt.usage.output_tokens ?? streamTokensOut;
2016
+ }
2017
+ // Anthropic: message_start has usage.input_tokens
2018
+ if (evt.type === 'message_start' && evt.message?.usage) {
2019
+ streamTokensIn = evt.message.usage.input_tokens ?? streamTokensIn;
2020
+ }
2021
+ // OpenAI format: choices with usage
2022
+ if (evt.usage) {
2023
+ streamTokensIn = evt.usage.prompt_tokens ?? evt.usage.input_tokens ?? streamTokensIn;
2024
+ streamTokensOut = evt.usage.completion_tokens ?? evt.usage.output_tokens ?? streamTokensOut;
2025
+ }
2026
+ }
2027
+ catch {
2028
+ // not JSON, skip
2029
+ }
2030
+ }
2031
+ }
1986
2032
  }
1987
2033
  }
1988
2034
  finally {
1989
2035
  reader.releaseLock();
1990
2036
  }
1991
2037
  }
2038
+ // Store streaming token counts so telemetry can use them
2039
+ nativeResponseData = { usage: { input_tokens: streamTokensIn, output_tokens: streamTokensOut } };
1992
2040
  res.end();
1993
2041
  }
1994
2042
  else {
@@ -2010,7 +2058,7 @@ async function startProxy(config = {}) {
2010
2058
  const usage = nativeResponseData?.usage;
2011
2059
  const tokensIn = usage?.input_tokens ?? usage?.prompt_tokens ?? 0;
2012
2060
  const tokensOut = usage?.output_tokens ?? usage?.completion_tokens ?? 0;
2013
- sendCloudTelemetry(taskType, targetModel || requestedModel, tokensIn, tokensOut, durationMs, true);
2061
+ sendCloudTelemetry(taskType, targetModel || requestedModel, tokensIn, tokensOut, durationMs, true, undefined, originalModel ?? undefined);
2014
2062
  }
2015
2063
  }
2016
2064
  catch (err) {
@@ -2354,7 +2402,7 @@ async function startProxy(config = {}) {
2354
2402
  const usage = responseData?.usage;
2355
2403
  const tokensIn = usage?.input_tokens ?? usage?.prompt_tokens ?? 0;
2356
2404
  const tokensOut = usage?.output_tokens ?? usage?.completion_tokens ?? 0;
2357
- sendCloudTelemetry(taskType, cascadeResult.model, tokensIn, tokensOut, durationMs, true);
2405
+ sendCloudTelemetry(taskType, cascadeResult.model, tokensIn, tokensOut, durationMs, true, undefined, originalRequestedModel ?? undefined);
2358
2406
  }
2359
2407
  res.writeHead(200, { 'Content-Type': 'application/json' });
2360
2408
  res.end(JSON.stringify(responseData));
@@ -2487,6 +2535,9 @@ async function handleStreamingRequest(res, request, targetProvider, targetModel,
2487
2535
  'Cache-Control': 'no-cache',
2488
2536
  'Connection': 'keep-alive',
2489
2537
  });
2538
+ // Track token usage from streaming events
2539
+ let streamTokensIn = 0;
2540
+ let streamTokensOut = 0;
2490
2541
  try {
2491
2542
  // Stream the response based on provider format
2492
2543
  switch (targetProvider) {
@@ -2494,18 +2545,58 @@ async function handleStreamingRequest(res, request, targetProvider, targetModel,
2494
2545
  // Convert Anthropic stream to OpenAI format
2495
2546
  for await (const chunk of convertAnthropicStream(providerResponse, targetModel)) {
2496
2547
  res.write(chunk);
2548
+ // Parse OpenAI-format chunks for usage (emitted at end of stream)
2549
+ try {
2550
+ const lines = chunk.split('\n');
2551
+ for (const line of lines) {
2552
+ if (line.startsWith('data: ') && line !== 'data: [DONE]') {
2553
+ const evt = JSON.parse(line.slice(6));
2554
+ if (evt.usage) {
2555
+ streamTokensIn = evt.usage.prompt_tokens ?? streamTokensIn;
2556
+ streamTokensOut = evt.usage.completion_tokens ?? streamTokensOut;
2557
+ }
2558
+ }
2559
+ }
2560
+ }
2561
+ catch { /* skip parse errors */ }
2497
2562
  }
2498
2563
  break;
2499
2564
  case 'google':
2500
2565
  // Convert Gemini stream to OpenAI format
2501
2566
  for await (const chunk of convertGeminiStream(providerResponse, targetModel)) {
2502
2567
  res.write(chunk);
2568
+ try {
2569
+ const lines = chunk.split('\n');
2570
+ for (const line of lines) {
2571
+ if (line.startsWith('data: ') && line !== 'data: [DONE]') {
2572
+ const evt = JSON.parse(line.slice(6));
2573
+ if (evt.usage) {
2574
+ streamTokensIn = evt.usage.prompt_tokens ?? streamTokensIn;
2575
+ streamTokensOut = evt.usage.completion_tokens ?? streamTokensOut;
2576
+ }
2577
+ }
2578
+ }
2579
+ }
2580
+ catch { /* skip parse errors */ }
2503
2581
  }
2504
2582
  break;
2505
2583
  default:
2506
2584
  // xAI, Moonshot, OpenAI all use OpenAI-compatible streaming format
2507
2585
  for await (const chunk of pipeOpenAIStream(providerResponse)) {
2508
2586
  res.write(chunk);
2587
+ try {
2588
+ const lines = chunk.split('\n');
2589
+ for (const line of lines) {
2590
+ if (line.startsWith('data: ') && line !== 'data: [DONE]') {
2591
+ const evt = JSON.parse(line.slice(6));
2592
+ if (evt.usage) {
2593
+ streamTokensIn = evt.usage.prompt_tokens ?? streamTokensIn;
2594
+ streamTokensOut = evt.usage.completion_tokens ?? streamTokensOut;
2595
+ }
2596
+ }
2597
+ }
2598
+ }
2599
+ catch { /* skip parse errors */ }
2509
2600
  }
2510
2601
  }
2511
2602
  }
@@ -2530,7 +2621,7 @@ async function handleStreamingRequest(res, request, targetProvider, targetModel,
2530
2621
  .catch((err) => {
2531
2622
  log(`Failed to record run: ${err}`);
2532
2623
  });
2533
- sendCloudTelemetry(taskType, targetModel, 0, 0, durationMs, true);
2624
+ sendCloudTelemetry(taskType, targetModel, streamTokensIn, streamTokensOut, durationMs, true, undefined, request.model ?? undefined);
2534
2625
  }
2535
2626
  res.end();
2536
2627
  }