@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.
- package/README.md +94 -28
- package/dist/circuit-breaker.d.ts +39 -0
- package/dist/circuit-breaker.d.ts.map +1 -0
- package/dist/circuit-breaker.js +97 -0
- package/dist/circuit-breaker.js.map +1 -0
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +20 -12
- package/dist/cli.js.map +1 -1
- package/dist/health.d.ts +16 -0
- package/dist/health.d.ts.map +1 -0
- package/dist/health.js +80 -0
- package/dist/health.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +19 -1
- package/dist/index.js.map +1 -1
- package/dist/launcher.d.ts +10 -0
- package/dist/launcher.d.ts.map +1 -0
- package/dist/launcher.js +73 -0
- package/dist/launcher.js.map +1 -0
- package/dist/logger.d.ts +11 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +13 -0
- package/dist/logger.js.map +1 -0
- package/dist/middleware.d.ts +60 -0
- package/dist/middleware.d.ts.map +1 -0
- package/dist/middleware.js +254 -0
- package/dist/middleware.js.map +1 -0
- package/dist/process-manager.d.ts +61 -0
- package/dist/process-manager.d.ts.map +1 -0
- package/dist/process-manager.js +173 -0
- package/dist/process-manager.js.map +1 -0
- package/dist/relay-config.d.ts +19 -0
- package/dist/relay-config.d.ts.map +1 -0
- package/dist/relay-config.js +32 -0
- package/dist/relay-config.js.map +1 -0
- package/dist/standalone-proxy.d.ts.map +1 -1
- package/dist/standalone-proxy.js +98 -7
- package/dist/standalone-proxy.js.map +1 -1
- package/dist/stats.d.ts +49 -0
- package/dist/stats.d.ts.map +1 -0
- package/dist/stats.js +72 -0
- package/dist/stats.js.map +1 -0
- package/dist/status.d.ts +46 -0
- package/dist/status.d.ts.map +1 -0
- package/dist/status.js +78 -0
- package/dist/status.js.map +1 -0
- package/dist/telemetry.d.ts +6 -0
- package/dist/telemetry.d.ts.map +1 -1
- package/dist/telemetry.js +14 -2
- package/dist/telemetry.js.map +1 -1
- 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;
|
|
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"}
|
package/dist/standalone-proxy.js
CHANGED
|
@@ -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
|
-
|
|
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,
|
|
2624
|
+
sendCloudTelemetry(taskType, targetModel, streamTokensIn, streamTokensOut, durationMs, true, undefined, request.model ?? undefined);
|
|
2534
2625
|
}
|
|
2535
2626
|
res.end();
|
|
2536
2627
|
}
|