@relayplane/proxy 1.9.18 → 1.9.21
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/dist/agent-policy.d.ts +54 -0
- package/dist/agent-policy.d.ts.map +1 -0
- package/dist/agent-policy.js +183 -0
- package/dist/agent-policy.js.map +1 -0
- package/dist/budget.d.ts +81 -0
- package/dist/budget.d.ts.map +1 -1
- package/dist/budget.js +224 -1
- package/dist/budget.js.map +1 -1
- package/dist/cli.js +773 -4
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +6 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js.map +1 -1
- package/dist/policy-analyzer.d.ts +41 -0
- package/dist/policy-analyzer.d.ts.map +1 -0
- package/dist/policy-analyzer.js +268 -0
- package/dist/policy-analyzer.js.map +1 -0
- package/dist/policy-suggestions.d.ts +31 -0
- package/dist/policy-suggestions.d.ts.map +1 -0
- package/dist/policy-suggestions.js +173 -0
- package/dist/policy-suggestions.js.map +1 -0
- package/dist/routing-log.d.ts +47 -0
- package/dist/routing-log.d.ts.map +1 -0
- package/dist/routing-log.js +141 -0
- package/dist/routing-log.js.map +1 -0
- package/dist/standalone-proxy.d.ts.map +1 -1
- package/dist/standalone-proxy.js +139 -0
- package/dist/standalone-proxy.js.map +1 -1
- package/dist/telemetryPinger.d.ts +2 -0
- package/dist/telemetryPinger.d.ts.map +1 -0
- package/dist/telemetryPinger.js +80 -0
- package/dist/telemetryPinger.js.map +1 -0
- package/package.json +4 -2
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Policy Analyzer
|
|
4
|
+
*
|
|
5
|
+
* Reads routing-log.jsonl directly (not the in-memory buffer) and joins with
|
|
6
|
+
* agents.json to produce per-agent traffic summaries used by the suggestion engine
|
|
7
|
+
* and CLI display.
|
|
8
|
+
*/
|
|
9
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
12
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
13
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
14
|
+
}
|
|
15
|
+
Object.defineProperty(o, k2, desc);
|
|
16
|
+
}) : (function(o, m, k, k2) {
|
|
17
|
+
if (k2 === undefined) k2 = k;
|
|
18
|
+
o[k2] = m[k];
|
|
19
|
+
}));
|
|
20
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
21
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
22
|
+
}) : function(o, v) {
|
|
23
|
+
o["default"] = v;
|
|
24
|
+
});
|
|
25
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
26
|
+
var ownKeys = function(o) {
|
|
27
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
28
|
+
var ar = [];
|
|
29
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
30
|
+
return ar;
|
|
31
|
+
};
|
|
32
|
+
return ownKeys(o);
|
|
33
|
+
};
|
|
34
|
+
return function (mod) {
|
|
35
|
+
if (mod && mod.__esModule) return mod;
|
|
36
|
+
var result = {};
|
|
37
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
38
|
+
__setModuleDefault(result, mod);
|
|
39
|
+
return result;
|
|
40
|
+
};
|
|
41
|
+
})();
|
|
42
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
43
|
+
exports.MODEL_COST_PER_1M = void 0;
|
|
44
|
+
exports.estimateDailyCost = estimateDailyCost;
|
|
45
|
+
exports.inferAgentName = inferAgentName;
|
|
46
|
+
exports.analyzeTraffic = analyzeTraffic;
|
|
47
|
+
const fs = __importStar(require("node:fs"));
|
|
48
|
+
const routing_log_js_1 = require("./routing-log.js");
|
|
49
|
+
const agent_tracker_js_1 = require("./agent-tracker.js");
|
|
50
|
+
// ─── Model cost table ─────────────────────────────────────────────────────────
|
|
51
|
+
// Costs in USD per 1M tokens. Input/output separately.
|
|
52
|
+
// Update when provider pricing changes.
|
|
53
|
+
exports.MODEL_COST_PER_1M = {
|
|
54
|
+
'anthropic/claude-opus-4-5': { input: 15.00, output: 75.00 },
|
|
55
|
+
'anthropic/claude-opus-4': { input: 15.00, output: 75.00 },
|
|
56
|
+
'anthropic/claude-sonnet-4-5': { input: 3.00, output: 15.00 },
|
|
57
|
+
'anthropic/claude-sonnet-4': { input: 3.00, output: 15.00 },
|
|
58
|
+
'anthropic/claude-haiku-4-5': { input: 0.80, output: 4.00 },
|
|
59
|
+
'anthropic/claude-haiku-4': { input: 0.80, output: 4.00 },
|
|
60
|
+
'openai/gpt-4o': { input: 2.50, output: 10.00 },
|
|
61
|
+
'openai/gpt-4o-mini': { input: 0.15, output: 0.60 },
|
|
62
|
+
'google/gemini-2.0-flash': { input: 0.10, output: 0.40 },
|
|
63
|
+
'google/gemini-1.5-flash': { input: 0.075, output: 0.30 },
|
|
64
|
+
'groq/llama-3.3-70b': { input: 0.59, output: 0.79 },
|
|
65
|
+
'groq/llama-3.1-8b-instant': { input: 0.05, output: 0.08 },
|
|
66
|
+
'openrouter/auto': { input: 1.00, output: 5.00 }, // rough estimate
|
|
67
|
+
};
|
|
68
|
+
function estimateDailyCost(avgInputTokens, avgOutputTokens, requestsPerDay, model) {
|
|
69
|
+
const costs = exports.MODEL_COST_PER_1M[model];
|
|
70
|
+
if (!costs)
|
|
71
|
+
return 0;
|
|
72
|
+
const perRequest = (avgInputTokens / 1_000_000) * costs.input +
|
|
73
|
+
(avgOutputTokens / 1_000_000) * costs.output;
|
|
74
|
+
return perRequest * requestsPerDay;
|
|
75
|
+
}
|
|
76
|
+
// ─── Name inference ───────────────────────────────────────────────────────────
|
|
77
|
+
function slugify(text) {
|
|
78
|
+
return text
|
|
79
|
+
.toLowerCase()
|
|
80
|
+
.replace(/\s+/g, '-')
|
|
81
|
+
.replace(/[^a-z0-9-]/g, '')
|
|
82
|
+
.slice(0, 24);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Infer a human-readable agent name from its system prompt and task distribution.
|
|
86
|
+
*/
|
|
87
|
+
function inferAgentName(systemPromptPreview, taskDistribution) {
|
|
88
|
+
// Priority 1: "You are a/an [role]"
|
|
89
|
+
const youAreMatch = systemPromptPreview.match(/you are (?:a |an )?([A-Za-z][\w\s]{1,24})/i);
|
|
90
|
+
if (youAreMatch && youAreMatch[1]) {
|
|
91
|
+
return slugify(youAreMatch[1].trim());
|
|
92
|
+
}
|
|
93
|
+
// Priority 2: "Your job/role/task/purpose is to [verb]"
|
|
94
|
+
const jobMatch = systemPromptPreview.match(/your (?:job|role|task|purpose) is to (\w+)/i);
|
|
95
|
+
if (jobMatch && jobMatch[1]) {
|
|
96
|
+
return slugify(jobMatch[1].trim()) + '-agent';
|
|
97
|
+
}
|
|
98
|
+
// Priority 3: "As a/an [role] assistant/agent/bot"
|
|
99
|
+
const asAMatch = systemPromptPreview.match(/as (?:a |an )?(\w+(?:\s+\w+)?) (?:assistant|agent|bot)/i);
|
|
100
|
+
if (asAMatch && asAMatch[1]) {
|
|
101
|
+
return slugify(asAMatch[1].trim());
|
|
102
|
+
}
|
|
103
|
+
// Priority 4: fallback to dominant task
|
|
104
|
+
const dominant = Object.entries(taskDistribution).sort((a, b) => b[1] - a[1])[0];
|
|
105
|
+
if (dominant) {
|
|
106
|
+
return dominant[0] + '-agent';
|
|
107
|
+
}
|
|
108
|
+
return 'unknown-agent';
|
|
109
|
+
}
|
|
110
|
+
// ─── Main analysis function ───────────────────────────────────────────────────
|
|
111
|
+
/**
|
|
112
|
+
* Read routing-log.jsonl and agents.json to produce per-agent traffic summaries.
|
|
113
|
+
* Default lookbackDays = 7.
|
|
114
|
+
*/
|
|
115
|
+
async function analyzeTraffic(opts) {
|
|
116
|
+
const lookbackDays = opts?.lookbackDays ?? 7;
|
|
117
|
+
const cutoff = Date.now() - lookbackDays * 86_400_000;
|
|
118
|
+
// 1. Read JSONL file
|
|
119
|
+
if (!fs.existsSync(routing_log_js_1.LOG_FILE)) {
|
|
120
|
+
return [];
|
|
121
|
+
}
|
|
122
|
+
let lines;
|
|
123
|
+
try {
|
|
124
|
+
const content = fs.readFileSync(routing_log_js_1.LOG_FILE, 'utf-8');
|
|
125
|
+
lines = content.split('\n').filter(l => l.trim().length > 0);
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
return [];
|
|
129
|
+
}
|
|
130
|
+
// Parse and filter entries within lookback window
|
|
131
|
+
const entries = [];
|
|
132
|
+
for (const line of lines) {
|
|
133
|
+
try {
|
|
134
|
+
const entry = JSON.parse(line);
|
|
135
|
+
const ts = Date.parse(entry.ts);
|
|
136
|
+
if (!isNaN(ts) && ts >= cutoff) {
|
|
137
|
+
entries.push(entry);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
catch {
|
|
141
|
+
// Skip corrupt lines
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
if (entries.length === 0) {
|
|
145
|
+
return [];
|
|
146
|
+
}
|
|
147
|
+
// 2. Group by agentFingerprint (skip null fingerprints)
|
|
148
|
+
const groups = new Map();
|
|
149
|
+
for (const entry of entries) {
|
|
150
|
+
if (!entry.agentFingerprint)
|
|
151
|
+
continue;
|
|
152
|
+
const existing = groups.get(entry.agentFingerprint);
|
|
153
|
+
if (existing) {
|
|
154
|
+
existing.push(entry);
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
groups.set(entry.agentFingerprint, [entry]);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
if (groups.size === 0) {
|
|
161
|
+
return [];
|
|
162
|
+
}
|
|
163
|
+
// 3. Load agent registry
|
|
164
|
+
const registry = (0, agent_tracker_js_1.getAgentRegistry)();
|
|
165
|
+
// 4. Build analysis for each fingerprint group
|
|
166
|
+
const analyses = [];
|
|
167
|
+
for (const [fingerprint, groupEntries] of groups) {
|
|
168
|
+
const total = groupEntries.length;
|
|
169
|
+
// a. Task distribution
|
|
170
|
+
const taskCounts = {};
|
|
171
|
+
for (const e of groupEntries) {
|
|
172
|
+
taskCounts[e.taskType] = (taskCounts[e.taskType] ?? 0) + 1;
|
|
173
|
+
}
|
|
174
|
+
const taskDistribution = {};
|
|
175
|
+
for (const [task, count] of Object.entries(taskCounts)) {
|
|
176
|
+
taskDistribution[task] = count / total;
|
|
177
|
+
}
|
|
178
|
+
// b. Dominant task
|
|
179
|
+
const dominantTask = Object.entries(taskDistribution).sort((a, b) => b[1] - a[1])[0]?.[0] ?? 'unknown';
|
|
180
|
+
// c. Token averages
|
|
181
|
+
const entriesWithTokens = groupEntries.filter(e => e.inputTokens !== undefined || e.outputTokens !== undefined);
|
|
182
|
+
let avgInputTokens;
|
|
183
|
+
let avgOutputTokens;
|
|
184
|
+
let tokensAreEstimated;
|
|
185
|
+
if (entriesWithTokens.length > 0) {
|
|
186
|
+
avgInputTokens = entriesWithTokens.reduce((sum, e) => sum + (e.inputTokens ?? 0), 0) / entriesWithTokens.length;
|
|
187
|
+
avgOutputTokens = entriesWithTokens.reduce((sum, e) => sum + (e.outputTokens ?? 0), 0) / entriesWithTokens.length;
|
|
188
|
+
tokensAreEstimated = false;
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
// Estimate from cost: assume 80/20 input/output split
|
|
192
|
+
// We'll estimate based on the model pricing
|
|
193
|
+
let totalEstimatedTokens = 0;
|
|
194
|
+
let validEntries = 0;
|
|
195
|
+
for (const e of groupEntries) {
|
|
196
|
+
const costs = exports.MODEL_COST_PER_1M[e.resolvedModel];
|
|
197
|
+
if (costs && costs.input > 0) {
|
|
198
|
+
// Rough estimate: use a small baseline if cost not available
|
|
199
|
+
const estimatedTokens = 2000; // baseline
|
|
200
|
+
totalEstimatedTokens += estimatedTokens;
|
|
201
|
+
validEntries++;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
const avgTotal = validEntries > 0 ? totalEstimatedTokens / validEntries : 2000;
|
|
205
|
+
avgInputTokens = avgTotal * 0.8;
|
|
206
|
+
avgOutputTokens = avgTotal * 0.2;
|
|
207
|
+
tokensAreEstimated = true;
|
|
208
|
+
}
|
|
209
|
+
const avgTotalTokens = avgInputTokens + avgOutputTokens;
|
|
210
|
+
// d. Current model (most recent entry by ts)
|
|
211
|
+
const sortedByTs = [...groupEntries].sort((a, b) => Date.parse(b.ts) - Date.parse(a.ts));
|
|
212
|
+
const currentModel = sortedByTs[0]?.resolvedModel ?? 'unknown';
|
|
213
|
+
// e. Join with agents.json
|
|
214
|
+
const registryEntry = registry[fingerprint];
|
|
215
|
+
let daysObserved = 1;
|
|
216
|
+
let costPerDay = 0;
|
|
217
|
+
let requestsPerDay = total;
|
|
218
|
+
let systemPromptPreview = '';
|
|
219
|
+
let totalRequests = total;
|
|
220
|
+
let name;
|
|
221
|
+
let nameIsInferred;
|
|
222
|
+
if (registryEntry) {
|
|
223
|
+
const firstSeen = Date.parse(registryEntry.firstSeen);
|
|
224
|
+
const lastSeen = Date.parse(registryEntry.lastSeen);
|
|
225
|
+
daysObserved = Math.max(1, (lastSeen - firstSeen) / 86_400_000);
|
|
226
|
+
costPerDay = registryEntry.totalCost / daysObserved;
|
|
227
|
+
requestsPerDay = registryEntry.totalRequests / daysObserved;
|
|
228
|
+
systemPromptPreview = (registryEntry.systemPromptPreview ?? '').slice(0, 80);
|
|
229
|
+
totalRequests = registryEntry.totalRequests;
|
|
230
|
+
// Use registry name if user-renamed (not "Agent N" pattern)
|
|
231
|
+
const isDefaultName = /^Agent \d+$/.test(registryEntry.name);
|
|
232
|
+
if (!isDefaultName) {
|
|
233
|
+
name = registryEntry.name;
|
|
234
|
+
nameIsInferred = false;
|
|
235
|
+
}
|
|
236
|
+
else {
|
|
237
|
+
name = inferAgentName(systemPromptPreview, taskDistribution);
|
|
238
|
+
nameIsInferred = true;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
else {
|
|
242
|
+
// Fingerprint in log but not in agents.json — use log data
|
|
243
|
+
name = inferAgentName(systemPromptPreview, taskDistribution);
|
|
244
|
+
nameIsInferred = true;
|
|
245
|
+
}
|
|
246
|
+
analyses.push({
|
|
247
|
+
fingerprint,
|
|
248
|
+
name,
|
|
249
|
+
nameIsInferred,
|
|
250
|
+
taskDistribution,
|
|
251
|
+
dominantTask,
|
|
252
|
+
avgInputTokens,
|
|
253
|
+
avgOutputTokens,
|
|
254
|
+
avgTotalTokens,
|
|
255
|
+
tokensAreEstimated,
|
|
256
|
+
requestsPerDay,
|
|
257
|
+
costPerDay,
|
|
258
|
+
currentModel,
|
|
259
|
+
daysObserved,
|
|
260
|
+
totalRequests,
|
|
261
|
+
systemPromptPreview,
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
// 5. Sort by costPerDay descending
|
|
265
|
+
analyses.sort((a, b) => b.costPerDay - a.costPerDay);
|
|
266
|
+
return analyses;
|
|
267
|
+
}
|
|
268
|
+
//# sourceMappingURL=policy-analyzer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policy-analyzer.js","sourceRoot":"","sources":["../src/policy-analyzer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Policy Suggestions
|
|
3
|
+
*
|
|
4
|
+
* Takes AgentAnalysis[] and available providers to produce PolicySuggestion[]
|
|
5
|
+
* with model recommendations and savings estimates.
|
|
6
|
+
*/
|
|
7
|
+
import type { AgentAnalysis } from './policy-analyzer.js';
|
|
8
|
+
export interface PolicySuggestion {
|
|
9
|
+
fingerprint: string;
|
|
10
|
+
agentName: string;
|
|
11
|
+
currentModel: string;
|
|
12
|
+
suggestedModel: string;
|
|
13
|
+
escalateTo?: string;
|
|
14
|
+
escalateOn?: Array<'complexity_high' | 'rate_limit' | 'error'>;
|
|
15
|
+
neverDowngrade: boolean;
|
|
16
|
+
reason: string;
|
|
17
|
+
estimatedDailySavings: number;
|
|
18
|
+
estimatedMonthlySavings: number;
|
|
19
|
+
noSuggestion?: boolean;
|
|
20
|
+
noSuggestionReason?: string;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Returns list of provider names (lowercase) where a key is available.
|
|
24
|
+
* Checks both env vars and config file. Deduplicates.
|
|
25
|
+
*/
|
|
26
|
+
export declare function detectAvailableProviders(): string[];
|
|
27
|
+
/**
|
|
28
|
+
* Produce suggestions for all agents.
|
|
29
|
+
*/
|
|
30
|
+
export declare function suggestPolicies(analyses: AgentAnalysis[], availableProviders: string[]): PolicySuggestion[];
|
|
31
|
+
//# sourceMappingURL=policy-suggestions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policy-suggestions.d.ts","sourceRoot":"","sources":["../src/policy-suggestions.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAI1D,MAAM,WAAW,gBAAgB;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,KAAK,CAAC,iBAAiB,GAAG,YAAY,GAAG,OAAO,CAAC,CAAC;IAC/D,cAAc,EAAE,OAAO,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,qBAAqB,EAAE,MAAM,CAAC;IAC9B,uBAAuB,EAAE,MAAM,CAAC;IAChC,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAID;;;GAGG;AACH,wBAAgB,wBAAwB,IAAI,MAAM,EAAE,CAwBnD;AAuID;;GAEG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,aAAa,EAAE,EAAE,kBAAkB,EAAE,MAAM,EAAE,GAAG,gBAAgB,EAAE,CAE3G"}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Policy Suggestions
|
|
4
|
+
*
|
|
5
|
+
* Takes AgentAnalysis[] and available providers to produce PolicySuggestion[]
|
|
6
|
+
* with model recommendations and savings estimates.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.detectAvailableProviders = detectAvailableProviders;
|
|
10
|
+
exports.suggestPolicies = suggestPolicies;
|
|
11
|
+
const config_js_1 = require("./config.js");
|
|
12
|
+
const policy_analyzer_js_1 = require("./policy-analyzer.js");
|
|
13
|
+
// ─── Provider detection ────────────────────────────────────────────────────────
|
|
14
|
+
/**
|
|
15
|
+
* Returns list of provider names (lowercase) where a key is available.
|
|
16
|
+
* Checks both env vars and config file. Deduplicates.
|
|
17
|
+
*/
|
|
18
|
+
function detectAvailableProviders() {
|
|
19
|
+
const providers = new Set();
|
|
20
|
+
// Check environment variables
|
|
21
|
+
if (process.env['ANTHROPIC_API_KEY'])
|
|
22
|
+
providers.add('anthropic');
|
|
23
|
+
if (process.env['OPENAI_API_KEY'])
|
|
24
|
+
providers.add('openai');
|
|
25
|
+
if (process.env['GOOGLE_API_KEY'])
|
|
26
|
+
providers.add('google');
|
|
27
|
+
if (process.env['GEMINI_API_KEY'])
|
|
28
|
+
providers.add('google'); // alias
|
|
29
|
+
if (process.env['GROQ_API_KEY'])
|
|
30
|
+
providers.add('groq');
|
|
31
|
+
if (process.env['OPENROUTER_API_KEY'])
|
|
32
|
+
providers.add('openrouter');
|
|
33
|
+
// Check config file
|
|
34
|
+
try {
|
|
35
|
+
const providerConfigs = (0, config_js_1.getProviderConfigs)();
|
|
36
|
+
for (const [providerName, config] of Object.entries(providerConfigs)) {
|
|
37
|
+
if (config.accounts && config.accounts.length > 0 && config.accounts[0]?.apiKey) {
|
|
38
|
+
providers.add(providerName.toLowerCase());
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
// Config may not exist — that's fine
|
|
44
|
+
}
|
|
45
|
+
return [...providers].sort();
|
|
46
|
+
}
|
|
47
|
+
// ─── Suggestion engine ────────────────────────────────────────────────────────
|
|
48
|
+
/**
|
|
49
|
+
* Returns first candidate model whose provider prefix is in availableProviders.
|
|
50
|
+
* Returns null if none are available.
|
|
51
|
+
*/
|
|
52
|
+
function bestAvailable(candidates, providers) {
|
|
53
|
+
for (const candidate of candidates) {
|
|
54
|
+
const prefix = candidate.split('/')[0] ?? '';
|
|
55
|
+
if (providers.includes(prefix)) {
|
|
56
|
+
return candidate;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Produce a policy suggestion for a single agent.
|
|
63
|
+
*/
|
|
64
|
+
function suggestForAgent(analysis, availableProviders) {
|
|
65
|
+
const { fingerprint, name: agentName, currentModel, taskDistribution, avgTotalTokens, costPerDay, requestsPerDay, avgInputTokens, avgOutputTokens } = analysis;
|
|
66
|
+
let suggestedModel = null;
|
|
67
|
+
let escalateTo;
|
|
68
|
+
let escalateOn;
|
|
69
|
+
let neverDowngrade = false;
|
|
70
|
+
let reason = '';
|
|
71
|
+
// RULE 1 — Long-context: avgTotalTokens > 50_000
|
|
72
|
+
if (avgTotalTokens > 50_000) {
|
|
73
|
+
suggestedModel = bestAvailable([
|
|
74
|
+
'anthropic/claude-opus-4-5', 'anthropic/claude-opus-4',
|
|
75
|
+
'openai/gpt-4o',
|
|
76
|
+
], availableProviders);
|
|
77
|
+
neverDowngrade = true;
|
|
78
|
+
reason = `Long-context patterns (avg ${Math.round(avgTotalTokens / 1000)}K tokens) — keep on full-context model`;
|
|
79
|
+
}
|
|
80
|
+
// RULE 2 — Security/review: review + security >= 0.8
|
|
81
|
+
else if ((taskDistribution['review'] ?? 0) + (taskDistribution['security'] ?? 0) >= 0.8) {
|
|
82
|
+
const pct = Math.round(((taskDistribution['review'] ?? 0) + (taskDistribution['security'] ?? 0)) * 100);
|
|
83
|
+
suggestedModel = bestAvailable([
|
|
84
|
+
'anthropic/claude-opus-4-5', 'anthropic/claude-opus-4',
|
|
85
|
+
], availableProviders) ?? currentModel;
|
|
86
|
+
neverDowngrade = true;
|
|
87
|
+
reason = `High review/security share (${pct}%) — never downgrade for accuracy`;
|
|
88
|
+
}
|
|
89
|
+
// RULE 3 — Code-heavy: code >= 0.8
|
|
90
|
+
else if ((taskDistribution['code'] ?? 0) >= 0.8) {
|
|
91
|
+
const pct = Math.round((taskDistribution['code'] ?? 0) * 100);
|
|
92
|
+
suggestedModel = bestAvailable([
|
|
93
|
+
'anthropic/claude-sonnet-4-5', 'anthropic/claude-sonnet-4', 'openai/gpt-4o',
|
|
94
|
+
], availableProviders);
|
|
95
|
+
escalateTo = bestAvailable([
|
|
96
|
+
'anthropic/claude-opus-4-5', 'anthropic/claude-opus-4',
|
|
97
|
+
], availableProviders) ?? undefined;
|
|
98
|
+
escalateOn = ['complexity_high'];
|
|
99
|
+
neverDowngrade = false;
|
|
100
|
+
reason = `Code-heavy (${pct}%) — sonnet for speed, escalate to opus on complexity`;
|
|
101
|
+
}
|
|
102
|
+
// RULE 4 — Summarization: summarization >= 0.8
|
|
103
|
+
else if ((taskDistribution['summarization'] ?? 0) >= 0.8) {
|
|
104
|
+
const pct = Math.round((taskDistribution['summarization'] ?? 0) * 100);
|
|
105
|
+
suggestedModel = bestAvailable([
|
|
106
|
+
'google/gemini-2.0-flash', 'google/gemini-1.5-flash',
|
|
107
|
+
'anthropic/claude-haiku-4-5', 'openai/gpt-4o-mini',
|
|
108
|
+
], availableProviders);
|
|
109
|
+
neverDowngrade = false;
|
|
110
|
+
reason = `Summarization-heavy (${pct}%) — fast/cheap model`;
|
|
111
|
+
}
|
|
112
|
+
// RULE 5 — Simple/utility: (simple + utility) >= 0.8 AND avgTotalTokens < 5_000
|
|
113
|
+
else if (((taskDistribution['simple'] ?? 0) + (taskDistribution['utility'] ?? 0)) >= 0.8 &&
|
|
114
|
+
avgTotalTokens < 5_000) {
|
|
115
|
+
const pct = Math.round(((taskDistribution['simple'] ?? 0) + (taskDistribution['utility'] ?? 0)) * 100);
|
|
116
|
+
suggestedModel = bestAvailable([
|
|
117
|
+
'groq/llama-3.1-8b-instant', 'groq/llama-3.3-70b',
|
|
118
|
+
'google/gemini-2.0-flash', 'anthropic/claude-haiku-4-5', 'openai/gpt-4o-mini',
|
|
119
|
+
], availableProviders);
|
|
120
|
+
neverDowngrade = false;
|
|
121
|
+
reason = `Simple tasks with low token volume (avg ${Math.round(avgTotalTokens)} tokens) — cheapest capable model`;
|
|
122
|
+
}
|
|
123
|
+
// DEFAULT — no dominant pattern
|
|
124
|
+
else {
|
|
125
|
+
suggestedModel = bestAvailable([
|
|
126
|
+
'anthropic/claude-sonnet-4-5', 'openai/gpt-4o', 'google/gemini-2.0-flash',
|
|
127
|
+
], availableProviders);
|
|
128
|
+
neverDowngrade = false;
|
|
129
|
+
reason = 'Mixed patterns — balanced capability model';
|
|
130
|
+
}
|
|
131
|
+
// Post-rule: handle null or same model
|
|
132
|
+
let noSuggestion;
|
|
133
|
+
let noSuggestionReason;
|
|
134
|
+
if (suggestedModel === null) {
|
|
135
|
+
noSuggestion = true;
|
|
136
|
+
noSuggestionReason = "No available provider for this agent's task profile";
|
|
137
|
+
suggestedModel = currentModel;
|
|
138
|
+
}
|
|
139
|
+
else if (suggestedModel === currentModel) {
|
|
140
|
+
noSuggestion = true;
|
|
141
|
+
noSuggestionReason = 'Already on the recommended model';
|
|
142
|
+
}
|
|
143
|
+
// Compute savings
|
|
144
|
+
const projectedDailyCost = (0, policy_analyzer_js_1.estimateDailyCost)(avgInputTokens, avgOutputTokens, requestsPerDay, suggestedModel);
|
|
145
|
+
const estimatedDailySavings = Math.max(0, costPerDay - projectedDailyCost);
|
|
146
|
+
const estimatedMonthlySavings = estimatedDailySavings * 30;
|
|
147
|
+
const result = {
|
|
148
|
+
fingerprint,
|
|
149
|
+
agentName,
|
|
150
|
+
currentModel,
|
|
151
|
+
suggestedModel,
|
|
152
|
+
neverDowngrade,
|
|
153
|
+
reason,
|
|
154
|
+
estimatedDailySavings,
|
|
155
|
+
estimatedMonthlySavings,
|
|
156
|
+
};
|
|
157
|
+
if (escalateTo)
|
|
158
|
+
result.escalateTo = escalateTo;
|
|
159
|
+
if (escalateOn)
|
|
160
|
+
result.escalateOn = escalateOn;
|
|
161
|
+
if (noSuggestion !== undefined)
|
|
162
|
+
result.noSuggestion = noSuggestion;
|
|
163
|
+
if (noSuggestionReason !== undefined)
|
|
164
|
+
result.noSuggestionReason = noSuggestionReason;
|
|
165
|
+
return result;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Produce suggestions for all agents.
|
|
169
|
+
*/
|
|
170
|
+
function suggestPolicies(analyses, availableProviders) {
|
|
171
|
+
return analyses.map(a => suggestForAgent(a, availableProviders));
|
|
172
|
+
}
|
|
173
|
+
//# sourceMappingURL=policy-suggestions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policy-suggestions.js","sourceRoot":"","sources":["../src/policy-suggestions.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Routing Log
|
|
3
|
+
*
|
|
4
|
+
* In-memory ring buffer (1000 entries) with JSONL persistence at
|
|
5
|
+
* ~/.relayplane/routing-log.jsonl. Tracks all routing decisions for
|
|
6
|
+
* observability and debugging.
|
|
7
|
+
*/
|
|
8
|
+
import type { ResolvedBy } from './agent-policy.js';
|
|
9
|
+
export interface RoutingLogEntry {
|
|
10
|
+
ts: string;
|
|
11
|
+
requestId: string;
|
|
12
|
+
agentFingerprint: string | null;
|
|
13
|
+
agentName: string | null;
|
|
14
|
+
taskType: string;
|
|
15
|
+
complexity: string;
|
|
16
|
+
resolvedModel: string;
|
|
17
|
+
resolvedBy: ResolvedBy;
|
|
18
|
+
candidateModel: string | null;
|
|
19
|
+
reason: string;
|
|
20
|
+
inputTokens?: number;
|
|
21
|
+
outputTokens?: number;
|
|
22
|
+
}
|
|
23
|
+
export declare const LOG_FILE: string;
|
|
24
|
+
/** Reset for testing */
|
|
25
|
+
export declare function _resetRoutingLog(): void;
|
|
26
|
+
/**
|
|
27
|
+
* Initialize routing log by loading the last 1000 lines from file.
|
|
28
|
+
* Called once on proxy startup.
|
|
29
|
+
*/
|
|
30
|
+
export declare function initRoutingLog(): void;
|
|
31
|
+
/**
|
|
32
|
+
* Append a routing decision to the in-memory buffer and JSONL file.
|
|
33
|
+
*/
|
|
34
|
+
export declare function appendRoutingLog(entry: RoutingLogEntry): void;
|
|
35
|
+
/**
|
|
36
|
+
* Get routing log entries from the in-memory buffer with optional filters.
|
|
37
|
+
*/
|
|
38
|
+
export declare function getRoutingLog(opts?: {
|
|
39
|
+
limit?: number;
|
|
40
|
+
agentFingerprint?: string;
|
|
41
|
+
taskType?: string;
|
|
42
|
+
}): RoutingLogEntry[];
|
|
43
|
+
/**
|
|
44
|
+
* No-op flush — writes are synchronous. Called for symmetry on shutdown.
|
|
45
|
+
*/
|
|
46
|
+
export declare function flushRoutingLog(): void;
|
|
47
|
+
//# sourceMappingURL=routing-log.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routing-log.d.ts","sourceRoot":"","sources":["../src/routing-log.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAIpD,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,UAAU,CAAC;IACvB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,MAAM,EAAE,MAAM,CAAC;IAEf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAKD,eAAO,MAAM,QAAQ,QAA0C,CAAC;AAShE,wBAAwB;AACxB,wBAAgB,gBAAgB,IAAI,IAAI,CAEvC;AAID;;;GAGG;AACH,wBAAgB,cAAc,IAAI,IAAI,CAkBrC;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,eAAe,GAAG,IAAI,CAyB7D;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,IAAI,CAAC,EAAE;IACnC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GAAG,eAAe,EAAE,CAcpB;AAED;;GAEG;AACH,wBAAgB,eAAe,IAAI,IAAI,CAEtC"}
|