@kkmila/cpc 1.0.1 → 1.0.4
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 +2 -0
- package/package.json +6 -2
- package/registry.yaml +5 -0
- package/src/commands/provider.mjs +205 -17
- package/src/lib/baseline-models.mjs +286 -0
package/README.md
CHANGED
|
@@ -36,6 +36,7 @@ cpc install
|
|
|
36
36
|
| `cpc export [name]` | 导出提供商配置 |
|
|
37
37
|
| `cpc import <file>` | 导入提供商配置 |
|
|
38
38
|
| `cpc list-installed` | 列出已安装的提供商 |
|
|
39
|
+
| `cpc doctor` | 检查配置和运行状态 |
|
|
39
40
|
|
|
40
41
|
## 使用示例
|
|
41
42
|
|
|
@@ -73,6 +74,7 @@ cpc test openai
|
|
|
73
74
|
- omp
|
|
74
75
|
- mimo-code
|
|
75
76
|
- cursor
|
|
77
|
+
- opencode
|
|
76
78
|
|
|
77
79
|
## 配置文件
|
|
78
80
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kkmila/cpc",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"description": "Custom Provider CLI for managing model providers",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -10,6 +10,10 @@
|
|
|
10
10
|
"exports": {
|
|
11
11
|
".": "./src/commands/provider.mjs"
|
|
12
12
|
},
|
|
13
|
+
"publishConfig": {
|
|
14
|
+
"access": "public",
|
|
15
|
+
"registry": "https://registry.npmjs.org/"
|
|
16
|
+
},
|
|
13
17
|
"files": [
|
|
14
18
|
"bin",
|
|
15
19
|
"src",
|
|
@@ -18,7 +22,7 @@
|
|
|
18
22
|
],
|
|
19
23
|
"scripts": {
|
|
20
24
|
"dev": "node bin/cpc.mjs",
|
|
21
|
-
"test": "node --test src/**/*.test.mjs"
|
|
25
|
+
"test": "node --test 'src/**/*.test.mjs'"
|
|
22
26
|
},
|
|
23
27
|
"keywords": [
|
|
24
28
|
"cli",
|
package/registry.yaml
CHANGED
|
@@ -5,6 +5,7 @@ import { resolve, join } from "node:path";
|
|
|
5
5
|
import { homedir } from "node:os";
|
|
6
6
|
import yaml from "js-yaml";
|
|
7
7
|
import { loadRegistry, getAgents, resolveAgentPaths } from "../lib/agents.mjs";
|
|
8
|
+
import { enrichModelWithBaseline } from "../lib/baseline-models.mjs";
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* Get the config directory path (~/.config/cpc)
|
|
@@ -387,6 +388,27 @@ function getAgentProviderConfig(agentId, provider) {
|
|
|
387
388
|
},
|
|
388
389
|
};
|
|
389
390
|
|
|
391
|
+
case "opencode":
|
|
392
|
+
return {
|
|
393
|
+
opencodeJson: {
|
|
394
|
+
provider: {
|
|
395
|
+
[provider.name]: {
|
|
396
|
+
models: provider.models.reduce((acc, m) => {
|
|
397
|
+
acc[m.id] = {
|
|
398
|
+
name: m.name || m.id,
|
|
399
|
+
};
|
|
400
|
+
return acc;
|
|
401
|
+
}, {}),
|
|
402
|
+
npm: "@ai-sdk/openai-compatible",
|
|
403
|
+
options: {
|
|
404
|
+
apiKey: provider.apiKey,
|
|
405
|
+
baseURL: provider.baseUrl,
|
|
406
|
+
},
|
|
407
|
+
},
|
|
408
|
+
},
|
|
409
|
+
},
|
|
410
|
+
};
|
|
411
|
+
|
|
390
412
|
default:
|
|
391
413
|
return null;
|
|
392
414
|
}
|
|
@@ -511,6 +533,22 @@ function installProviderToAgent(agentId, provider, agentHome) {
|
|
|
511
533
|
return true;
|
|
512
534
|
}
|
|
513
535
|
|
|
536
|
+
case "opencode": {
|
|
537
|
+
const configPath = join(agentHome, "opencode.json");
|
|
538
|
+
let configData = {};
|
|
539
|
+
if (existsSync(configPath)) {
|
|
540
|
+
configData = JSON.parse(readFileSync(configPath, "utf8"));
|
|
541
|
+
}
|
|
542
|
+
if (!configData.provider) configData.provider = {};
|
|
543
|
+
configData.provider = {
|
|
544
|
+
...configData.provider,
|
|
545
|
+
...config.opencodeJson.provider,
|
|
546
|
+
};
|
|
547
|
+
writeFileSync(configPath, JSON.stringify(configData, null, 2));
|
|
548
|
+
console.log(chalk.green(` ✓ 更新 OpenCode opencode.json`));
|
|
549
|
+
return true;
|
|
550
|
+
}
|
|
551
|
+
|
|
514
552
|
default:
|
|
515
553
|
console.log(chalk.yellow(` ⚠ 未知 agent: ${agentId}`));
|
|
516
554
|
return false;
|
|
@@ -521,6 +559,21 @@ function installProviderToAgent(agentId, provider, agentHome) {
|
|
|
521
559
|
}
|
|
522
560
|
}
|
|
523
561
|
|
|
562
|
+
/**
|
|
563
|
+
* Check if an agent is installed by looking at its home directory
|
|
564
|
+
*/
|
|
565
|
+
function isAgentInstalled(agentId, agents) {
|
|
566
|
+
const agent = agents[agentId];
|
|
567
|
+
if (!agent || !agent.home) return false;
|
|
568
|
+
|
|
569
|
+
// Expand ~ to home directory
|
|
570
|
+
const home = agent.home.replace(
|
|
571
|
+
/^~/,
|
|
572
|
+
process.env.HOME || process.env.USERPROFILE,
|
|
573
|
+
);
|
|
574
|
+
return existsSync(home);
|
|
575
|
+
}
|
|
576
|
+
|
|
524
577
|
export function registerProvider(program, repoRoot) {
|
|
525
578
|
// ── cpc add ──────────────────────────────────────────────────
|
|
526
579
|
program
|
|
@@ -647,14 +700,23 @@ export function registerProvider(program, repoRoot) {
|
|
|
647
700
|
for (const modelId of selected) {
|
|
648
701
|
const model = result.models.find((m) => m.id === modelId);
|
|
649
702
|
const defaults = getModelDefaults(modelId);
|
|
650
|
-
|
|
703
|
+
// 先用 baseline 字典补全 contextWindow / input / description,
|
|
704
|
+
// 未命中时回退到 API 响应与 MODEL_DEFAULTS
|
|
705
|
+
const configured = enrichModelWithBaseline({
|
|
651
706
|
id: modelId,
|
|
652
707
|
name: model?.name || modelId,
|
|
653
708
|
contextWindow: model?.contextWindow || defaults.contextWindow,
|
|
654
709
|
maxTokens: model?.maxTokens || defaults.maxTokens,
|
|
655
710
|
reasoning: model?.reasoning || defaults.reasoning,
|
|
656
711
|
input: model?.input || ["text"],
|
|
657
|
-
};
|
|
712
|
+
});
|
|
713
|
+
if (configured.description) {
|
|
714
|
+
console.log(
|
|
715
|
+
chalk.dim(
|
|
716
|
+
` ↪ ${modelId}: 命中 baseline, 已注入 contextWindow=${configured.contextWindow}, input=[${configured.input.join(",")}]`,
|
|
717
|
+
),
|
|
718
|
+
);
|
|
719
|
+
}
|
|
658
720
|
models.push(configured);
|
|
659
721
|
}
|
|
660
722
|
} else {
|
|
@@ -976,14 +1038,23 @@ export function registerProvider(program, repoRoot) {
|
|
|
976
1038
|
for (const modelId of selected) {
|
|
977
1039
|
const model = result.models.find((m) => m.id === modelId);
|
|
978
1040
|
const defaults = getModelDefaults(modelId);
|
|
979
|
-
|
|
1041
|
+
// 先用 baseline 字典补全 contextWindow / input / description,
|
|
1042
|
+
// 未命中时回退到 API 响应与 MODEL_DEFAULTS
|
|
1043
|
+
const configured = enrichModelWithBaseline({
|
|
980
1044
|
id: modelId,
|
|
981
1045
|
name: model?.name || modelId,
|
|
982
1046
|
contextWindow: model?.contextWindow || defaults.contextWindow,
|
|
983
1047
|
maxTokens: model?.maxTokens || defaults.maxTokens,
|
|
984
1048
|
reasoning: model?.reasoning || defaults.reasoning,
|
|
985
1049
|
input: model?.input || ["text"],
|
|
986
|
-
};
|
|
1050
|
+
});
|
|
1051
|
+
if (configured.description) {
|
|
1052
|
+
console.log(
|
|
1053
|
+
chalk.dim(
|
|
1054
|
+
` ↪ ${modelId}: 命中 baseline, 已注入 contextWindow=${configured.contextWindow}, input=[${configured.input.join(",")}]`,
|
|
1055
|
+
),
|
|
1056
|
+
);
|
|
1057
|
+
}
|
|
987
1058
|
|
|
988
1059
|
if (opts.merge) {
|
|
989
1060
|
config.models = config.models || [];
|
|
@@ -1134,24 +1205,37 @@ export function registerProvider(program, repoRoot) {
|
|
|
1134
1205
|
}
|
|
1135
1206
|
|
|
1136
1207
|
// Step 3: Select agent(s)
|
|
1137
|
-
let
|
|
1208
|
+
let agentIds;
|
|
1138
1209
|
if (opts.agent) {
|
|
1139
|
-
|
|
1210
|
+
agentIds = opts.agent === "all" ? Object.keys(agents) : [opts.agent];
|
|
1140
1211
|
} else {
|
|
1141
|
-
|
|
1142
|
-
|
|
1212
|
+
// Auto-detect installed agents
|
|
1213
|
+
const selected = await checkbox({
|
|
1214
|
+
message: "Select target agents (installed agents are pre-selected):",
|
|
1143
1215
|
choices: [
|
|
1144
|
-
...Object.entries(agents).map(([id, a]) =>
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1216
|
+
...Object.entries(agents).map(([id, a]) => {
|
|
1217
|
+
const installed = isAgentInstalled(id, agents);
|
|
1218
|
+
return {
|
|
1219
|
+
name: `${id.padEnd(14)} ${a.description || ""}${installed ? " ✓" : ""}`,
|
|
1220
|
+
value: id,
|
|
1221
|
+
checked: installed,
|
|
1222
|
+
};
|
|
1223
|
+
}),
|
|
1150
1224
|
],
|
|
1225
|
+
instructions: {
|
|
1226
|
+
navigator: "↑↓ navigate",
|
|
1227
|
+
select: "space select",
|
|
1228
|
+
all: "a toggle all",
|
|
1229
|
+
},
|
|
1151
1230
|
});
|
|
1152
|
-
}
|
|
1153
1231
|
|
|
1154
|
-
|
|
1232
|
+
if (selected.length === 0) {
|
|
1233
|
+
console.log(chalk.yellow("No agents selected. Cancelled."));
|
|
1234
|
+
return;
|
|
1235
|
+
}
|
|
1236
|
+
|
|
1237
|
+
agentIds = selected;
|
|
1238
|
+
}
|
|
1155
1239
|
|
|
1156
1240
|
// Step 4: Install provider to agents
|
|
1157
1241
|
console.log();
|
|
@@ -1563,6 +1647,29 @@ export function registerProvider(program, repoRoot) {
|
|
|
1563
1647
|
break;
|
|
1564
1648
|
}
|
|
1565
1649
|
|
|
1650
|
+
case "opencode": {
|
|
1651
|
+
const configPath = join(agent.home, "opencode.json");
|
|
1652
|
+
if (existsSync(configPath)) {
|
|
1653
|
+
const configData = JSON.parse(readFileSync(configPath, "utf8"));
|
|
1654
|
+
if (configData.provider && Object.keys(configData.provider).length > 0) {
|
|
1655
|
+
for (const [name, config] of Object.entries(
|
|
1656
|
+
configData.provider,
|
|
1657
|
+
)) {
|
|
1658
|
+
console.log(` ${chalk.blue(name)}`);
|
|
1659
|
+
console.log(` URL: ${chalk.dim(config.options?.baseURL || "not set")}`);
|
|
1660
|
+
console.log(
|
|
1661
|
+
` Models: ${chalk.dim(Object.keys(config.models || {}).length)}`,
|
|
1662
|
+
);
|
|
1663
|
+
}
|
|
1664
|
+
} else {
|
|
1665
|
+
console.log(chalk.dim(" No providers configured"));
|
|
1666
|
+
}
|
|
1667
|
+
} else {
|
|
1668
|
+
console.log(chalk.dim(" Config file not found"));
|
|
1669
|
+
}
|
|
1670
|
+
break;
|
|
1671
|
+
}
|
|
1672
|
+
|
|
1566
1673
|
default:
|
|
1567
1674
|
console.log(chalk.dim(" Unsupported agent"));
|
|
1568
1675
|
}
|
|
@@ -1570,7 +1677,88 @@ export function registerProvider(program, repoRoot) {
|
|
|
1570
1677
|
console.log(chalk.red(` Error reading config: ${err.message}`));
|
|
1571
1678
|
}
|
|
1572
1679
|
|
|
1573
|
-
|
|
1680
|
+
console.log();
|
|
1681
|
+
}
|
|
1682
|
+
});
|
|
1683
|
+
|
|
1684
|
+
// ── cpc doctor ───────────────────────────────────────────────
|
|
1685
|
+
program
|
|
1686
|
+
.command("doctor")
|
|
1687
|
+
.description("Check if cpc is properly configured and working")
|
|
1688
|
+
.action(() => {
|
|
1689
|
+
console.log(chalk.bold("\n🔍 CPC Doctor\n"));
|
|
1690
|
+
|
|
1691
|
+
let issues = 0;
|
|
1692
|
+
|
|
1693
|
+
// Check Node.js version
|
|
1694
|
+
const nodeVersion = process.version;
|
|
1695
|
+
const requiredVersion = "18.0.0";
|
|
1696
|
+
const nodeOk = compareVersions(nodeVersion.slice(1), requiredVersion) >= 0;
|
|
1697
|
+
console.log(`${nodeOk ? "✅" : "❌"} Node.js version: ${nodeVersion} (required: >=${requiredVersion})`);
|
|
1698
|
+
if (!nodeOk) issues++;
|
|
1699
|
+
|
|
1700
|
+
// Check registry.yaml
|
|
1701
|
+
try {
|
|
1702
|
+
const registry = loadRegistry(repoRoot);
|
|
1703
|
+
const agents = getAgents(registry);
|
|
1704
|
+
const agentCount = Object.keys(agents).length;
|
|
1705
|
+
console.log(`✅ registry.yaml: ${agentCount} agents defined`);
|
|
1706
|
+
} catch (err) {
|
|
1707
|
+
console.log(`❌ registry.yaml: ${err.message}`);
|
|
1708
|
+
issues++;
|
|
1709
|
+
}
|
|
1710
|
+
|
|
1711
|
+
// Check config directory
|
|
1712
|
+
const configDir = getConfigDir();
|
|
1713
|
+
if (existsSync(configDir)) {
|
|
1714
|
+
console.log(`✅ Config directory: ${configDir}`);
|
|
1715
|
+
|
|
1716
|
+
// Check providers.json
|
|
1717
|
+
try {
|
|
1718
|
+
const providers = loadProviders();
|
|
1719
|
+
const providerCount = Object.keys(providers.providers).length;
|
|
1720
|
+
console.log(`✅ providers.json: ${providerCount} provider(s) configured`);
|
|
1721
|
+
} catch (err) {
|
|
1722
|
+
console.log(`❌ providers.json: ${err.message}`);
|
|
1723
|
+
issues++;
|
|
1724
|
+
}
|
|
1725
|
+
} else {
|
|
1726
|
+
console.log(`⚠️ Config directory: ${configDir} (will be created on first use)`);
|
|
1727
|
+
}
|
|
1728
|
+
|
|
1729
|
+
// Check installed agents
|
|
1730
|
+
console.log(chalk.bold("\nInstalled Agents:"));
|
|
1731
|
+
try {
|
|
1732
|
+
const registry = loadRegistry(repoRoot);
|
|
1733
|
+
const agents = getAgents(registry);
|
|
1734
|
+
for (const [id, agent] of Object.entries(agents)) {
|
|
1735
|
+
const installed = isAgentInstalled(id, agents);
|
|
1736
|
+
console.log(`${installed ? "✅" : "⬜"} ${id.padEnd(14)} ${agent.description || ""}`);
|
|
1737
|
+
}
|
|
1738
|
+
} catch (err) {
|
|
1739
|
+
console.log(chalk.red(` Error: ${err.message}`));
|
|
1574
1740
|
}
|
|
1741
|
+
|
|
1742
|
+
// Summary
|
|
1743
|
+
console.log(chalk.bold(`\n${"═".repeat(40)}`));
|
|
1744
|
+
if (issues === 0) {
|
|
1745
|
+
console.log(chalk.green.bold("✅ All checks passed! CPC is ready to use."));
|
|
1746
|
+
} else {
|
|
1747
|
+
console.log(chalk.red.bold(`❌ Found ${issues} issue(s). Please fix them before using CPC.`));
|
|
1748
|
+
}
|
|
1749
|
+
console.log();
|
|
1575
1750
|
});
|
|
1576
1751
|
}
|
|
1752
|
+
|
|
1753
|
+
/**
|
|
1754
|
+
* Compare semver versions
|
|
1755
|
+
*/
|
|
1756
|
+
function compareVersions(a, b) {
|
|
1757
|
+
const pa = a.split(".").map(Number);
|
|
1758
|
+
const pb = b.split(".").map(Number);
|
|
1759
|
+
for (let i = 0; i < 3; i++) {
|
|
1760
|
+
if (pa[i] > pb[i]) return 1;
|
|
1761
|
+
if (pa[i] < pb[i]) return -1;
|
|
1762
|
+
}
|
|
1763
|
+
return 0;
|
|
1764
|
+
}
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 基础模型字典 (Baseline)
|
|
3
|
+
*
|
|
4
|
+
* 来源: providers-baseline.json
|
|
5
|
+
* 用途: 当从外部 API (/v1/models) 获取到模型 ID 后, 用本字典补全
|
|
6
|
+
* - contextWindow: 模型上下文窗口
|
|
7
|
+
* - input: 模型支持的输入类型 (text / image / ...)
|
|
8
|
+
* - description: 模型中文描述
|
|
9
|
+
* 然后随 model 一起持久化到 ~/.config/cpc/providers.json
|
|
10
|
+
*
|
|
11
|
+
* 注意: 本字典不包含 maxTokens / reasoning, 仍由 src/commands/provider.mjs
|
|
12
|
+
* 中的 MODEL_DEFAULTS 处理. 安装到 Agent 时, 各 agent schema 不消费
|
|
13
|
+
* description, 该字段仅作为 provider.json 内的元信息保留.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
export const BASELINE_MODELS = [
|
|
17
|
+
{
|
|
18
|
+
modelId: "mimo-v2-omni",
|
|
19
|
+
contextWindow: 32768,
|
|
20
|
+
input: ["text"],
|
|
21
|
+
description: "小米 MiMo V2 全模态旗舰,文本/图像/音频/视频统一理解与生成。",
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
modelId: "mimo-v2-pro",
|
|
25
|
+
contextWindow: 32768,
|
|
26
|
+
input: ["text"],
|
|
27
|
+
description: "小米 MiMo V2 推理增强版,强项为复杂推理与长程任务。",
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
modelId: "mimo-v2-tts",
|
|
31
|
+
contextWindow: 32768,
|
|
32
|
+
input: ["text"],
|
|
33
|
+
description: "小米 MiMo V2 文本转语音合成模型,多音色/多语种。",
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
modelId: "mimo-v2.5",
|
|
37
|
+
contextWindow: 32768,
|
|
38
|
+
input: ["text"],
|
|
39
|
+
description: "小米 MiMo V2.5 通用对话基座,性价比首选。",
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
modelId: "mimo-v2.5-asr",
|
|
43
|
+
contextWindow: 32768,
|
|
44
|
+
input: ["text"],
|
|
45
|
+
description: "小米 MiMo V2.5 语音识别模型,音频转文字。",
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
modelId: "mimo-v2.5-pro",
|
|
49
|
+
contextWindow: 32768,
|
|
50
|
+
input: ["text"],
|
|
51
|
+
description: "小米 MiMo V2.5 推理增强版,复杂任务与编程。",
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
modelId: "mimo-v2.5-tts",
|
|
55
|
+
contextWindow: 32768,
|
|
56
|
+
input: ["text"],
|
|
57
|
+
description: "小米 MiMo V2.5 文本转语音合成模型。",
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
modelId: "mimo-v2.5-tts-voiceclone",
|
|
61
|
+
contextWindow: 32768,
|
|
62
|
+
input: ["text"],
|
|
63
|
+
description: "小米 MiMo V2.5 声音克隆 TTS,参考音频复刻音色。",
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
modelId: "mimo-v2.5-tts-voicedesign",
|
|
67
|
+
contextWindow: 32768,
|
|
68
|
+
input: ["text"],
|
|
69
|
+
description: "小米 MiMo V2.5 声音设计 TTS,按描述/特征生成新音色。",
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
modelId: "gemma4:e4b",
|
|
73
|
+
contextWindow: 32768,
|
|
74
|
+
input: ["text"],
|
|
75
|
+
description:
|
|
76
|
+
"Google Gemma 4 边缘端模型(4.5B effective / 8B with embeddings),128K 上下文,支持文本+图像+音频。",
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
modelId: "qwen3.6:27b",
|
|
80
|
+
contextWindow: 32768,
|
|
81
|
+
input: ["text"],
|
|
82
|
+
description:
|
|
83
|
+
"Qwen3.5 架构的 27.8B dense 本地模型(Q4_K_M 17GB),本地多用途对话与编程。",
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
modelId: "qwen3.6-plus",
|
|
87
|
+
contextWindow: 1000000,
|
|
88
|
+
input: ["text", "image"],
|
|
89
|
+
description:
|
|
90
|
+
"阿里 Qwen3.6 旗舰 VL 模型,主打 Agentic Coding、Vibe Coding 与 1M 多模态长上下文。",
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
modelId: "qwen3-vl-plus",
|
|
94
|
+
contextWindow: 1000000,
|
|
95
|
+
input: ["text", "image"],
|
|
96
|
+
description:
|
|
97
|
+
"通义千问视觉旗舰,OS World 视觉 Agent SOTA,文档/视频/OCR 全能理解。",
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
modelId: "qwen3.5-122b-a10b",
|
|
101
|
+
contextWindow: 262144,
|
|
102
|
+
input: ["text", "image"],
|
|
103
|
+
description:
|
|
104
|
+
"Qwen3.5 开源 MoE 旗舰(122B 总参/10B 激活),262K 上下文,原生视觉语言模型。",
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
modelId: "qwen3-coder-plus",
|
|
108
|
+
contextWindow: 1000000,
|
|
109
|
+
input: ["text"],
|
|
110
|
+
description:
|
|
111
|
+
"通义千问编码专项旗舰,1M 仓库级上下文,强工具调用与多文件重构。",
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
modelId: "text-embedding-v4",
|
|
115
|
+
contextWindow: 32000,
|
|
116
|
+
input: ["text"],
|
|
117
|
+
description:
|
|
118
|
+
"阿里 Qwen3-Embedding 旗舰,MTEB 多语言榜单领先,支持 8 种向量维度的语义检索首选。",
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
modelId: "GLM-5.0",
|
|
122
|
+
contextWindow: 200000,
|
|
123
|
+
input: ["text"],
|
|
124
|
+
description:
|
|
125
|
+
"智谱基座旗舰 744B MoE,编程对齐 Claude Opus 4.5,复杂系统工程与长程 Agent 基座。",
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
modelId: "GLM-5.0-anthropic",
|
|
129
|
+
contextWindow: 200000,
|
|
130
|
+
input: ["text"],
|
|
131
|
+
description: "GLM-5.0 的 Anthropic 协议兼容封装。",
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
modelId: "glm-5.1",
|
|
135
|
+
contextWindow: 200000,
|
|
136
|
+
input: ["text"],
|
|
137
|
+
description:
|
|
138
|
+
"智谱长程任务旗舰,可单任务持续自主工作 8 小时,编程对齐 Claude Opus 4.6。",
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
modelId: "glm-5.1-anthropic",
|
|
142
|
+
contextWindow: 200000,
|
|
143
|
+
input: ["text"],
|
|
144
|
+
description: "glm-5.1 的 Anthropic 协议兼容封装。",
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
modelId: "deepseek-v4-pro",
|
|
148
|
+
contextWindow: 1000000,
|
|
149
|
+
input: ["text"],
|
|
150
|
+
description:
|
|
151
|
+
"DeepSeek V4 旗舰 1.6T MoE,开源 SOTA 世界知识与 Agent Coding,支持双思考模式。",
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
modelId: "deepseek-v4-pro-anthropic",
|
|
155
|
+
contextWindow: 1000000,
|
|
156
|
+
input: ["text"],
|
|
157
|
+
description: "DeepSeek V4-Pro 的 Anthropic 协议封装。",
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
modelId: "deepseek-v4-flash",
|
|
161
|
+
contextWindow: 1000000,
|
|
162
|
+
input: ["text"],
|
|
163
|
+
description:
|
|
164
|
+
"DeepSeek V4 轻量版(284B/13B 激活),1M 上下文,主打高吞吐低成本日常 Agent。",
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
modelId: "DeepSeek-V3.2",
|
|
168
|
+
contextWindow: 128000,
|
|
169
|
+
input: ["text"],
|
|
170
|
+
description:
|
|
171
|
+
"DeepSeek V3 末代 DSA 稀疏注意力版本,平衡推理深度与输出长度,适合日常问答与 Agent。",
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
modelId: "Doubao-Seed-2.0-Pro",
|
|
175
|
+
contextWindow: 256000,
|
|
176
|
+
input: ["text", "image"],
|
|
177
|
+
description:
|
|
178
|
+
"字节豆包 2.0 旗舰,多模态视觉推理/空间推理 SOTA,全面对标 GPT-5.2 / Gemini 3 Pro。",
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
modelId: "Doubao-Seed-2.0-Code",
|
|
182
|
+
contextWindow: 256000,
|
|
183
|
+
input: ["text", "image"],
|
|
184
|
+
description:
|
|
185
|
+
"字节豆包 2.0 编程专版,前端强、支持 UI-to-Code 视觉理解,与 TRAE 集成最佳。",
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
modelId: "Kimi-K2.5",
|
|
189
|
+
contextWindow: 256000,
|
|
190
|
+
input: ["text", "image"],
|
|
191
|
+
description:
|
|
192
|
+
"月之暗面原生多模态旗舰,文本+视觉联合优化,Agent Swarm 与视觉编程(UI/视频生成代码)。",
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
modelId: "kimi-k2.6",
|
|
196
|
+
contextWindow: 256000,
|
|
197
|
+
input: ["text", "image"],
|
|
198
|
+
description:
|
|
199
|
+
"月之暗面 K2.6,开源 SOTA 长程编码(13 小时不间断)+ Agent Swarm 300 子智能体。",
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
modelId: "kimi-k2.6-anthropic",
|
|
203
|
+
contextWindow: 256000,
|
|
204
|
+
input: ["text", "image"],
|
|
205
|
+
description: "kimi-k2.6 的 Anthropic 协议兼容封装。",
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
modelId: "minimax-m3",
|
|
209
|
+
contextWindow: 1000000,
|
|
210
|
+
input: ["text", "image"],
|
|
211
|
+
description:
|
|
212
|
+
"MiniMax 2026 旗舰,国内首个前沿 Coding + 1M 上下文 + 原生多模态三合一模型。",
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
modelId: "MiniMax-M2.7",
|
|
216
|
+
contextWindow: 204800,
|
|
217
|
+
input: ["text"],
|
|
218
|
+
description:
|
|
219
|
+
"MiniMax M2.7,模型自我进化路径,端到端软件工程交付与 Office 复杂编辑。",
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
modelId: "minimax-m2.7",
|
|
223
|
+
contextWindow: 204800,
|
|
224
|
+
input: ["text"],
|
|
225
|
+
description: "MiniMax M2.7 小写别名,与 MiniMax-M2.7 同款。",
|
|
226
|
+
},
|
|
227
|
+
];
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* 在 baseline 字典中查找模型
|
|
231
|
+
* 匹配规则:
|
|
232
|
+
* 1) 精确匹配 (大小写不敏感)
|
|
233
|
+
* 2) 包含匹配 (查询 ID 包含 baseline 的 modelId 或反之)
|
|
234
|
+
* @param {string} modelId
|
|
235
|
+
* @returns {object|null} 命中的 baseline 记录
|
|
236
|
+
*/
|
|
237
|
+
export function lookupBaselineModel(modelId) {
|
|
238
|
+
if (!modelId) return null;
|
|
239
|
+
const lower = String(modelId).toLowerCase();
|
|
240
|
+
|
|
241
|
+
// 1) 精确匹配
|
|
242
|
+
const exact = BASELINE_MODELS.find((m) => m.modelId.toLowerCase() === lower);
|
|
243
|
+
if (exact) return exact;
|
|
244
|
+
|
|
245
|
+
// 2) 包含匹配: baseline.modelId ⊆ query.id (适用 qwen3.6 / MiniMax 大小写差异)
|
|
246
|
+
const contains = BASELINE_MODELS.find((m) =>
|
|
247
|
+
lower.includes(m.modelId.toLowerCase()),
|
|
248
|
+
);
|
|
249
|
+
if (contains) return contains;
|
|
250
|
+
|
|
251
|
+
// 3) 反向包含: query.id ⊆ baseline.modelId (适用 minimax-m3 命中 MiniMax-M2.7 等)
|
|
252
|
+
const reverse = BASELINE_MODELS.find((m) =>
|
|
253
|
+
m.modelId.toLowerCase().includes(lower),
|
|
254
|
+
);
|
|
255
|
+
return reverse || null;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* 用 baseline 字典补全 model 字段
|
|
260
|
+
* 命中时, baseline 的 contextWindow / input / description 会覆盖传入值
|
|
261
|
+
* 命不中则原样返回
|
|
262
|
+
* maxTokens / reasoning 不在 baseline 范围, 由调用方自己处理
|
|
263
|
+
* @param {object} model 至少有 { id }
|
|
264
|
+
* @returns {object} 增强后的 model
|
|
265
|
+
*/
|
|
266
|
+
export function enrichModelWithBaseline(model) {
|
|
267
|
+
if (!model || !model.id) return model;
|
|
268
|
+
const baseline = lookupBaselineModel(model.id);
|
|
269
|
+
if (!baseline) return model;
|
|
270
|
+
|
|
271
|
+
const enriched = { ...model };
|
|
272
|
+
|
|
273
|
+
if (typeof baseline.contextWindow === "number") {
|
|
274
|
+
enriched.contextWindow = baseline.contextWindow;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if (Array.isArray(baseline.input) && baseline.input.length > 0) {
|
|
278
|
+
enriched.input = [...baseline.input];
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
if (baseline.description) {
|
|
282
|
+
enriched.description = baseline.description;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
return enriched;
|
|
286
|
+
}
|