easyrouter-config 1.0.6 → 1.0.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/dist/index.js +113 -20
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -20,7 +20,7 @@ import { parseArgs } from "util";
|
|
|
20
20
|
import pc from "picocolors";
|
|
21
21
|
|
|
22
22
|
// src/config/constants.ts
|
|
23
|
-
var VERSION = true ? "1.0.
|
|
23
|
+
var VERSION = true ? "1.0.8" : "0.0.0-dev";
|
|
24
24
|
var PRIMARY_HOST = "https://easyrouter.io";
|
|
25
25
|
var FALLBACK_HOSTS = ["https://ezr.sh"];
|
|
26
26
|
var HOST_PROBE_TIMEOUT_MS = 5e3;
|
|
@@ -362,6 +362,72 @@ var claudeClient = {
|
|
|
362
362
|
// src/config/models.ts
|
|
363
363
|
import { log as log2, select, isCancel } from "@clack/prompts";
|
|
364
364
|
import pc4 from "picocolors";
|
|
365
|
+
async function fetchOpenRouterContextMap(verbose) {
|
|
366
|
+
const map = /* @__PURE__ */ new Map();
|
|
367
|
+
try {
|
|
368
|
+
const ctrl = new AbortController();
|
|
369
|
+
const timer = setTimeout(() => ctrl.abort(), FETCH_MODELS_TIMEOUT_MS);
|
|
370
|
+
const res = await fetch("https://openrouter.ai/api/v1/models", {
|
|
371
|
+
signal: ctrl.signal,
|
|
372
|
+
headers: { "User-Agent": "easyrouter-config/1.0" }
|
|
373
|
+
});
|
|
374
|
+
clearTimeout(timer);
|
|
375
|
+
if (!res.ok) return map;
|
|
376
|
+
const json = await res.json();
|
|
377
|
+
const models = Array.isArray(json?.data) ? json.data : [];
|
|
378
|
+
for (const m of models) {
|
|
379
|
+
const short = m.id.split("/").pop() ?? m.id;
|
|
380
|
+
const ctx = m.context_length ?? 0;
|
|
381
|
+
const maxOut = m.max_completion_tokens ?? Math.min(32768, Math.max(4096, Math.floor(ctx / 4)));
|
|
382
|
+
const entry = { contextLength: ctx, maxTokens: maxOut };
|
|
383
|
+
map.set(short.toLowerCase(), entry);
|
|
384
|
+
map.set(normalizeModelId(short), entry);
|
|
385
|
+
}
|
|
386
|
+
if (verbose) log2.info(`\u2713 OpenRouter: \u62C9\u53D6\u5230 ${models.length} \u4E2A\u6A21\u578B\u4E0A\u4E0B\u6587\u4FE1\u606F`);
|
|
387
|
+
} catch (err) {
|
|
388
|
+
if (verbose) log2.warn(`OpenRouter \u62C9\u53D6\u5931\u8D25\uFF1A${err?.message}\uFF08\u5C06\u7528\u515C\u5E95 contextWindow\uFF09`);
|
|
389
|
+
}
|
|
390
|
+
return map;
|
|
391
|
+
}
|
|
392
|
+
function normalizeModelId(s) {
|
|
393
|
+
s = s.replace(/-\d{6}$/, "");
|
|
394
|
+
s = s.replace(/(?<=[a-zA-Z])-(\d+)-(\d+)(?=-|$)/g, "-$1.$2");
|
|
395
|
+
return s.toLowerCase();
|
|
396
|
+
}
|
|
397
|
+
var _orContextMapPromise = null;
|
|
398
|
+
function getOpenRouterContextMap(verbose) {
|
|
399
|
+
if (!_orContextMapPromise) {
|
|
400
|
+
_orContextMapPromise = fetchOpenRouterContextMap(verbose);
|
|
401
|
+
}
|
|
402
|
+
return _orContextMapPromise;
|
|
403
|
+
}
|
|
404
|
+
function toOpenClawCost(m, orContextMap) {
|
|
405
|
+
const ratio = m.model_ratio ?? 0;
|
|
406
|
+
const compRatio = m.completion_ratio ?? 1;
|
|
407
|
+
const cacheRatio = m.cache_ratio ?? 0;
|
|
408
|
+
const input = round6(ratio * 2e-3);
|
|
409
|
+
const output = round6(ratio * compRatio * 2e-3);
|
|
410
|
+
const cacheRead = round6(ratio * cacheRatio * 2e-3);
|
|
411
|
+
const cacheWrite = round6(input * 1.25);
|
|
412
|
+
let contextWindow = 131072;
|
|
413
|
+
let maxTokens = 32768;
|
|
414
|
+
if (orContextMap) {
|
|
415
|
+
const name = m.model_name;
|
|
416
|
+
const hit = orContextMap.get(name.toLowerCase()) ?? orContextMap.get(normalizeModelId(name));
|
|
417
|
+
if (hit && hit.contextLength > 0) {
|
|
418
|
+
contextWindow = hit.contextLength;
|
|
419
|
+
maxTokens = hit.maxTokens;
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
return {
|
|
423
|
+
cost: { input, output, cacheRead, cacheWrite },
|
|
424
|
+
contextWindow,
|
|
425
|
+
maxTokens
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
function round6(n) {
|
|
429
|
+
return Math.round(n * 1e6) / 1e6;
|
|
430
|
+
}
|
|
365
431
|
async function fetchEasyRouterModels(host, verbose) {
|
|
366
432
|
const url = host.replace(/\/+$/, "") + "/api/pricing";
|
|
367
433
|
try {
|
|
@@ -557,7 +623,8 @@ var openClawClient = {
|
|
|
557
623
|
await runOpenClawSubprocess(args, ctx);
|
|
558
624
|
let registered = 0;
|
|
559
625
|
if (llms.length > 1) {
|
|
560
|
-
|
|
626
|
+
const orContextMap = await getOpenRouterContextMap(ctx.verbose);
|
|
627
|
+
registered = await patchOpenClawJson(llms, defaultModel, ctx, orContextMap);
|
|
561
628
|
}
|
|
562
629
|
return {
|
|
563
630
|
configPaths: ["~/.openclaw/openclaw.json"],
|
|
@@ -565,7 +632,7 @@ var openClawClient = {
|
|
|
565
632
|
};
|
|
566
633
|
}
|
|
567
634
|
};
|
|
568
|
-
async function patchOpenClawJson(llms, defaultModel, ctx) {
|
|
635
|
+
async function patchOpenClawJson(llms, defaultModel, ctx, orContextMap) {
|
|
569
636
|
const configPath = join(homedir(), ".openclaw", "openclaw.json");
|
|
570
637
|
let json;
|
|
571
638
|
try {
|
|
@@ -595,27 +662,50 @@ async function patchOpenClawJson(llms, defaultModel, ctx) {
|
|
|
595
662
|
}
|
|
596
663
|
return 1;
|
|
597
664
|
}
|
|
598
|
-
const
|
|
599
|
-
|
|
600
|
-
|
|
665
|
+
const existingById = /* @__PURE__ */ new Map();
|
|
666
|
+
provider.models.forEach((m, i) => {
|
|
667
|
+
existingById.set(m.id, i);
|
|
668
|
+
});
|
|
601
669
|
const before = provider.models.length;
|
|
670
|
+
let updated = 0;
|
|
671
|
+
const agentsModels = json?.agents?.defaults?.models ?? null;
|
|
602
672
|
for (const m of llms) {
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
673
|
+
const { cost, contextWindow, maxTokens } = toOpenClawCost(m, orContextMap);
|
|
674
|
+
const idx = existingById.get(m.model_name);
|
|
675
|
+
if (idx !== void 0) {
|
|
676
|
+
const existing = provider.models[idx];
|
|
677
|
+
const costChanged = existing.cost?.input !== cost.input || existing.cost?.output !== cost.output;
|
|
678
|
+
const ctxChanged = existing.contextWindow !== contextWindow;
|
|
679
|
+
if (costChanged || ctxChanged) {
|
|
680
|
+
existing.cost = cost;
|
|
681
|
+
existing.contextWindow = contextWindow;
|
|
682
|
+
existing.maxTokens = maxTokens;
|
|
683
|
+
updated++;
|
|
684
|
+
}
|
|
685
|
+
} else {
|
|
686
|
+
provider.models.push({
|
|
687
|
+
id: m.model_name,
|
|
688
|
+
name: m.model_name + " (EasyRouter)",
|
|
689
|
+
contextWindow,
|
|
690
|
+
maxTokens,
|
|
691
|
+
input: ["text"],
|
|
692
|
+
cost,
|
|
693
|
+
reasoning: false
|
|
694
|
+
});
|
|
695
|
+
}
|
|
696
|
+
if (agentsModels !== null) {
|
|
697
|
+
const agentKey = `${OPENCLAW_PROVIDER_ID}/${m.model_name}`;
|
|
698
|
+
const existing = agentsModels[agentKey];
|
|
699
|
+
if (!existing || Object.keys(existing).length === 0) {
|
|
700
|
+
agentsModels[agentKey] = { alias: agentKey };
|
|
701
|
+
}
|
|
702
|
+
}
|
|
614
703
|
}
|
|
615
|
-
|
|
704
|
+
const added = provider.models.length - before;
|
|
705
|
+
if (added === 0 && updated === 0) {
|
|
616
706
|
if (ctx.verbose) {
|
|
617
707
|
console.warn(
|
|
618
|
-
`[openclaw] \
|
|
708
|
+
`[openclaw] \u6240\u6709 ${llms.length} \u4E2A\u6A21\u578B\u5747\u5DF2\u662F\u6700\u65B0\uFF0C\u8DF3\u8FC7\u5199\u5165`
|
|
619
709
|
);
|
|
620
710
|
}
|
|
621
711
|
return provider.models.length;
|
|
@@ -623,8 +713,11 @@ async function patchOpenClawJson(llms, defaultModel, ctx) {
|
|
|
623
713
|
await fs.copyFile(configPath, configPath + ".bak." + Date.now());
|
|
624
714
|
await atomicWriteJson(configPath, json);
|
|
625
715
|
if (ctx.verbose) {
|
|
716
|
+
const parts = [];
|
|
717
|
+
if (added > 0) parts.push(`\u65B0\u589E ${added} \u4E2A`);
|
|
718
|
+
if (updated > 0) parts.push(`\u4FEE\u6B63 cost/ctx ${updated} \u4E2A`);
|
|
626
719
|
console.warn(
|
|
627
|
-
`[openclaw] \u2713
|
|
720
|
+
`[openclaw] \u2713 ${parts.join("\uFF0C")}\uFF0C\u5171 ${provider.models.length} \u4E2A\uFF08\u9ED8\u8BA4\uFF1A${defaultModel}\uFF09`
|
|
628
721
|
);
|
|
629
722
|
}
|
|
630
723
|
return provider.models.length;
|