@relayplane/proxy 0.1.6 → 0.1.8
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 +73 -1
- package/dist/cli.js +243 -34
- package/dist/cli.js.map +1 -1
- package/dist/cli.mjs +243 -34
- package/dist/cli.mjs.map +1 -1
- package/dist/index.d.mts +96 -1
- package/dist/index.d.ts +96 -1
- package/dist/index.js +298 -79
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +292 -78
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -30,6 +30,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/index.ts
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
|
+
DEFAULT_CONFIG: () => DEFAULT_CONFIG,
|
|
33
34
|
DEFAULT_ENDPOINTS: () => DEFAULT_ENDPOINTS,
|
|
34
35
|
MODEL_MAPPING: () => MODEL_MAPPING,
|
|
35
36
|
MODEL_PRICING: () => MODEL_PRICING,
|
|
@@ -44,15 +45,20 @@ __export(index_exports, {
|
|
|
44
45
|
TaskTypes: () => TaskTypes,
|
|
45
46
|
calculateCost: () => calculateCost,
|
|
46
47
|
calculateSavings: () => calculateSavings,
|
|
48
|
+
getConfigPath: () => getConfigPath,
|
|
47
49
|
getInferenceConfidence: () => getInferenceConfidence,
|
|
48
50
|
getModelPricing: () => getModelPricing,
|
|
51
|
+
getStrategy: () => getStrategy,
|
|
49
52
|
inferTaskType: () => inferTaskType,
|
|
50
|
-
|
|
53
|
+
loadConfig: () => loadConfig,
|
|
54
|
+
startProxy: () => startProxy,
|
|
55
|
+
watchConfig: () => watchConfig
|
|
51
56
|
});
|
|
52
57
|
module.exports = __toCommonJS(index_exports);
|
|
53
58
|
|
|
54
59
|
// src/proxy.ts
|
|
55
60
|
var http = __toESM(require("http"));
|
|
61
|
+
var url = __toESM(require("url"));
|
|
56
62
|
|
|
57
63
|
// src/storage/store.ts
|
|
58
64
|
var import_better_sqlite3 = __toESM(require("better-sqlite3"));
|
|
@@ -1622,7 +1628,107 @@ ${input.prompt}` : input.prompt;
|
|
|
1622
1628
|
}
|
|
1623
1629
|
};
|
|
1624
1630
|
|
|
1631
|
+
// src/config.ts
|
|
1632
|
+
var fs2 = __toESM(require("fs"));
|
|
1633
|
+
var path2 = __toESM(require("path"));
|
|
1634
|
+
var os2 = __toESM(require("os"));
|
|
1635
|
+
var import_zod = require("zod");
|
|
1636
|
+
var StrategySchema = import_zod.z.object({
|
|
1637
|
+
model: import_zod.z.string(),
|
|
1638
|
+
minConfidence: import_zod.z.number().min(0).max(1).optional(),
|
|
1639
|
+
fallback: import_zod.z.string().optional()
|
|
1640
|
+
});
|
|
1641
|
+
var ConfigSchema = import_zod.z.object({
|
|
1642
|
+
strategies: import_zod.z.record(import_zod.z.string(), StrategySchema).optional(),
|
|
1643
|
+
defaults: import_zod.z.object({
|
|
1644
|
+
qualityModel: import_zod.z.string().optional(),
|
|
1645
|
+
costModel: import_zod.z.string().optional()
|
|
1646
|
+
}).optional()
|
|
1647
|
+
});
|
|
1648
|
+
var DEFAULT_CONFIG = {
|
|
1649
|
+
strategies: {
|
|
1650
|
+
code_review: { model: "anthropic:claude-sonnet-4-20250514" },
|
|
1651
|
+
code_generation: { model: "anthropic:claude-3-5-haiku-latest" },
|
|
1652
|
+
analysis: { model: "anthropic:claude-sonnet-4-20250514" },
|
|
1653
|
+
summarization: { model: "anthropic:claude-3-5-haiku-latest" },
|
|
1654
|
+
creative_writing: { model: "anthropic:claude-sonnet-4-20250514" },
|
|
1655
|
+
data_extraction: { model: "anthropic:claude-3-5-haiku-latest" },
|
|
1656
|
+
translation: { model: "anthropic:claude-3-5-haiku-latest" },
|
|
1657
|
+
question_answering: { model: "anthropic:claude-3-5-haiku-latest" },
|
|
1658
|
+
general: { model: "anthropic:claude-3-5-haiku-latest" }
|
|
1659
|
+
},
|
|
1660
|
+
defaults: {
|
|
1661
|
+
qualityModel: "claude-sonnet-4-20250514",
|
|
1662
|
+
costModel: "claude-3-5-haiku-latest"
|
|
1663
|
+
}
|
|
1664
|
+
};
|
|
1665
|
+
function getConfigPath() {
|
|
1666
|
+
return path2.join(os2.homedir(), ".relayplane", "config.json");
|
|
1667
|
+
}
|
|
1668
|
+
function writeDefaultConfig() {
|
|
1669
|
+
const configPath = getConfigPath();
|
|
1670
|
+
const dir = path2.dirname(configPath);
|
|
1671
|
+
if (!fs2.existsSync(dir)) {
|
|
1672
|
+
fs2.mkdirSync(dir, { recursive: true });
|
|
1673
|
+
}
|
|
1674
|
+
if (!fs2.existsSync(configPath)) {
|
|
1675
|
+
fs2.writeFileSync(
|
|
1676
|
+
configPath,
|
|
1677
|
+
JSON.stringify(DEFAULT_CONFIG, null, 2) + "\n",
|
|
1678
|
+
"utf-8"
|
|
1679
|
+
);
|
|
1680
|
+
console.log(`[relayplane] Created default config at ${configPath}`);
|
|
1681
|
+
}
|
|
1682
|
+
}
|
|
1683
|
+
function loadConfig() {
|
|
1684
|
+
const configPath = getConfigPath();
|
|
1685
|
+
writeDefaultConfig();
|
|
1686
|
+
try {
|
|
1687
|
+
const raw = fs2.readFileSync(configPath, "utf-8");
|
|
1688
|
+
const parsed = JSON.parse(raw);
|
|
1689
|
+
const validated = ConfigSchema.parse(parsed);
|
|
1690
|
+
return validated;
|
|
1691
|
+
} catch (err) {
|
|
1692
|
+
if (err instanceof import_zod.z.ZodError) {
|
|
1693
|
+
console.error(`[relayplane] Invalid config: ${err.message}`);
|
|
1694
|
+
} else if (err instanceof SyntaxError) {
|
|
1695
|
+
console.error(`[relayplane] Config JSON parse error: ${err.message}`);
|
|
1696
|
+
} else {
|
|
1697
|
+
console.error(`[relayplane] Failed to load config: ${err}`);
|
|
1698
|
+
}
|
|
1699
|
+
console.log("[relayplane] Using default config");
|
|
1700
|
+
return DEFAULT_CONFIG;
|
|
1701
|
+
}
|
|
1702
|
+
}
|
|
1703
|
+
function getStrategy(config, taskType) {
|
|
1704
|
+
return config.strategies?.[taskType] ?? null;
|
|
1705
|
+
}
|
|
1706
|
+
function watchConfig(onChange) {
|
|
1707
|
+
const configPath = getConfigPath();
|
|
1708
|
+
const dir = path2.dirname(configPath);
|
|
1709
|
+
if (!fs2.existsSync(dir)) {
|
|
1710
|
+
fs2.mkdirSync(dir, { recursive: true });
|
|
1711
|
+
}
|
|
1712
|
+
let debounceTimer = null;
|
|
1713
|
+
fs2.watch(dir, (eventType, filename) => {
|
|
1714
|
+
if (filename === "config.json") {
|
|
1715
|
+
if (debounceTimer) clearTimeout(debounceTimer);
|
|
1716
|
+
debounceTimer = setTimeout(() => {
|
|
1717
|
+
console.log("[relayplane] Config file changed, reloading...");
|
|
1718
|
+
const newConfig = loadConfig();
|
|
1719
|
+
onChange(newConfig);
|
|
1720
|
+
}, 100);
|
|
1721
|
+
}
|
|
1722
|
+
});
|
|
1723
|
+
}
|
|
1724
|
+
|
|
1625
1725
|
// src/proxy.ts
|
|
1726
|
+
var VERSION = "0.1.8";
|
|
1727
|
+
var recentRuns = [];
|
|
1728
|
+
var MAX_RECENT_RUNS = 100;
|
|
1729
|
+
var modelCounts = {};
|
|
1730
|
+
var serverStartTime = 0;
|
|
1731
|
+
var currentConfig = loadConfig();
|
|
1626
1732
|
var DEFAULT_ENDPOINTS = {
|
|
1627
1733
|
anthropic: {
|
|
1628
1734
|
baseUrl: "https://api.anthropic.com/v1",
|
|
@@ -1919,9 +2025,9 @@ function convertMessagesToGemini(messages) {
|
|
|
1919
2025
|
return { text: p.text };
|
|
1920
2026
|
}
|
|
1921
2027
|
if (p.type === "image_url" && p.image_url?.url) {
|
|
1922
|
-
const
|
|
1923
|
-
if (
|
|
1924
|
-
const match =
|
|
2028
|
+
const url2 = p.image_url.url;
|
|
2029
|
+
if (url2.startsWith("data:")) {
|
|
2030
|
+
const match = url2.match(/^data:([^;]+);base64,(.+)$/);
|
|
1925
2031
|
if (match) {
|
|
1926
2032
|
return {
|
|
1927
2033
|
inline_data: {
|
|
@@ -1931,7 +2037,7 @@ function convertMessagesToGemini(messages) {
|
|
|
1931
2037
|
};
|
|
1932
2038
|
}
|
|
1933
2039
|
}
|
|
1934
|
-
return { text: `[Image: ${
|
|
2040
|
+
return { text: `[Image: ${url2}]` };
|
|
1935
2041
|
}
|
|
1936
2042
|
return { text: "" };
|
|
1937
2043
|
});
|
|
@@ -2345,28 +2451,88 @@ async function startProxy(config = {}) {
|
|
|
2345
2451
|
};
|
|
2346
2452
|
const server = http.createServer(async (req, res) => {
|
|
2347
2453
|
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
2348
|
-
res.setHeader("Access-Control-Allow-Methods", "POST, OPTIONS");
|
|
2454
|
+
res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
|
|
2349
2455
|
res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
|
|
2350
2456
|
if (req.method === "OPTIONS") {
|
|
2351
2457
|
res.writeHead(204);
|
|
2352
2458
|
res.end();
|
|
2353
2459
|
return;
|
|
2354
2460
|
}
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2461
|
+
const parsedUrl = url.parse(req.url || "", true);
|
|
2462
|
+
const pathname = parsedUrl.pathname || "";
|
|
2463
|
+
if (req.method === "GET" && pathname === "/health") {
|
|
2464
|
+
const uptimeMs = Date.now() - serverStartTime;
|
|
2465
|
+
const uptimeSecs = Math.floor(uptimeMs / 1e3);
|
|
2466
|
+
const hours = Math.floor(uptimeSecs / 3600);
|
|
2467
|
+
const mins = Math.floor(uptimeSecs % 3600 / 60);
|
|
2468
|
+
const secs = uptimeSecs % 60;
|
|
2469
|
+
const providers = {};
|
|
2470
|
+
for (const [name, config2] of Object.entries(DEFAULT_ENDPOINTS)) {
|
|
2471
|
+
providers[name] = !!process.env[config2.apiKeyEnv];
|
|
2472
|
+
}
|
|
2473
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
2474
|
+
res.end(JSON.stringify({
|
|
2475
|
+
status: "ok",
|
|
2476
|
+
version: VERSION,
|
|
2477
|
+
uptime: `${hours}h ${mins}m ${secs}s`,
|
|
2478
|
+
uptimeMs,
|
|
2479
|
+
providers,
|
|
2480
|
+
totalRuns: recentRuns.length > 0 ? Object.values(modelCounts).reduce((a, b) => a + b, 0) : 0
|
|
2481
|
+
}));
|
|
2482
|
+
return;
|
|
2483
|
+
}
|
|
2484
|
+
if (req.method === "GET" && pathname === "/stats") {
|
|
2485
|
+
const stats = relay.stats();
|
|
2486
|
+
const savings = relay.savingsReport(30);
|
|
2487
|
+
const totalRuns = Object.values(modelCounts).reduce((a, b) => a + b, 0);
|
|
2488
|
+
const modelDistribution = {};
|
|
2489
|
+
for (const [model, count] of Object.entries(modelCounts)) {
|
|
2490
|
+
modelDistribution[model] = {
|
|
2491
|
+
count,
|
|
2492
|
+
percentage: totalRuns > 0 ? (count / totalRuns * 100).toFixed(1) + "%" : "0%"
|
|
2493
|
+
};
|
|
2369
2494
|
}
|
|
2495
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
2496
|
+
res.end(JSON.stringify({
|
|
2497
|
+
totalRuns,
|
|
2498
|
+
savings: {
|
|
2499
|
+
estimatedSavingsPercent: savings.savingsPercent.toFixed(1) + "%",
|
|
2500
|
+
actualCostUsd: savings.actualCost.toFixed(4),
|
|
2501
|
+
baselineCostUsd: savings.baselineCost.toFixed(4),
|
|
2502
|
+
savedUsd: savings.savings.toFixed(4)
|
|
2503
|
+
},
|
|
2504
|
+
modelDistribution,
|
|
2505
|
+
byTaskType: stats.byTaskType,
|
|
2506
|
+
period: stats.period
|
|
2507
|
+
}));
|
|
2508
|
+
return;
|
|
2509
|
+
}
|
|
2510
|
+
if (req.method === "GET" && pathname === "/runs") {
|
|
2511
|
+
const limitParam = parsedUrl.query["limit"];
|
|
2512
|
+
const parsedLimit = limitParam ? parseInt(String(limitParam), 10) : 20;
|
|
2513
|
+
const limit = Math.min(Number.isNaN(parsedLimit) ? 20 : parsedLimit, MAX_RECENT_RUNS);
|
|
2514
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
2515
|
+
res.end(JSON.stringify({
|
|
2516
|
+
runs: recentRuns.slice(0, limit),
|
|
2517
|
+
total: recentRuns.length
|
|
2518
|
+
}));
|
|
2519
|
+
return;
|
|
2520
|
+
}
|
|
2521
|
+
if (req.method === "GET" && pathname.includes("/models")) {
|
|
2522
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
2523
|
+
res.end(
|
|
2524
|
+
JSON.stringify({
|
|
2525
|
+
object: "list",
|
|
2526
|
+
data: [
|
|
2527
|
+
{ id: "relayplane:auto", object: "model", owned_by: "relayplane" },
|
|
2528
|
+
{ id: "relayplane:cost", object: "model", owned_by: "relayplane" },
|
|
2529
|
+
{ id: "relayplane:quality", object: "model", owned_by: "relayplane" }
|
|
2530
|
+
]
|
|
2531
|
+
})
|
|
2532
|
+
);
|
|
2533
|
+
return;
|
|
2534
|
+
}
|
|
2535
|
+
if (req.method !== "POST" || !pathname.includes("/chat/completions")) {
|
|
2370
2536
|
res.writeHead(404, { "Content-Type": "application/json" });
|
|
2371
2537
|
res.end(JSON.stringify({ error: "Not found" }));
|
|
2372
2538
|
return;
|
|
@@ -2413,33 +2579,44 @@ async function startProxy(config = {}) {
|
|
|
2413
2579
|
const confidence = getInferenceConfidence(promptText, taskType);
|
|
2414
2580
|
log(`Inferred task: ${taskType} (confidence: ${confidence.toFixed(2)})`);
|
|
2415
2581
|
if (routingMode !== "passthrough") {
|
|
2416
|
-
const
|
|
2417
|
-
if (
|
|
2418
|
-
const parsed = parsePreferredModel(
|
|
2582
|
+
const configStrategy = getStrategy(currentConfig, taskType);
|
|
2583
|
+
if (configStrategy) {
|
|
2584
|
+
const parsed = parsePreferredModel(configStrategy.model);
|
|
2419
2585
|
if (parsed) {
|
|
2420
2586
|
targetProvider = parsed.provider;
|
|
2421
2587
|
targetModel = parsed.model;
|
|
2422
|
-
log(`Using
|
|
2588
|
+
log(`Using config strategy: ${configStrategy.model}`);
|
|
2589
|
+
}
|
|
2590
|
+
}
|
|
2591
|
+
if (!configStrategy) {
|
|
2592
|
+
const rule = relay.routing.get(taskType);
|
|
2593
|
+
if (rule && rule.preferredModel) {
|
|
2594
|
+
const parsed = parsePreferredModel(rule.preferredModel);
|
|
2595
|
+
if (parsed) {
|
|
2596
|
+
targetProvider = parsed.provider;
|
|
2597
|
+
targetModel = parsed.model;
|
|
2598
|
+
log(`Using learned rule: ${rule.preferredModel}`);
|
|
2599
|
+
} else {
|
|
2600
|
+
const defaultRoute = DEFAULT_ROUTING[taskType];
|
|
2601
|
+
targetProvider = defaultRoute.provider;
|
|
2602
|
+
targetModel = defaultRoute.model;
|
|
2603
|
+
}
|
|
2423
2604
|
} else {
|
|
2424
2605
|
const defaultRoute = DEFAULT_ROUTING[taskType];
|
|
2425
2606
|
targetProvider = defaultRoute.provider;
|
|
2426
2607
|
targetModel = defaultRoute.model;
|
|
2427
2608
|
}
|
|
2428
|
-
} else {
|
|
2429
|
-
const defaultRoute = DEFAULT_ROUTING[taskType];
|
|
2430
|
-
targetProvider = defaultRoute.provider;
|
|
2431
|
-
targetModel = defaultRoute.model;
|
|
2432
2609
|
}
|
|
2433
2610
|
if (routingMode === "cost") {
|
|
2434
|
-
const
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
}
|
|
2611
|
+
const costModel = currentConfig.defaults?.costModel || "claude-3-5-haiku-latest";
|
|
2612
|
+
targetModel = costModel;
|
|
2613
|
+
targetProvider = "anthropic";
|
|
2614
|
+
log(`Cost mode: using ${costModel}`);
|
|
2439
2615
|
} else if (routingMode === "quality") {
|
|
2440
|
-
const qualityModel = process.env["RELAYPLANE_QUALITY_MODEL"] || "claude-sonnet-4-20250514";
|
|
2616
|
+
const qualityModel = currentConfig.defaults?.qualityModel || process.env["RELAYPLANE_QUALITY_MODEL"] || "claude-sonnet-4-20250514";
|
|
2441
2617
|
targetModel = qualityModel;
|
|
2442
2618
|
targetProvider = "anthropic";
|
|
2619
|
+
log(`Quality mode: using ${qualityModel}`);
|
|
2443
2620
|
}
|
|
2444
2621
|
}
|
|
2445
2622
|
log(`Routing to: ${targetProvider}/${targetModel}`);
|
|
@@ -2486,12 +2663,19 @@ async function startProxy(config = {}) {
|
|
|
2486
2663
|
);
|
|
2487
2664
|
}
|
|
2488
2665
|
});
|
|
2666
|
+
watchConfig((newConfig) => {
|
|
2667
|
+
currentConfig = newConfig;
|
|
2668
|
+
console.log("[relayplane] Config reloaded");
|
|
2669
|
+
});
|
|
2489
2670
|
return new Promise((resolve, reject) => {
|
|
2490
2671
|
server.on("error", reject);
|
|
2491
2672
|
server.listen(port, host, () => {
|
|
2673
|
+
serverStartTime = Date.now();
|
|
2492
2674
|
console.log(`RelayPlane proxy listening on http://${host}:${port}`);
|
|
2493
2675
|
console.log(` Models: relayplane:auto, relayplane:cost, relayplane:quality`);
|
|
2494
2676
|
console.log(` Endpoint: POST /v1/chat/completions`);
|
|
2677
|
+
console.log(` Stats: GET /stats, /runs, /health`);
|
|
2678
|
+
console.log(` Config: ~/.relayplane/config.json (hot-reload enabled)`);
|
|
2495
2679
|
console.log(` Streaming: \u2705 Enabled`);
|
|
2496
2680
|
resolve(server);
|
|
2497
2681
|
});
|
|
@@ -2554,11 +2738,26 @@ async function handleStreamingRequest(res, request, targetProvider, targetModel,
|
|
|
2554
2738
|
log(`Streaming error: ${err}`);
|
|
2555
2739
|
}
|
|
2556
2740
|
const durationMs = Date.now() - startTime;
|
|
2741
|
+
const modelKey = `${targetProvider}/${targetModel}`;
|
|
2742
|
+
modelCounts[modelKey] = (modelCounts[modelKey] || 0) + 1;
|
|
2557
2743
|
relay.run({
|
|
2558
2744
|
prompt: promptText.slice(0, 500),
|
|
2559
2745
|
taskType,
|
|
2560
2746
|
model: `${targetProvider}:${targetModel}`
|
|
2561
2747
|
}).then((runResult) => {
|
|
2748
|
+
recentRuns.unshift({
|
|
2749
|
+
runId: runResult.runId,
|
|
2750
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2751
|
+
model: modelKey,
|
|
2752
|
+
taskType,
|
|
2753
|
+
confidence,
|
|
2754
|
+
mode: routingMode,
|
|
2755
|
+
durationMs,
|
|
2756
|
+
promptPreview: promptText.slice(0, 100) + (promptText.length > 100 ? "..." : "")
|
|
2757
|
+
});
|
|
2758
|
+
if (recentRuns.length > MAX_RECENT_RUNS) {
|
|
2759
|
+
recentRuns.pop();
|
|
2760
|
+
}
|
|
2562
2761
|
log(`Completed streaming in ${durationMs}ms, runId: ${runResult.runId}`);
|
|
2563
2762
|
}).catch((err) => {
|
|
2564
2763
|
log(`Failed to record run: ${err}`);
|
|
@@ -2629,15 +2828,30 @@ async function handleNonStreamingRequest(res, request, targetProvider, targetMod
|
|
|
2629
2828
|
return;
|
|
2630
2829
|
}
|
|
2631
2830
|
const durationMs = Date.now() - startTime;
|
|
2831
|
+
const modelKey = `${targetProvider}/${targetModel}`;
|
|
2832
|
+
modelCounts[modelKey] = (modelCounts[modelKey] || 0) + 1;
|
|
2632
2833
|
try {
|
|
2633
2834
|
const runResult = await relay.run({
|
|
2634
2835
|
prompt: promptText.slice(0, 500),
|
|
2635
2836
|
taskType,
|
|
2636
2837
|
model: `${targetProvider}:${targetModel}`
|
|
2637
2838
|
});
|
|
2839
|
+
recentRuns.unshift({
|
|
2840
|
+
runId: runResult.runId,
|
|
2841
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2842
|
+
model: modelKey,
|
|
2843
|
+
taskType,
|
|
2844
|
+
confidence,
|
|
2845
|
+
mode: routingMode,
|
|
2846
|
+
durationMs,
|
|
2847
|
+
promptPreview: promptText.slice(0, 100) + (promptText.length > 100 ? "..." : "")
|
|
2848
|
+
});
|
|
2849
|
+
if (recentRuns.length > MAX_RECENT_RUNS) {
|
|
2850
|
+
recentRuns.pop();
|
|
2851
|
+
}
|
|
2638
2852
|
responseData["_relayplane"] = {
|
|
2639
2853
|
runId: runResult.runId,
|
|
2640
|
-
routedTo:
|
|
2854
|
+
routedTo: modelKey,
|
|
2641
2855
|
taskType,
|
|
2642
2856
|
confidence,
|
|
2643
2857
|
durationMs,
|
|
@@ -2652,7 +2866,7 @@ async function handleNonStreamingRequest(res, request, targetProvider, targetMod
|
|
|
2652
2866
|
}
|
|
2653
2867
|
|
|
2654
2868
|
// src/types.ts
|
|
2655
|
-
var
|
|
2869
|
+
var import_zod2 = require("zod");
|
|
2656
2870
|
var TaskTypes = [
|
|
2657
2871
|
"code_generation",
|
|
2658
2872
|
"code_review",
|
|
@@ -2664,64 +2878,65 @@ var TaskTypes = [
|
|
|
2664
2878
|
"question_answering",
|
|
2665
2879
|
"general"
|
|
2666
2880
|
];
|
|
2667
|
-
var TaskTypeSchema =
|
|
2881
|
+
var TaskTypeSchema = import_zod2.z.enum(TaskTypes);
|
|
2668
2882
|
var Providers = ["openai", "anthropic", "google", "xai", "moonshot", "local"];
|
|
2669
|
-
var ProviderSchema =
|
|
2670
|
-
var RelayPlaneConfigSchema =
|
|
2671
|
-
dbPath:
|
|
2672
|
-
providers:
|
|
2673
|
-
apiKey:
|
|
2674
|
-
baseUrl:
|
|
2883
|
+
var ProviderSchema = import_zod2.z.enum(Providers);
|
|
2884
|
+
var RelayPlaneConfigSchema = import_zod2.z.object({
|
|
2885
|
+
dbPath: import_zod2.z.string().optional(),
|
|
2886
|
+
providers: import_zod2.z.record(ProviderSchema, import_zod2.z.object({
|
|
2887
|
+
apiKey: import_zod2.z.string().optional(),
|
|
2888
|
+
baseUrl: import_zod2.z.string().optional()
|
|
2675
2889
|
})).optional(),
|
|
2676
2890
|
defaultProvider: ProviderSchema.optional(),
|
|
2677
|
-
defaultModel:
|
|
2891
|
+
defaultModel: import_zod2.z.string().optional()
|
|
2678
2892
|
});
|
|
2679
|
-
var RunInputSchema =
|
|
2680
|
-
prompt:
|
|
2681
|
-
systemPrompt:
|
|
2893
|
+
var RunInputSchema = import_zod2.z.object({
|
|
2894
|
+
prompt: import_zod2.z.string().min(1),
|
|
2895
|
+
systemPrompt: import_zod2.z.string().optional(),
|
|
2682
2896
|
taskType: TaskTypeSchema.optional(),
|
|
2683
|
-
model:
|
|
2684
|
-
metadata:
|
|
2897
|
+
model: import_zod2.z.string().optional(),
|
|
2898
|
+
metadata: import_zod2.z.record(import_zod2.z.unknown()).optional()
|
|
2685
2899
|
});
|
|
2686
2900
|
var RuleSources = ["default", "user", "learned"];
|
|
2687
|
-
var RoutingRuleSchema =
|
|
2688
|
-
id:
|
|
2901
|
+
var RoutingRuleSchema = import_zod2.z.object({
|
|
2902
|
+
id: import_zod2.z.string(),
|
|
2689
2903
|
taskType: TaskTypeSchema,
|
|
2690
|
-
preferredModel:
|
|
2691
|
-
source:
|
|
2692
|
-
confidence:
|
|
2693
|
-
sampleCount:
|
|
2694
|
-
createdAt:
|
|
2695
|
-
updatedAt:
|
|
2904
|
+
preferredModel: import_zod2.z.string(),
|
|
2905
|
+
source: import_zod2.z.enum(RuleSources),
|
|
2906
|
+
confidence: import_zod2.z.number().min(0).max(1).optional(),
|
|
2907
|
+
sampleCount: import_zod2.z.number().int().positive().optional(),
|
|
2908
|
+
createdAt: import_zod2.z.string(),
|
|
2909
|
+
updatedAt: import_zod2.z.string()
|
|
2696
2910
|
});
|
|
2697
2911
|
var OutcomeQualities = ["excellent", "good", "acceptable", "poor", "failed"];
|
|
2698
|
-
var OutcomeInputSchema =
|
|
2699
|
-
runId:
|
|
2700
|
-
success:
|
|
2701
|
-
quality:
|
|
2702
|
-
latencySatisfactory:
|
|
2703
|
-
costSatisfactory:
|
|
2704
|
-
feedback:
|
|
2912
|
+
var OutcomeInputSchema = import_zod2.z.object({
|
|
2913
|
+
runId: import_zod2.z.string().min(1),
|
|
2914
|
+
success: import_zod2.z.boolean(),
|
|
2915
|
+
quality: import_zod2.z.enum(OutcomeQualities).optional(),
|
|
2916
|
+
latencySatisfactory: import_zod2.z.boolean().optional(),
|
|
2917
|
+
costSatisfactory: import_zod2.z.boolean().optional(),
|
|
2918
|
+
feedback: import_zod2.z.string().optional()
|
|
2705
2919
|
});
|
|
2706
|
-
var SuggestionSchema =
|
|
2707
|
-
id:
|
|
2920
|
+
var SuggestionSchema = import_zod2.z.object({
|
|
2921
|
+
id: import_zod2.z.string(),
|
|
2708
2922
|
taskType: TaskTypeSchema,
|
|
2709
|
-
currentModel:
|
|
2710
|
-
suggestedModel:
|
|
2711
|
-
reason:
|
|
2712
|
-
confidence:
|
|
2713
|
-
expectedImprovement:
|
|
2714
|
-
successRate:
|
|
2715
|
-
latency:
|
|
2716
|
-
cost:
|
|
2923
|
+
currentModel: import_zod2.z.string(),
|
|
2924
|
+
suggestedModel: import_zod2.z.string(),
|
|
2925
|
+
reason: import_zod2.z.string(),
|
|
2926
|
+
confidence: import_zod2.z.number().min(0).max(1),
|
|
2927
|
+
expectedImprovement: import_zod2.z.object({
|
|
2928
|
+
successRate: import_zod2.z.number().optional(),
|
|
2929
|
+
latency: import_zod2.z.number().optional(),
|
|
2930
|
+
cost: import_zod2.z.number().optional()
|
|
2717
2931
|
}),
|
|
2718
|
-
sampleCount:
|
|
2719
|
-
createdAt:
|
|
2720
|
-
accepted:
|
|
2721
|
-
acceptedAt:
|
|
2932
|
+
sampleCount: import_zod2.z.number().int().positive(),
|
|
2933
|
+
createdAt: import_zod2.z.string(),
|
|
2934
|
+
accepted: import_zod2.z.boolean().optional(),
|
|
2935
|
+
acceptedAt: import_zod2.z.string().optional()
|
|
2722
2936
|
});
|
|
2723
2937
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2724
2938
|
0 && (module.exports = {
|
|
2939
|
+
DEFAULT_CONFIG,
|
|
2725
2940
|
DEFAULT_ENDPOINTS,
|
|
2726
2941
|
MODEL_MAPPING,
|
|
2727
2942
|
MODEL_PRICING,
|
|
@@ -2736,9 +2951,13 @@ var SuggestionSchema = import_zod.z.object({
|
|
|
2736
2951
|
TaskTypes,
|
|
2737
2952
|
calculateCost,
|
|
2738
2953
|
calculateSavings,
|
|
2954
|
+
getConfigPath,
|
|
2739
2955
|
getInferenceConfidence,
|
|
2740
2956
|
getModelPricing,
|
|
2957
|
+
getStrategy,
|
|
2741
2958
|
inferTaskType,
|
|
2742
|
-
|
|
2959
|
+
loadConfig,
|
|
2960
|
+
startProxy,
|
|
2961
|
+
watchConfig
|
|
2743
2962
|
});
|
|
2744
2963
|
//# sourceMappingURL=index.js.map
|