@relayplane/proxy 0.1.7 → 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 +126 -15
- package/dist/cli.js.map +1 -1
- package/dist/cli.mjs +126 -15
- 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 +181 -60
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +175 -59
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1570,12 +1570,107 @@ ${input.prompt}` : input.prompt;
|
|
|
1570
1570
|
}
|
|
1571
1571
|
};
|
|
1572
1572
|
|
|
1573
|
+
// src/config.ts
|
|
1574
|
+
import * as fs2 from "fs";
|
|
1575
|
+
import * as path2 from "path";
|
|
1576
|
+
import * as os2 from "os";
|
|
1577
|
+
import { z } from "zod";
|
|
1578
|
+
var StrategySchema = z.object({
|
|
1579
|
+
model: z.string(),
|
|
1580
|
+
minConfidence: z.number().min(0).max(1).optional(),
|
|
1581
|
+
fallback: z.string().optional()
|
|
1582
|
+
});
|
|
1583
|
+
var ConfigSchema = z.object({
|
|
1584
|
+
strategies: z.record(z.string(), StrategySchema).optional(),
|
|
1585
|
+
defaults: z.object({
|
|
1586
|
+
qualityModel: z.string().optional(),
|
|
1587
|
+
costModel: z.string().optional()
|
|
1588
|
+
}).optional()
|
|
1589
|
+
});
|
|
1590
|
+
var DEFAULT_CONFIG = {
|
|
1591
|
+
strategies: {
|
|
1592
|
+
code_review: { model: "anthropic:claude-sonnet-4-20250514" },
|
|
1593
|
+
code_generation: { model: "anthropic:claude-3-5-haiku-latest" },
|
|
1594
|
+
analysis: { model: "anthropic:claude-sonnet-4-20250514" },
|
|
1595
|
+
summarization: { model: "anthropic:claude-3-5-haiku-latest" },
|
|
1596
|
+
creative_writing: { model: "anthropic:claude-sonnet-4-20250514" },
|
|
1597
|
+
data_extraction: { model: "anthropic:claude-3-5-haiku-latest" },
|
|
1598
|
+
translation: { model: "anthropic:claude-3-5-haiku-latest" },
|
|
1599
|
+
question_answering: { model: "anthropic:claude-3-5-haiku-latest" },
|
|
1600
|
+
general: { model: "anthropic:claude-3-5-haiku-latest" }
|
|
1601
|
+
},
|
|
1602
|
+
defaults: {
|
|
1603
|
+
qualityModel: "claude-sonnet-4-20250514",
|
|
1604
|
+
costModel: "claude-3-5-haiku-latest"
|
|
1605
|
+
}
|
|
1606
|
+
};
|
|
1607
|
+
function getConfigPath() {
|
|
1608
|
+
return path2.join(os2.homedir(), ".relayplane", "config.json");
|
|
1609
|
+
}
|
|
1610
|
+
function writeDefaultConfig() {
|
|
1611
|
+
const configPath = getConfigPath();
|
|
1612
|
+
const dir = path2.dirname(configPath);
|
|
1613
|
+
if (!fs2.existsSync(dir)) {
|
|
1614
|
+
fs2.mkdirSync(dir, { recursive: true });
|
|
1615
|
+
}
|
|
1616
|
+
if (!fs2.existsSync(configPath)) {
|
|
1617
|
+
fs2.writeFileSync(
|
|
1618
|
+
configPath,
|
|
1619
|
+
JSON.stringify(DEFAULT_CONFIG, null, 2) + "\n",
|
|
1620
|
+
"utf-8"
|
|
1621
|
+
);
|
|
1622
|
+
console.log(`[relayplane] Created default config at ${configPath}`);
|
|
1623
|
+
}
|
|
1624
|
+
}
|
|
1625
|
+
function loadConfig() {
|
|
1626
|
+
const configPath = getConfigPath();
|
|
1627
|
+
writeDefaultConfig();
|
|
1628
|
+
try {
|
|
1629
|
+
const raw = fs2.readFileSync(configPath, "utf-8");
|
|
1630
|
+
const parsed = JSON.parse(raw);
|
|
1631
|
+
const validated = ConfigSchema.parse(parsed);
|
|
1632
|
+
return validated;
|
|
1633
|
+
} catch (err) {
|
|
1634
|
+
if (err instanceof z.ZodError) {
|
|
1635
|
+
console.error(`[relayplane] Invalid config: ${err.message}`);
|
|
1636
|
+
} else if (err instanceof SyntaxError) {
|
|
1637
|
+
console.error(`[relayplane] Config JSON parse error: ${err.message}`);
|
|
1638
|
+
} else {
|
|
1639
|
+
console.error(`[relayplane] Failed to load config: ${err}`);
|
|
1640
|
+
}
|
|
1641
|
+
console.log("[relayplane] Using default config");
|
|
1642
|
+
return DEFAULT_CONFIG;
|
|
1643
|
+
}
|
|
1644
|
+
}
|
|
1645
|
+
function getStrategy(config, taskType) {
|
|
1646
|
+
return config.strategies?.[taskType] ?? null;
|
|
1647
|
+
}
|
|
1648
|
+
function watchConfig(onChange) {
|
|
1649
|
+
const configPath = getConfigPath();
|
|
1650
|
+
const dir = path2.dirname(configPath);
|
|
1651
|
+
if (!fs2.existsSync(dir)) {
|
|
1652
|
+
fs2.mkdirSync(dir, { recursive: true });
|
|
1653
|
+
}
|
|
1654
|
+
let debounceTimer = null;
|
|
1655
|
+
fs2.watch(dir, (eventType, filename) => {
|
|
1656
|
+
if (filename === "config.json") {
|
|
1657
|
+
if (debounceTimer) clearTimeout(debounceTimer);
|
|
1658
|
+
debounceTimer = setTimeout(() => {
|
|
1659
|
+
console.log("[relayplane] Config file changed, reloading...");
|
|
1660
|
+
const newConfig = loadConfig();
|
|
1661
|
+
onChange(newConfig);
|
|
1662
|
+
}, 100);
|
|
1663
|
+
}
|
|
1664
|
+
});
|
|
1665
|
+
}
|
|
1666
|
+
|
|
1573
1667
|
// src/proxy.ts
|
|
1574
|
-
var VERSION = "0.1.
|
|
1668
|
+
var VERSION = "0.1.8";
|
|
1575
1669
|
var recentRuns = [];
|
|
1576
1670
|
var MAX_RECENT_RUNS = 100;
|
|
1577
1671
|
var modelCounts = {};
|
|
1578
1672
|
var serverStartTime = 0;
|
|
1673
|
+
var currentConfig = loadConfig();
|
|
1579
1674
|
var DEFAULT_ENDPOINTS = {
|
|
1580
1675
|
anthropic: {
|
|
1581
1676
|
baseUrl: "https://api.anthropic.com/v1",
|
|
@@ -2426,33 +2521,44 @@ async function startProxy(config = {}) {
|
|
|
2426
2521
|
const confidence = getInferenceConfidence(promptText, taskType);
|
|
2427
2522
|
log(`Inferred task: ${taskType} (confidence: ${confidence.toFixed(2)})`);
|
|
2428
2523
|
if (routingMode !== "passthrough") {
|
|
2429
|
-
const
|
|
2430
|
-
if (
|
|
2431
|
-
const parsed = parsePreferredModel(
|
|
2524
|
+
const configStrategy = getStrategy(currentConfig, taskType);
|
|
2525
|
+
if (configStrategy) {
|
|
2526
|
+
const parsed = parsePreferredModel(configStrategy.model);
|
|
2432
2527
|
if (parsed) {
|
|
2433
2528
|
targetProvider = parsed.provider;
|
|
2434
2529
|
targetModel = parsed.model;
|
|
2435
|
-
log(`Using
|
|
2530
|
+
log(`Using config strategy: ${configStrategy.model}`);
|
|
2531
|
+
}
|
|
2532
|
+
}
|
|
2533
|
+
if (!configStrategy) {
|
|
2534
|
+
const rule = relay.routing.get(taskType);
|
|
2535
|
+
if (rule && rule.preferredModel) {
|
|
2536
|
+
const parsed = parsePreferredModel(rule.preferredModel);
|
|
2537
|
+
if (parsed) {
|
|
2538
|
+
targetProvider = parsed.provider;
|
|
2539
|
+
targetModel = parsed.model;
|
|
2540
|
+
log(`Using learned rule: ${rule.preferredModel}`);
|
|
2541
|
+
} else {
|
|
2542
|
+
const defaultRoute = DEFAULT_ROUTING[taskType];
|
|
2543
|
+
targetProvider = defaultRoute.provider;
|
|
2544
|
+
targetModel = defaultRoute.model;
|
|
2545
|
+
}
|
|
2436
2546
|
} else {
|
|
2437
2547
|
const defaultRoute = DEFAULT_ROUTING[taskType];
|
|
2438
2548
|
targetProvider = defaultRoute.provider;
|
|
2439
2549
|
targetModel = defaultRoute.model;
|
|
2440
2550
|
}
|
|
2441
|
-
} else {
|
|
2442
|
-
const defaultRoute = DEFAULT_ROUTING[taskType];
|
|
2443
|
-
targetProvider = defaultRoute.provider;
|
|
2444
|
-
targetModel = defaultRoute.model;
|
|
2445
2551
|
}
|
|
2446
2552
|
if (routingMode === "cost") {
|
|
2447
|
-
const
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
}
|
|
2553
|
+
const costModel = currentConfig.defaults?.costModel || "claude-3-5-haiku-latest";
|
|
2554
|
+
targetModel = costModel;
|
|
2555
|
+
targetProvider = "anthropic";
|
|
2556
|
+
log(`Cost mode: using ${costModel}`);
|
|
2452
2557
|
} else if (routingMode === "quality") {
|
|
2453
|
-
const qualityModel = process.env["RELAYPLANE_QUALITY_MODEL"] || "claude-sonnet-4-20250514";
|
|
2558
|
+
const qualityModel = currentConfig.defaults?.qualityModel || process.env["RELAYPLANE_QUALITY_MODEL"] || "claude-sonnet-4-20250514";
|
|
2454
2559
|
targetModel = qualityModel;
|
|
2455
2560
|
targetProvider = "anthropic";
|
|
2561
|
+
log(`Quality mode: using ${qualityModel}`);
|
|
2456
2562
|
}
|
|
2457
2563
|
}
|
|
2458
2564
|
log(`Routing to: ${targetProvider}/${targetModel}`);
|
|
@@ -2499,6 +2605,10 @@ async function startProxy(config = {}) {
|
|
|
2499
2605
|
);
|
|
2500
2606
|
}
|
|
2501
2607
|
});
|
|
2608
|
+
watchConfig((newConfig) => {
|
|
2609
|
+
currentConfig = newConfig;
|
|
2610
|
+
console.log("[relayplane] Config reloaded");
|
|
2611
|
+
});
|
|
2502
2612
|
return new Promise((resolve, reject) => {
|
|
2503
2613
|
server.on("error", reject);
|
|
2504
2614
|
server.listen(port, host, () => {
|
|
@@ -2507,6 +2617,7 @@ async function startProxy(config = {}) {
|
|
|
2507
2617
|
console.log(` Models: relayplane:auto, relayplane:cost, relayplane:quality`);
|
|
2508
2618
|
console.log(` Endpoint: POST /v1/chat/completions`);
|
|
2509
2619
|
console.log(` Stats: GET /stats, /runs, /health`);
|
|
2620
|
+
console.log(` Config: ~/.relayplane/config.json (hot-reload enabled)`);
|
|
2510
2621
|
console.log(` Streaming: \u2705 Enabled`);
|
|
2511
2622
|
resolve(server);
|
|
2512
2623
|
});
|
|
@@ -2697,7 +2808,7 @@ async function handleNonStreamingRequest(res, request, targetProvider, targetMod
|
|
|
2697
2808
|
}
|
|
2698
2809
|
|
|
2699
2810
|
// src/types.ts
|
|
2700
|
-
import { z } from "zod";
|
|
2811
|
+
import { z as z2 } from "zod";
|
|
2701
2812
|
var TaskTypes = [
|
|
2702
2813
|
"code_generation",
|
|
2703
2814
|
"code_review",
|
|
@@ -2709,63 +2820,64 @@ var TaskTypes = [
|
|
|
2709
2820
|
"question_answering",
|
|
2710
2821
|
"general"
|
|
2711
2822
|
];
|
|
2712
|
-
var TaskTypeSchema =
|
|
2823
|
+
var TaskTypeSchema = z2.enum(TaskTypes);
|
|
2713
2824
|
var Providers = ["openai", "anthropic", "google", "xai", "moonshot", "local"];
|
|
2714
|
-
var ProviderSchema =
|
|
2715
|
-
var RelayPlaneConfigSchema =
|
|
2716
|
-
dbPath:
|
|
2717
|
-
providers:
|
|
2718
|
-
apiKey:
|
|
2719
|
-
baseUrl:
|
|
2825
|
+
var ProviderSchema = z2.enum(Providers);
|
|
2826
|
+
var RelayPlaneConfigSchema = z2.object({
|
|
2827
|
+
dbPath: z2.string().optional(),
|
|
2828
|
+
providers: z2.record(ProviderSchema, z2.object({
|
|
2829
|
+
apiKey: z2.string().optional(),
|
|
2830
|
+
baseUrl: z2.string().optional()
|
|
2720
2831
|
})).optional(),
|
|
2721
2832
|
defaultProvider: ProviderSchema.optional(),
|
|
2722
|
-
defaultModel:
|
|
2833
|
+
defaultModel: z2.string().optional()
|
|
2723
2834
|
});
|
|
2724
|
-
var RunInputSchema =
|
|
2725
|
-
prompt:
|
|
2726
|
-
systemPrompt:
|
|
2835
|
+
var RunInputSchema = z2.object({
|
|
2836
|
+
prompt: z2.string().min(1),
|
|
2837
|
+
systemPrompt: z2.string().optional(),
|
|
2727
2838
|
taskType: TaskTypeSchema.optional(),
|
|
2728
|
-
model:
|
|
2729
|
-
metadata:
|
|
2839
|
+
model: z2.string().optional(),
|
|
2840
|
+
metadata: z2.record(z2.unknown()).optional()
|
|
2730
2841
|
});
|
|
2731
2842
|
var RuleSources = ["default", "user", "learned"];
|
|
2732
|
-
var RoutingRuleSchema =
|
|
2733
|
-
id:
|
|
2843
|
+
var RoutingRuleSchema = z2.object({
|
|
2844
|
+
id: z2.string(),
|
|
2734
2845
|
taskType: TaskTypeSchema,
|
|
2735
|
-
preferredModel:
|
|
2736
|
-
source:
|
|
2737
|
-
confidence:
|
|
2738
|
-
sampleCount:
|
|
2739
|
-
createdAt:
|
|
2740
|
-
updatedAt:
|
|
2846
|
+
preferredModel: z2.string(),
|
|
2847
|
+
source: z2.enum(RuleSources),
|
|
2848
|
+
confidence: z2.number().min(0).max(1).optional(),
|
|
2849
|
+
sampleCount: z2.number().int().positive().optional(),
|
|
2850
|
+
createdAt: z2.string(),
|
|
2851
|
+
updatedAt: z2.string()
|
|
2741
2852
|
});
|
|
2742
2853
|
var OutcomeQualities = ["excellent", "good", "acceptable", "poor", "failed"];
|
|
2743
|
-
var OutcomeInputSchema =
|
|
2744
|
-
runId:
|
|
2745
|
-
success:
|
|
2746
|
-
quality:
|
|
2747
|
-
latencySatisfactory:
|
|
2748
|
-
costSatisfactory:
|
|
2749
|
-
feedback:
|
|
2854
|
+
var OutcomeInputSchema = z2.object({
|
|
2855
|
+
runId: z2.string().min(1),
|
|
2856
|
+
success: z2.boolean(),
|
|
2857
|
+
quality: z2.enum(OutcomeQualities).optional(),
|
|
2858
|
+
latencySatisfactory: z2.boolean().optional(),
|
|
2859
|
+
costSatisfactory: z2.boolean().optional(),
|
|
2860
|
+
feedback: z2.string().optional()
|
|
2750
2861
|
});
|
|
2751
|
-
var SuggestionSchema =
|
|
2752
|
-
id:
|
|
2862
|
+
var SuggestionSchema = z2.object({
|
|
2863
|
+
id: z2.string(),
|
|
2753
2864
|
taskType: TaskTypeSchema,
|
|
2754
|
-
currentModel:
|
|
2755
|
-
suggestedModel:
|
|
2756
|
-
reason:
|
|
2757
|
-
confidence:
|
|
2758
|
-
expectedImprovement:
|
|
2759
|
-
successRate:
|
|
2760
|
-
latency:
|
|
2761
|
-
cost:
|
|
2865
|
+
currentModel: z2.string(),
|
|
2866
|
+
suggestedModel: z2.string(),
|
|
2867
|
+
reason: z2.string(),
|
|
2868
|
+
confidence: z2.number().min(0).max(1),
|
|
2869
|
+
expectedImprovement: z2.object({
|
|
2870
|
+
successRate: z2.number().optional(),
|
|
2871
|
+
latency: z2.number().optional(),
|
|
2872
|
+
cost: z2.number().optional()
|
|
2762
2873
|
}),
|
|
2763
|
-
sampleCount:
|
|
2764
|
-
createdAt:
|
|
2765
|
-
accepted:
|
|
2766
|
-
acceptedAt:
|
|
2874
|
+
sampleCount: z2.number().int().positive(),
|
|
2875
|
+
createdAt: z2.string(),
|
|
2876
|
+
accepted: z2.boolean().optional(),
|
|
2877
|
+
acceptedAt: z2.string().optional()
|
|
2767
2878
|
});
|
|
2768
2879
|
export {
|
|
2880
|
+
DEFAULT_CONFIG,
|
|
2769
2881
|
DEFAULT_ENDPOINTS,
|
|
2770
2882
|
MODEL_MAPPING,
|
|
2771
2883
|
MODEL_PRICING,
|
|
@@ -2780,9 +2892,13 @@ export {
|
|
|
2780
2892
|
TaskTypes,
|
|
2781
2893
|
calculateCost,
|
|
2782
2894
|
calculateSavings,
|
|
2895
|
+
getConfigPath,
|
|
2783
2896
|
getInferenceConfidence,
|
|
2784
2897
|
getModelPricing,
|
|
2898
|
+
getStrategy,
|
|
2785
2899
|
inferTaskType,
|
|
2786
|
-
|
|
2900
|
+
loadConfig,
|
|
2901
|
+
startProxy,
|
|
2902
|
+
watchConfig
|
|
2787
2903
|
};
|
|
2788
2904
|
//# sourceMappingURL=index.mjs.map
|