@relayplane/proxy 0.1.4 → 0.1.6
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 +25 -3
- package/dist/cli.js +112 -15
- package/dist/cli.js.map +1 -1
- package/dist/cli.mjs +112 -15
- package/dist/cli.mjs.map +1 -1
- package/dist/index.js +13 -8
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +13 -8
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -132,11 +132,13 @@ CREATE TABLE IF NOT EXISTS schema_version (
|
|
|
132
132
|
INSERT OR IGNORE INTO schema_version (version) VALUES (1);
|
|
133
133
|
`;
|
|
134
134
|
var DEFAULT_ROUTING_RULES = [
|
|
135
|
-
|
|
136
|
-
{ taskType: "
|
|
135
|
+
// Complex tasks → Sonnet (need reasoning & quality)
|
|
136
|
+
{ taskType: "code_generation", preferredModel: "anthropic:claude-sonnet-4-20250514" },
|
|
137
|
+
{ taskType: "code_review", preferredModel: "anthropic:claude-sonnet-4-20250514" },
|
|
138
|
+
{ taskType: "analysis", preferredModel: "anthropic:claude-sonnet-4-20250514" },
|
|
139
|
+
{ taskType: "creative_writing", preferredModel: "anthropic:claude-sonnet-4-20250514" },
|
|
140
|
+
// Simple tasks → Haiku (cost efficient)
|
|
137
141
|
{ taskType: "summarization", preferredModel: "anthropic:claude-3-5-haiku-latest" },
|
|
138
|
-
{ taskType: "analysis", preferredModel: "anthropic:claude-3-5-haiku-latest" },
|
|
139
|
-
{ taskType: "creative_writing", preferredModel: "anthropic:claude-3-5-haiku-latest" },
|
|
140
142
|
{ taskType: "data_extraction", preferredModel: "anthropic:claude-3-5-haiku-latest" },
|
|
141
143
|
{ taskType: "translation", preferredModel: "anthropic:claude-3-5-haiku-latest" },
|
|
142
144
|
{ taskType: "question_answering", preferredModel: "anthropic:claude-3-5-haiku-latest" },
|
|
@@ -1603,11 +1605,14 @@ var MODEL_MAPPING = {
|
|
|
1603
1605
|
"gpt-4.1": { provider: "openai", model: "gpt-4.1" }
|
|
1604
1606
|
};
|
|
1605
1607
|
var DEFAULT_ROUTING = {
|
|
1606
|
-
|
|
1607
|
-
code_review: { provider: "anthropic", model: "claude-
|
|
1608
|
+
// Complex tasks → Sonnet (need reasoning & quality)
|
|
1609
|
+
code_review: { provider: "anthropic", model: "claude-sonnet-4-20250514" },
|
|
1610
|
+
analysis: { provider: "anthropic", model: "claude-sonnet-4-20250514" },
|
|
1611
|
+
creative_writing: { provider: "anthropic", model: "claude-sonnet-4-20250514" },
|
|
1612
|
+
// Medium tasks → Sonnet (benefit from better model)
|
|
1613
|
+
code_generation: { provider: "anthropic", model: "claude-sonnet-4-20250514" },
|
|
1614
|
+
// Simple tasks → Haiku (cost efficient)
|
|
1608
1615
|
summarization: { provider: "anthropic", model: "claude-3-5-haiku-latest" },
|
|
1609
|
-
analysis: { provider: "anthropic", model: "claude-3-5-haiku-latest" },
|
|
1610
|
-
creative_writing: { provider: "anthropic", model: "claude-3-5-haiku-latest" },
|
|
1611
1616
|
data_extraction: { provider: "anthropic", model: "claude-3-5-haiku-latest" },
|
|
1612
1617
|
translation: { provider: "anthropic", model: "claude-3-5-haiku-latest" },
|
|
1613
1618
|
question_answering: { provider: "anthropic", model: "claude-3-5-haiku-latest" },
|
|
@@ -2592,18 +2597,28 @@ async function handleNonStreamingRequest(res, request, targetProvider, targetMod
|
|
|
2592
2597
|
}
|
|
2593
2598
|
|
|
2594
2599
|
// src/cli.ts
|
|
2600
|
+
import Database2 from "better-sqlite3";
|
|
2595
2601
|
function printHelp() {
|
|
2596
2602
|
console.log(`
|
|
2597
2603
|
RelayPlane Proxy - Intelligent AI Model Routing
|
|
2598
2604
|
|
|
2599
2605
|
Usage:
|
|
2600
|
-
npx @relayplane/proxy [options]
|
|
2601
|
-
relayplane-proxy [options]
|
|
2606
|
+
npx @relayplane/proxy [command] [options]
|
|
2607
|
+
relayplane-proxy [command] [options]
|
|
2602
2608
|
|
|
2603
|
-
|
|
2609
|
+
Commands:
|
|
2610
|
+
(default) Start the proxy server
|
|
2611
|
+
stats Show routing statistics
|
|
2612
|
+
|
|
2613
|
+
Server Options:
|
|
2604
2614
|
--port <number> Port to listen on (default: 3001)
|
|
2605
2615
|
--host <string> Host to bind to (default: 127.0.0.1)
|
|
2606
2616
|
-v, --verbose Enable verbose logging
|
|
2617
|
+
|
|
2618
|
+
Stats Options:
|
|
2619
|
+
--days <number> Days of history to show (default: 7)
|
|
2620
|
+
|
|
2621
|
+
General:
|
|
2607
2622
|
-h, --help Show this help message
|
|
2608
2623
|
|
|
2609
2624
|
Environment Variables:
|
|
@@ -2613,26 +2628,108 @@ Environment Variables:
|
|
|
2613
2628
|
XAI_API_KEY xAI/Grok API key (optional)
|
|
2614
2629
|
MOONSHOT_API_KEY Moonshot API key (optional)
|
|
2615
2630
|
|
|
2616
|
-
|
|
2631
|
+
Examples:
|
|
2617
2632
|
# Start proxy on default port
|
|
2618
2633
|
npx @relayplane/proxy
|
|
2619
2634
|
|
|
2620
2635
|
# Start on custom port with verbose logging
|
|
2621
2636
|
npx @relayplane/proxy --port 8080 -v
|
|
2622
2637
|
|
|
2623
|
-
#
|
|
2624
|
-
|
|
2625
|
-
|
|
2638
|
+
# View routing stats for last 7 days
|
|
2639
|
+
npx @relayplane/proxy stats
|
|
2640
|
+
|
|
2641
|
+
# View stats for last 30 days
|
|
2642
|
+
npx @relayplane/proxy stats --days 30
|
|
2626
2643
|
|
|
2627
2644
|
Learn more: https://relayplane.com/integrations/openclaw
|
|
2628
2645
|
`);
|
|
2629
2646
|
}
|
|
2647
|
+
function showStats(days) {
|
|
2648
|
+
const dbPath = getDefaultDbPath();
|
|
2649
|
+
try {
|
|
2650
|
+
const db = new Database2(dbPath, { readonly: true });
|
|
2651
|
+
const cutoff = new Date(Date.now() - days * 24 * 60 * 60 * 1e3).toISOString();
|
|
2652
|
+
const runs = db.prepare(`
|
|
2653
|
+
SELECT
|
|
2654
|
+
model,
|
|
2655
|
+
task_type,
|
|
2656
|
+
COUNT(*) as count,
|
|
2657
|
+
SUM(tokens_in) as total_in,
|
|
2658
|
+
SUM(tokens_out) as total_out,
|
|
2659
|
+
AVG(duration_ms) as avg_duration,
|
|
2660
|
+
SUM(CASE WHEN success = 1 THEN 1 ELSE 0 END) as successes
|
|
2661
|
+
FROM runs
|
|
2662
|
+
WHERE created_at >= ?
|
|
2663
|
+
GROUP BY model
|
|
2664
|
+
ORDER BY count DESC
|
|
2665
|
+
`).all(cutoff);
|
|
2666
|
+
const totalRuns = runs.reduce((sum, r) => sum + r.count, 0);
|
|
2667
|
+
const totalTokensIn = runs.reduce((sum, r) => sum + (r.total_in || 0), 0);
|
|
2668
|
+
const totalTokensOut = runs.reduce((sum, r) => sum + (r.total_out || 0), 0);
|
|
2669
|
+
console.log("");
|
|
2670
|
+
console.log(` \u256D\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256E`);
|
|
2671
|
+
console.log(` \u2502 RelayPlane Routing Stats \u2502`);
|
|
2672
|
+
console.log(` \u2502 Last ${String(days).padStart(2)} days \u2502`);
|
|
2673
|
+
console.log(` \u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256F`);
|
|
2674
|
+
console.log("");
|
|
2675
|
+
if (totalRuns === 0) {
|
|
2676
|
+
console.log(" No routing data found for this period.");
|
|
2677
|
+
console.log(" Start using the proxy to collect stats!");
|
|
2678
|
+
console.log("");
|
|
2679
|
+
return;
|
|
2680
|
+
}
|
|
2681
|
+
console.log(" Summary:");
|
|
2682
|
+
console.log(` Total requests: ${totalRuns.toLocaleString()}`);
|
|
2683
|
+
console.log(` Total tokens in: ${totalTokensIn.toLocaleString()}`);
|
|
2684
|
+
console.log(` Total tokens out: ${totalTokensOut.toLocaleString()}`);
|
|
2685
|
+
console.log("");
|
|
2686
|
+
console.log(" By Model:");
|
|
2687
|
+
console.log(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
2688
|
+
for (const row of runs) {
|
|
2689
|
+
const pct = (row.count / totalRuns * 100).toFixed(1);
|
|
2690
|
+
const successRate = row.count > 0 ? (row.successes / row.count * 100).toFixed(0) : "0";
|
|
2691
|
+
console.log(` ${row.model.padEnd(35)} ${String(row.count).padStart(6)} (${pct.padStart(5)}%) ${successRate}% ok`);
|
|
2692
|
+
}
|
|
2693
|
+
console.log("");
|
|
2694
|
+
const haikuRuns = runs.filter((r) => r.model.includes("haiku"));
|
|
2695
|
+
const haikuTokensIn = haikuRuns.reduce((sum, r) => sum + (r.total_in || 0), 0);
|
|
2696
|
+
const haikuTokensOut = haikuRuns.reduce((sum, r) => sum + (r.total_out || 0), 0);
|
|
2697
|
+
const opusCost = totalTokensIn * 15 / 1e6 + totalTokensOut * 75 / 1e6;
|
|
2698
|
+
const haikuCost = haikuTokensIn * 0.25 / 1e6 + haikuTokensOut * 1.25 / 1e6;
|
|
2699
|
+
const nonHaikuCost = (totalTokensIn - haikuTokensIn) * 3 / 1e6 + (totalTokensOut - haikuTokensOut) * 15 / 1e6;
|
|
2700
|
+
const actualCost = haikuCost + nonHaikuCost;
|
|
2701
|
+
const savings = opusCost - actualCost;
|
|
2702
|
+
if (savings > 0) {
|
|
2703
|
+
console.log(" Estimated Savings:");
|
|
2704
|
+
console.log(` If all Opus: $${opusCost.toFixed(2)}`);
|
|
2705
|
+
console.log(` With routing: $${actualCost.toFixed(2)}`);
|
|
2706
|
+
console.log(` Saved: $${savings.toFixed(2)} (${(savings / opusCost * 100).toFixed(0)}%)`);
|
|
2707
|
+
console.log("");
|
|
2708
|
+
}
|
|
2709
|
+
db.close();
|
|
2710
|
+
} catch (err) {
|
|
2711
|
+
console.error("Error reading stats:", err);
|
|
2712
|
+
console.log("");
|
|
2713
|
+
console.log(" No data found. The proxy stores data at:");
|
|
2714
|
+
console.log(` ${dbPath}`);
|
|
2715
|
+
console.log("");
|
|
2716
|
+
}
|
|
2717
|
+
}
|
|
2630
2718
|
async function main() {
|
|
2631
2719
|
const args = process.argv.slice(2);
|
|
2632
2720
|
if (args.includes("-h") || args.includes("--help")) {
|
|
2633
2721
|
printHelp();
|
|
2634
2722
|
process.exit(0);
|
|
2635
2723
|
}
|
|
2724
|
+
if (args[0] === "stats") {
|
|
2725
|
+
let days = 7;
|
|
2726
|
+
const daysIdx = args.indexOf("--days");
|
|
2727
|
+
if (daysIdx !== -1 && args[daysIdx + 1]) {
|
|
2728
|
+
days = parseInt(args[daysIdx + 1], 10) || 7;
|
|
2729
|
+
}
|
|
2730
|
+
showStats(days);
|
|
2731
|
+
process.exit(0);
|
|
2732
|
+
}
|
|
2636
2733
|
let port = 3001;
|
|
2637
2734
|
let host = "127.0.0.1";
|
|
2638
2735
|
let verbose = false;
|