@relayplane/proxy 1.8.34 → 1.8.37
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/LICENSE +21 -0
- package/README.md +9 -7
- package/dist/cli.js +3 -0
- package/dist/cli.js.map +1 -1
- package/dist/lifecycle-telemetry.d.ts +29 -0
- package/dist/lifecycle-telemetry.d.ts.map +1 -0
- package/dist/lifecycle-telemetry.js +166 -0
- package/dist/lifecycle-telemetry.js.map +1 -0
- package/dist/osmosis-store.d.ts +4 -1
- package/dist/osmosis-store.d.ts.map +1 -1
- package/dist/osmosis-store.js +18 -4
- package/dist/osmosis-store.js.map +1 -1
- package/dist/session-tracker.d.ts +61 -0
- package/dist/session-tracker.d.ts.map +1 -0
- package/dist/session-tracker.js +262 -0
- package/dist/session-tracker.js.map +1 -0
- package/dist/standalone-proxy.d.ts.map +1 -1
- package/dist/standalone-proxy.js +212 -5
- package/dist/standalone-proxy.js.map +1 -1
- package/package.json +13 -14
package/dist/standalone-proxy.js
CHANGED
|
@@ -71,6 +71,7 @@ const path = __importStar(require("node:path"));
|
|
|
71
71
|
const core_1 = require("@relayplane/core");
|
|
72
72
|
const model_suggestions_js_1 = require("./utils/model-suggestions.js");
|
|
73
73
|
const telemetry_js_1 = require("./telemetry.js");
|
|
74
|
+
const lifecycle_telemetry_js_1 = require("./lifecycle-telemetry.js");
|
|
74
75
|
const config_js_1 = require("./config.js");
|
|
75
76
|
const index_js_1 = require("./mesh/index.js");
|
|
76
77
|
const response_cache_js_1 = require("./response-cache.js");
|
|
@@ -93,6 +94,7 @@ const estimateRateMap = new Map();
|
|
|
93
94
|
// Without this, IPs that make one request and disappear stay in the map forever.
|
|
94
95
|
setInterval(() => (0, estimate_js_1.purgeExpiredRateLimitEntries)(estimateRateMap, Date.now()), 5 * 60 * 1000);
|
|
95
96
|
const osmosis_store_js_1 = require("./osmosis-store.js");
|
|
97
|
+
const session_tracker_js_1 = require("./session-tracker.js");
|
|
96
98
|
const PROXY_VERSION = (() => {
|
|
97
99
|
try {
|
|
98
100
|
const pkgPath = path.join(__dirname, '..', 'package.json');
|
|
@@ -391,6 +393,10 @@ function sendCloudTelemetry(taskType, model, tokensIn, tokensOut, latencyMs, suc
|
|
|
391
393
|
setImmediate(() => (0, signup_nudge_js_1.checkAndShowNudge)());
|
|
392
394
|
// Star nudge fires at 50 requests (separate from signup nudge at 100)
|
|
393
395
|
setImmediate(() => (0, star_nudge_js_1.checkAndShowStarNudge)());
|
|
396
|
+
// Lifecycle event: fire proxy.activated once on first successful request
|
|
397
|
+
if (success) {
|
|
398
|
+
setImmediate(() => (0, lifecycle_telemetry_js_1.maybeFireActivated)());
|
|
399
|
+
}
|
|
394
400
|
}
|
|
395
401
|
catch {
|
|
396
402
|
// Telemetry should never break the proxy
|
|
@@ -2553,6 +2559,9 @@ td{padding:8px 12px;border-bottom:1px solid #111318}
|
|
|
2553
2559
|
<table><thead><tr><th>Agent</th><th>Requests</th><th>Total Cost</th><th>Last Active</th><th></th></tr></thead><tbody id="agents"></tbody></table></div>
|
|
2554
2560
|
<div class="section"><h2>Provider Status</h2><div class="prov" id="providers"></div></div>
|
|
2555
2561
|
<div class="section collapsible collapsed"><h2>Learning</h2><div id="learning-panel" style="display:flex;flex-direction:column;gap:12px"><div id="learning-stats" style="display:flex;gap:12px;flex-wrap:wrap"></div><div id="learning-recent"></div><div style="margin-top:8px;padding:10px 14px;background:#0f1720;border:1px solid #1e3a5f;border-radius:8px;font-size:.8rem;color:#60a5fa">Network: Join the network to share with 1,000+ agent installations → <a href="https://relayplane.com/pricing" style="color:#34d399">Upgrade</a></div></div></div>
|
|
2562
|
+
<div class="section collapsible collapsed" id="sessions-section"><h2>Sessions <span id="sessionsLabel" style="font-size:.75rem;color:#64748b;font-weight:400">(last 7d)</span></h2>
|
|
2563
|
+
<table><thead><tr><th>Session ID</th><th>Source</th><th>Started</th><th>Duration</th><th>Requests</th><th>Tokens In</th><th>Tokens Out</th><th>Cost</th><th>Status</th></tr></thead><tbody id="sessions"></tbody></table>
|
|
2564
|
+
<div id="sessions-upsell" style="display:none;margin-top:12px;padding:10px 14px;background:#1a1020;border:1px solid #7c3aed55;border-radius:8px;font-size:.8rem;color:#a78bfa">3+ sessions totalling over $2 this week — <a href="https://relayplane.com/pricing" style="color:#34d399">Upgrade to Pro</a> for full session analytics & budget controls.</div></div>
|
|
2556
2565
|
<div class="section"><h2>Recent Runs <span id="historyLabel" style="font-size:.75rem;color:#64748b;font-weight:400">(7d window, history-capped)</span></h2>
|
|
2557
2566
|
<table><thead><tr><th>Time</th><th>Agent</th><th>Model</th><th class="col-tt">Task Type</th><th class="col-cx">Complexity</th><th>Tokens In</th><th>Tokens Out</th><th class="col-cache">Cache Create</th><th class="col-cache">Cache Read</th><th>Cost</th><th>Latency</th><th>Status</th></tr></thead><tbody id="runs"></tbody></table></div>
|
|
2558
2567
|
<script>
|
|
@@ -2561,6 +2570,30 @@ document.querySelectorAll('.section.collapsible h2').forEach(h2=>h2.addEventList
|
|
|
2561
2570
|
function fmt(n,d=2){return typeof n==='number'?n.toFixed(d):'-'}
|
|
2562
2571
|
function fmtTime(s){const d=new Date(s);return d.toLocaleTimeString()}
|
|
2563
2572
|
function dur(s){const h=Math.floor(s/3600),m=Math.floor(s%3600/60);return h?h+'h '+m+'m':m+'m'}
|
|
2573
|
+
async function loadSessions(){
|
|
2574
|
+
try{
|
|
2575
|
+
const [sessR,activeR]=await Promise.all([
|
|
2576
|
+
fetch('/v1/sessions?limit=20&days=7').then(r=>r.json()).catch(()=>({sessions:[]})),
|
|
2577
|
+
fetch('/v1/sessions/active').then(r=>r.json()).catch(()=>({sessions:[]}))
|
|
2578
|
+
]);
|
|
2579
|
+
const activeIds=new Set((activeR.sessions||[]).map(s=>s.id));
|
|
2580
|
+
const sessions=sessR.sessions||[];
|
|
2581
|
+
const el=$('sessions');
|
|
2582
|
+
if(!el)return;
|
|
2583
|
+
el.innerHTML=sessions.length?sessions.map(s=>{
|
|
2584
|
+
const isActive=activeIds.has(s.id)||s.active;
|
|
2585
|
+
const dur=s.duration_ms>0?Math.round(s.duration_ms/1000)+'s':'—';
|
|
2586
|
+
const badge=isActive?'<span class="badge ok" style="font-size:.7rem">LIVE</span>':'<span style="color:#64748b;font-size:.75rem">idle</span>';
|
|
2587
|
+
const srcBadge=s.session_source==='claude-code'?'<span style="color:#60a5fa;font-size:.75rem">claude-code</span>':'<span style="color:#94a3b8;font-size:.75rem">synthetic</span>';
|
|
2588
|
+
const sid=s.id.length>20?s.id.slice(0,20)+'…':s.id;
|
|
2589
|
+
return '<tr><td style="font-family:monospace;font-size:.8rem" title="'+s.id+'">'+sid+'</td><td>'+srcBadge+'</td><td>'+fmtTime(new Date(s.started_at).toISOString())+'</td><td>'+dur+'</td><td>'+s.request_count+'</td><td>'+(s.total_tokens_in||0)+'</td><td>'+(s.total_tokens_out||0)+'</td><td>$'+fmt(s.total_cost_usd,4)+'</td><td>'+badge+'</td></tr>';
|
|
2590
|
+
}).join(''):'<tr><td colspan=9 style="color:#64748b">No sessions recorded yet</td></tr>';
|
|
2591
|
+
// Pro upsell: 3+ sessions with total cost > $2 this week
|
|
2592
|
+
const totalCost=sessions.reduce((s,r)=>s+(r.total_cost_usd||0),0);
|
|
2593
|
+
const upsell=$('sessions-upsell');
|
|
2594
|
+
if(upsell) upsell.style.display=(sessions.length>=3&&totalCost>2)?'block':'none';
|
|
2595
|
+
}catch(e){console.error('sessions load error',e)}
|
|
2596
|
+
}
|
|
2564
2597
|
async function load(){
|
|
2565
2598
|
try{
|
|
2566
2599
|
const [health,stats,runsR,sav,provH,agentsR]=await Promise.all([
|
|
@@ -2705,7 +2738,7 @@ async function loadLearning(){
|
|
|
2705
2738
|
}
|
|
2706
2739
|
}catch(e){console.error('learning load error',e)}
|
|
2707
2740
|
}
|
|
2708
|
-
load();loadLearning();setInterval(load,5000);setInterval(loadLearning,30000);
|
|
2741
|
+
load();loadLearning();loadSessions();setInterval(load,5000);setInterval(loadLearning,30000);setInterval(loadSessions,10000);
|
|
2709
2742
|
</script><footer style="text-align:center;padding:20px 0;color:#475569;font-size:.75rem;border-top:1px solid #1e293b;margin-top:20px">🔒 Request content stays on your machine. Never sent to cloud.</footer></body></html>`;
|
|
2710
2743
|
}
|
|
2711
2744
|
async function getKnowledgeStats() {
|
|
@@ -2861,6 +2894,8 @@ async function startProxy(config = {}) {
|
|
|
2861
2894
|
// Check once at startup whether the nudges have already been shown
|
|
2862
2895
|
(0, signup_nudge_js_1.initNudge)();
|
|
2863
2896
|
(0, star_nudge_js_1.initStarNudge)();
|
|
2897
|
+
// Lifecycle event: daily session heartbeat
|
|
2898
|
+
setImmediate(() => (0, lifecycle_telemetry_js_1.maybeSendSessionHeartbeat)());
|
|
2864
2899
|
// Flush history on shutdown
|
|
2865
2900
|
const handleShutdown = () => {
|
|
2866
2901
|
(0, agent_tracker_js_1.flushAgentRegistry)();
|
|
@@ -3299,6 +3334,136 @@ async function startProxy(config = {}) {
|
|
|
3299
3334
|
}
|
|
3300
3335
|
return;
|
|
3301
3336
|
}
|
|
3337
|
+
// === Budget endpoints ===
|
|
3338
|
+
if (req.method === 'GET' && pathname === '/control/budget') {
|
|
3339
|
+
const status = budgetManager.getStatus();
|
|
3340
|
+
const cfg = budgetManager.getConfig();
|
|
3341
|
+
const now = Date.now();
|
|
3342
|
+
const weekCutoff = now - 7 * 86400000;
|
|
3343
|
+
const monthCutoff = now - 30 * 86400000;
|
|
3344
|
+
const weekCost = requestHistory
|
|
3345
|
+
.filter(r => new Date(r.timestamp).getTime() >= weekCutoff)
|
|
3346
|
+
.reduce((s, r) => s + r.costUsd, 0);
|
|
3347
|
+
const monthCost = requestHistory
|
|
3348
|
+
.filter(r => new Date(r.timestamp).getTime() >= monthCutoff)
|
|
3349
|
+
.reduce((s, r) => s + r.costUsd, 0);
|
|
3350
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
3351
|
+
res.end(JSON.stringify({
|
|
3352
|
+
today_usd: Math.round(status.dailySpend * 10000) / 10000,
|
|
3353
|
+
limit_usd: status.dailyLimit,
|
|
3354
|
+
pct_used: Math.round(status.dailyPercent * 10) / 10,
|
|
3355
|
+
remaining_usd: Math.max(0, Math.round((status.dailyLimit - status.dailySpend) * 10000) / 10000),
|
|
3356
|
+
this_week_usd: Math.round(weekCost * 10000) / 10000,
|
|
3357
|
+
this_month_usd: Math.round(monthCost * 10000) / 10000,
|
|
3358
|
+
enabled: cfg.enabled,
|
|
3359
|
+
on_breach: cfg.onBreach,
|
|
3360
|
+
alert_thresholds: cfg.alertThresholds,
|
|
3361
|
+
hourly_usd: Math.round(status.hourlySpend * 10000) / 10000,
|
|
3362
|
+
hourly_limit_usd: status.hourlyLimit,
|
|
3363
|
+
hourly_pct_used: Math.round(status.hourlyPercent * 10) / 10,
|
|
3364
|
+
breached: status.breached,
|
|
3365
|
+
breach_type: status.breachType,
|
|
3366
|
+
}));
|
|
3367
|
+
return;
|
|
3368
|
+
}
|
|
3369
|
+
if (req.method === 'POST' && pathname === '/control/budget/set') {
|
|
3370
|
+
try {
|
|
3371
|
+
const body = await readJsonBody(req);
|
|
3372
|
+
const amount = Number(body.dailyUsd);
|
|
3373
|
+
if (!body.dailyUsd || isNaN(amount) || amount <= 0) {
|
|
3374
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
3375
|
+
res.end(JSON.stringify({ error: 'dailyUsd must be a positive number' }));
|
|
3376
|
+
return;
|
|
3377
|
+
}
|
|
3378
|
+
budgetManager.setLimits({ dailyUsd: amount });
|
|
3379
|
+
budgetManager.updateConfig({ enabled: true });
|
|
3380
|
+
proxyConfig = normalizeProxyConfig({
|
|
3381
|
+
...proxyConfig,
|
|
3382
|
+
budget: { ...proxyConfig.budget, dailyUsd: amount, enabled: true },
|
|
3383
|
+
});
|
|
3384
|
+
await saveProxyConfig(configPath, proxyConfig);
|
|
3385
|
+
startConfigWatcher();
|
|
3386
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
3387
|
+
res.end(JSON.stringify({ ok: true, dailyUsd: amount }));
|
|
3388
|
+
}
|
|
3389
|
+
catch {
|
|
3390
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
3391
|
+
res.end(JSON.stringify({ error: 'Invalid JSON' }));
|
|
3392
|
+
}
|
|
3393
|
+
return;
|
|
3394
|
+
}
|
|
3395
|
+
if (req.method === 'POST' && pathname === '/control/budget/set-alert') {
|
|
3396
|
+
try {
|
|
3397
|
+
const body = await readJsonBody(req);
|
|
3398
|
+
const pct = Number(body.threshold);
|
|
3399
|
+
if (!body.threshold || isNaN(pct) || pct <= 0 || pct > 100) {
|
|
3400
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
3401
|
+
res.end(JSON.stringify({ error: 'threshold must be 1-100' }));
|
|
3402
|
+
return;
|
|
3403
|
+
}
|
|
3404
|
+
const current = budgetManager.getConfig();
|
|
3405
|
+
const thresholds = [...new Set([...current.alertThresholds, pct])].sort((a, b) => a - b);
|
|
3406
|
+
budgetManager.updateConfig({ alertThresholds: thresholds });
|
|
3407
|
+
proxyConfig = normalizeProxyConfig({
|
|
3408
|
+
...proxyConfig,
|
|
3409
|
+
budget: { ...proxyConfig.budget, alertThresholds: thresholds },
|
|
3410
|
+
});
|
|
3411
|
+
await saveProxyConfig(configPath, proxyConfig);
|
|
3412
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
3413
|
+
res.end(JSON.stringify({ ok: true, alertThresholds: thresholds }));
|
|
3414
|
+
}
|
|
3415
|
+
catch {
|
|
3416
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
3417
|
+
res.end(JSON.stringify({ error: 'Invalid JSON' }));
|
|
3418
|
+
}
|
|
3419
|
+
return;
|
|
3420
|
+
}
|
|
3421
|
+
if (req.method === 'POST' && pathname === '/control/budget/reset') {
|
|
3422
|
+
budgetManager.reset();
|
|
3423
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
3424
|
+
res.end(JSON.stringify({ ok: true, message: 'Daily spend counter reset' }));
|
|
3425
|
+
return;
|
|
3426
|
+
}
|
|
3427
|
+
if (req.method === 'POST' && pathname === '/control/model') {
|
|
3428
|
+
try {
|
|
3429
|
+
const body = await readJsonBody(req);
|
|
3430
|
+
if (!body.model) {
|
|
3431
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
3432
|
+
res.end(JSON.stringify({ error: 'model required' }));
|
|
3433
|
+
return;
|
|
3434
|
+
}
|
|
3435
|
+
const previousRouting = proxyConfig.routing;
|
|
3436
|
+
const target = body.model;
|
|
3437
|
+
// Update all complexity tiers to the requested model
|
|
3438
|
+
proxyConfig = normalizeProxyConfig({
|
|
3439
|
+
...proxyConfig,
|
|
3440
|
+
routing: {
|
|
3441
|
+
...proxyConfig.routing,
|
|
3442
|
+
complexity: {
|
|
3443
|
+
enabled: proxyConfig.routing?.complexity?.enabled ?? true,
|
|
3444
|
+
simple: target,
|
|
3445
|
+
moderate: target,
|
|
3446
|
+
complex: target,
|
|
3447
|
+
},
|
|
3448
|
+
},
|
|
3449
|
+
});
|
|
3450
|
+
await saveProxyConfig(configPath, proxyConfig);
|
|
3451
|
+
startConfigWatcher();
|
|
3452
|
+
const prevModel = previousRouting?.complexity?.complex ?? previousRouting?.complexity?.moderate ?? 'unknown';
|
|
3453
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
3454
|
+
res.end(JSON.stringify({
|
|
3455
|
+
switched: true,
|
|
3456
|
+
previous: prevModel,
|
|
3457
|
+
current: target,
|
|
3458
|
+
reason: body.reason ?? '',
|
|
3459
|
+
}));
|
|
3460
|
+
}
|
|
3461
|
+
catch {
|
|
3462
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
3463
|
+
res.end(JSON.stringify({ error: 'Invalid JSON' }));
|
|
3464
|
+
}
|
|
3465
|
+
return;
|
|
3466
|
+
}
|
|
3302
3467
|
}
|
|
3303
3468
|
if (req.method === 'POST' && pathname === '/control/kill') {
|
|
3304
3469
|
try {
|
|
@@ -3670,6 +3835,36 @@ async function startProxy(config = {}) {
|
|
|
3670
3835
|
}
|
|
3671
3836
|
return;
|
|
3672
3837
|
}
|
|
3838
|
+
// === Session Intelligence endpoints ===
|
|
3839
|
+
if (req.method === 'GET' && pathname === '/v1/sessions') {
|
|
3840
|
+
const queryString = url.includes('?') ? url.split('?')[1] ?? '' : '';
|
|
3841
|
+
const params = new URLSearchParams(queryString);
|
|
3842
|
+
const limit = Math.min(parseInt(params.get('limit') || '20', 10), 100);
|
|
3843
|
+
const days = parseInt(params.get('days') || '7', 10);
|
|
3844
|
+
const sessions = (0, session_tracker_js_1.getSessions)({ limit, days });
|
|
3845
|
+
const now = Date.now();
|
|
3846
|
+
const activeCutoff = now - 5 * 60 * 1000;
|
|
3847
|
+
const result = sessions.map(s => ({
|
|
3848
|
+
...s,
|
|
3849
|
+
active: s.last_seen_at >= activeCutoff,
|
|
3850
|
+
duration_ms: s.last_seen_at - s.started_at,
|
|
3851
|
+
}));
|
|
3852
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
3853
|
+
res.end(JSON.stringify({ sessions: result, total: result.length }));
|
|
3854
|
+
return;
|
|
3855
|
+
}
|
|
3856
|
+
if (req.method === 'GET' && pathname === '/v1/sessions/active') {
|
|
3857
|
+
const active = (0, session_tracker_js_1.getActiveSessions)();
|
|
3858
|
+
const now = Date.now();
|
|
3859
|
+
const result = active.map(s => ({
|
|
3860
|
+
...s,
|
|
3861
|
+
active: true,
|
|
3862
|
+
duration_ms: s.last_seen_at - s.started_at,
|
|
3863
|
+
}));
|
|
3864
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
3865
|
+
res.end(JSON.stringify({ sessions: result, total: result.length }));
|
|
3866
|
+
return;
|
|
3867
|
+
}
|
|
3673
3868
|
// Extract auth context from incoming request
|
|
3674
3869
|
const ctx = extractRequestContext(req);
|
|
3675
3870
|
const anthropicEnvKey = process.env['ANTHROPIC_API_KEY'];
|
|
@@ -3709,6 +3904,8 @@ async function startProxy(config = {}) {
|
|
|
3709
3904
|
res.end(JSON.stringify({ error: 'Invalid JSON' }));
|
|
3710
3905
|
return;
|
|
3711
3906
|
}
|
|
3907
|
+
// Extract session ID (X-Claude-Code-Session-Id or synthetic)
|
|
3908
|
+
const { sessionId: nativeSessionId, sessionSource: nativeSessionSource } = (0, session_tracker_js_1.getSessionId)(req, requestBody['model']);
|
|
3712
3909
|
// Extract agent fingerprint and explicit agent ID
|
|
3713
3910
|
const nativeSystemPrompt = (0, agent_tracker_js_1.extractSystemPromptFromBody)(requestBody);
|
|
3714
3911
|
const nativeExplicitAgentId = getHeaderValue(req, 'x-relayplane-agent') || undefined;
|
|
@@ -4253,6 +4450,8 @@ async function startProxy(config = {}) {
|
|
|
4253
4450
|
if (nativeAgentFingerprint && nativeAgentFingerprint !== 'unknown') {
|
|
4254
4451
|
(0, agent_tracker_js_1.updateAgentCost)(nativeAgentFingerprint, nativeCostUsd);
|
|
4255
4452
|
}
|
|
4453
|
+
// ── Session Intelligence: upsert session record ──
|
|
4454
|
+
(0, session_tracker_js_1.upsertSession)(nativeSessionId, nativeSessionSource, nativeCostUsd, nativeTokIn, nativeTokOut);
|
|
4256
4455
|
// ── Post-request: budget spend + anomaly detection ──
|
|
4257
4456
|
postRequestRecord(targetModel || requestedModel, nativeTokIn, nativeTokOut, nativeCostUsd);
|
|
4258
4457
|
if (recordTelemetry) {
|
|
@@ -4397,6 +4596,8 @@ async function startProxy(config = {}) {
|
|
|
4397
4596
|
return;
|
|
4398
4597
|
}
|
|
4399
4598
|
const isStreaming = request.stream === true;
|
|
4599
|
+
// Extract session ID for chat/completions
|
|
4600
|
+
const { sessionId: chatSessionId, sessionSource: chatSessionSource } = (0, session_tracker_js_1.getSessionId)(req, request.model);
|
|
4400
4601
|
// Extract agent fingerprint for chat/completions
|
|
4401
4602
|
const chatSystemPrompt = (0, agent_tracker_js_1.extractSystemPromptFromBody)(request);
|
|
4402
4603
|
const chatExplicitAgentId = getHeaderValue(req, 'x-relayplane-agent') || undefined;
|
|
@@ -4751,7 +4952,7 @@ async function startProxy(config = {}) {
|
|
|
4751
4952
|
const startTime = Date.now();
|
|
4752
4953
|
// Handle streaming vs non-streaming
|
|
4753
4954
|
if (isStreaming) {
|
|
4754
|
-
await handleStreamingRequest(res, request, targetProvider, targetModel, apiKey, ctx, relay, promptText, taskType, confidence, useCascade ? 'cascade' : routingMode, recordTelemetry, startTime, log, cooldownManager, cooldownsEnabled, complexity, chatCacheHash, chatCacheBypass, chatAgentFingerprint, chatExplicitAgentId);
|
|
4955
|
+
await handleStreamingRequest(res, request, targetProvider, targetModel, apiKey, ctx, relay, promptText, taskType, confidence, useCascade ? 'cascade' : routingMode, recordTelemetry, startTime, log, cooldownManager, cooldownsEnabled, complexity, chatCacheHash, chatCacheBypass, chatAgentFingerprint, chatExplicitAgentId, chatSessionId, chatSessionSource);
|
|
4755
4956
|
}
|
|
4756
4957
|
else {
|
|
4757
4958
|
if (useCascade && cascadeConfig) {
|
|
@@ -4794,6 +4995,7 @@ async function startProxy(config = {}) {
|
|
|
4794
4995
|
updateLastHistoryEntry(cascadeTokensIn, cascadeTokensOut, cascadeCost, chatCascadeRespModel, cascadeCacheCreation, cascadeCacheRead, chatAgentFingerprint, chatExplicitAgentId);
|
|
4795
4996
|
if (chatAgentFingerprint && chatAgentFingerprint !== 'unknown')
|
|
4796
4997
|
(0, agent_tracker_js_1.updateAgentCost)(chatAgentFingerprint, cascadeCost);
|
|
4998
|
+
(0, session_tracker_js_1.upsertSession)(chatSessionId, chatSessionSource, cascadeCost, cascadeTokensIn, cascadeTokensOut);
|
|
4797
4999
|
if (recordTelemetry) {
|
|
4798
5000
|
try {
|
|
4799
5001
|
const runResult = await relay.run({
|
|
@@ -4852,7 +5054,8 @@ async function startProxy(config = {}) {
|
|
|
4852
5054
|
}
|
|
4853
5055
|
}
|
|
4854
5056
|
else {
|
|
4855
|
-
await handleNonStreamingRequest(res, request, targetProvider, targetModel, apiKey, ctx, relay, promptText, taskType, confidence, routingMode, recordTelemetry, startTime, log, cooldownManager, cooldownsEnabled, complexity, chatAgentFingerprint, chatExplicitAgentId, useAnthropicEnvKey)
|
|
5057
|
+
await handleNonStreamingRequest(res, request, targetProvider, targetModel, apiKey, ctx, relay, promptText, taskType, confidence, routingMode, recordTelemetry, startTime, log, cooldownManager, cooldownsEnabled, complexity, chatAgentFingerprint, chatExplicitAgentId, useAnthropicEnvKey, // pass for cross-provider cascade API key resolution (GH #38)
|
|
5058
|
+
chatSessionId, chatSessionSource);
|
|
4856
5059
|
}
|
|
4857
5060
|
}
|
|
4858
5061
|
});
|
|
@@ -5017,7 +5220,7 @@ async function executeNonStreamingProviderRequest(request, targetProvider, targe
|
|
|
5017
5220
|
}
|
|
5018
5221
|
return { responseData, ok: true, status: 200 };
|
|
5019
5222
|
}
|
|
5020
|
-
async function handleStreamingRequest(res, request, targetProvider, targetModel, apiKey, ctx, relay, promptText, taskType, confidence, routingMode, recordTelemetry, startTime, log, cooldownManager, cooldownsEnabled, complexity = 'simple', cacheHash, cacheBypass, agentFingerprint, agentId) {
|
|
5223
|
+
async function handleStreamingRequest(res, request, targetProvider, targetModel, apiKey, ctx, relay, promptText, taskType, confidence, routingMode, recordTelemetry, startTime, log, cooldownManager, cooldownsEnabled, complexity = 'simple', cacheHash, cacheBypass, agentFingerprint, agentId, sessionId, sessionSource) {
|
|
5021
5224
|
let providerResponse;
|
|
5022
5225
|
try {
|
|
5023
5226
|
switch (targetProvider) {
|
|
@@ -5226,6 +5429,8 @@ async function handleStreamingRequest(res, request, targetProvider, targetModel,
|
|
|
5226
5429
|
updateLastHistoryEntry(streamTokensIn, streamTokensOut, streamCost, undefined, streamCacheCreation || undefined, streamCacheRead || undefined, agentFingerprint, agentId);
|
|
5227
5430
|
if (agentFingerprint && agentFingerprint !== 'unknown')
|
|
5228
5431
|
(0, agent_tracker_js_1.updateAgentCost)(agentFingerprint, streamCost);
|
|
5432
|
+
if (sessionId && sessionSource)
|
|
5433
|
+
(0, session_tracker_js_1.upsertSession)(sessionId, sessionSource, streamCost, streamTokensIn, streamTokensOut);
|
|
5229
5434
|
// ── Post-request: budget spend + anomaly detection ──
|
|
5230
5435
|
try {
|
|
5231
5436
|
(0, budget_js_1.getBudgetManager)().recordSpend(streamCost, targetModel);
|
|
@@ -5263,7 +5468,7 @@ async function handleStreamingRequest(res, request, targetProvider, targetModel,
|
|
|
5263
5468
|
*/
|
|
5264
5469
|
async function handleNonStreamingRequest(res, request, targetProvider, targetModel, apiKey, ctx, relay, promptText, taskType, confidence, routingMode, recordTelemetry, startTime, log, cooldownManager, cooldownsEnabled, complexity = 'simple', agentFingerprint, agentId,
|
|
5265
5470
|
/** Anthropic env API key — required for cross-provider cascade API key resolution (GH #38) */
|
|
5266
|
-
anthropicEnvKeyForCascade) {
|
|
5471
|
+
anthropicEnvKeyForCascade, sessionId, sessionSource) {
|
|
5267
5472
|
let responseData;
|
|
5268
5473
|
try {
|
|
5269
5474
|
const result = await executeNonStreamingProviderRequest(request, targetProvider, targetModel, apiKey, ctx);
|
|
@@ -5360,6 +5565,8 @@ anthropicEnvKeyForCascade) {
|
|
|
5360
5565
|
updateLastHistoryEntry(tokensIn, tokensOut, cost, nonStreamRespModel, cacheCreationTokens || undefined, cacheReadTokens || undefined, agentFingerprint, agentId);
|
|
5361
5566
|
if (agentFingerprint && agentFingerprint !== 'unknown')
|
|
5362
5567
|
(0, agent_tracker_js_1.updateAgentCost)(agentFingerprint, cost);
|
|
5568
|
+
if (sessionId && sessionSource)
|
|
5569
|
+
(0, session_tracker_js_1.upsertSession)(sessionId, sessionSource, cost, tokensIn, tokensOut);
|
|
5363
5570
|
// ── Post-request: budget spend + anomaly detection ──
|
|
5364
5571
|
try {
|
|
5365
5572
|
(0, budget_js_1.getBudgetManager)().recordSpend(cost, targetModel);
|