duojie-helper 0.2.7 → 0.2.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/package.json +1 -1
- package/src/tools/openclaw.js +108 -52
package/package.json
CHANGED
package/src/tools/openclaw.js
CHANGED
|
@@ -5,7 +5,7 @@ import { API_CONFIG, getModels } from '../index.js';
|
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* 获取 OpenClaw 配置路径
|
|
8
|
-
* 支持 macOS / Linux / Windows
|
|
8
|
+
* 支持 macOS / Linux / Windows(os.homedir() 跨平台处理)
|
|
9
9
|
*/
|
|
10
10
|
function getOpenClawConfigPaths() {
|
|
11
11
|
const home = os.homedir();
|
|
@@ -32,39 +32,28 @@ async function readConfig(filePath) {
|
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
/**
|
|
35
|
-
*
|
|
36
|
-
* 优先级:opus-4-6-kiro > opus-4-6 > opus+kiro > opus > sonnet > 第一个
|
|
35
|
+
* 将模型对象转换为 OpenClaw models.providers 格式
|
|
37
36
|
*/
|
|
38
|
-
function
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
if (claudeList.length > 0) {
|
|
49
|
-
return (
|
|
50
|
-
claudeList.find(m => m.id.includes('opus-4-6') && m.id.includes('kiro'))?.id ||
|
|
51
|
-
claudeList.find(m => m.id.includes('opus-4-6'))?.id ||
|
|
52
|
-
claudeList.find(m => m.id.includes('opus') && m.id.includes('kiro'))?.id ||
|
|
53
|
-
claudeList.find(m => m.id.includes('opus'))?.id ||
|
|
54
|
-
claudeList.find(m => m.id.includes('sonnet'))?.id ||
|
|
55
|
-
claudeList[0].id
|
|
56
|
-
);
|
|
57
|
-
}
|
|
58
|
-
return all[0].id;
|
|
37
|
+
function buildModelEntry(m, contextWindow, maxTokens) {
|
|
38
|
+
return {
|
|
39
|
+
id: m.id,
|
|
40
|
+
name: m.name || m.id,
|
|
41
|
+
reasoning: false,
|
|
42
|
+
input: ['text', 'image'],
|
|
43
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
44
|
+
contextWindow,
|
|
45
|
+
maxTokens,
|
|
46
|
+
};
|
|
59
47
|
}
|
|
60
48
|
|
|
61
49
|
/**
|
|
62
50
|
* 配置 OpenClaw
|
|
63
51
|
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
52
|
+
* 使用官方 models.providers 自定义 provider 格式:
|
|
53
|
+
* models.providers.<name>.baseUrl / apiKey / api / models[]
|
|
54
|
+
* agents.defaults.model.primary → "<provider>/<model-id>"
|
|
55
|
+
*
|
|
56
|
+
* 参考文档:https://docs.openclaw.ai/gateway/configuration-reference#custom-providers-and-base-urls
|
|
68
57
|
*/
|
|
69
58
|
export async function configureOpenClaw(apiKey) {
|
|
70
59
|
const paths = getOpenClawConfigPaths();
|
|
@@ -77,32 +66,105 @@ export async function configureOpenClaw(apiKey) {
|
|
|
77
66
|
: {};
|
|
78
67
|
|
|
79
68
|
const models = getModels();
|
|
80
|
-
|
|
81
|
-
|
|
69
|
+
|
|
70
|
+
// 构建各类模型列表
|
|
71
|
+
const claudeModels = (models.claude || []).map(m => buildModelEntry(m, 200000, 64000));
|
|
72
|
+
const gptModels = (models.gpt || []).map(m => buildModelEntry(m, 128000, 16384));
|
|
73
|
+
const geminiModels = (models.gemini || []).map(m => buildModelEntry(m, 1000000, 65536));
|
|
74
|
+
const otherModels = (models.other || []).map(m => buildModelEntry(m, 128000, 16384));
|
|
75
|
+
|
|
76
|
+
// 构建 providers(按模型类型分组,api 协议对应文档规范)
|
|
77
|
+
const providers = {};
|
|
78
|
+
|
|
79
|
+
if (claudeModels.length > 0) {
|
|
80
|
+
providers['duojie-claude'] = {
|
|
81
|
+
baseUrl: API_CONFIG.baseUrl,
|
|
82
|
+
apiKey,
|
|
83
|
+
api: 'anthropic-messages',
|
|
84
|
+
models: claudeModels,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
if (gptModels.length > 0) {
|
|
88
|
+
providers['duojie-gpt'] = {
|
|
89
|
+
baseUrl: API_CONFIG.baseUrl,
|
|
90
|
+
apiKey,
|
|
91
|
+
api: models.gpt[0]?.api === 'openai-responses' ? 'openai-responses' : 'openai-completions',
|
|
92
|
+
models: gptModels,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
if (geminiModels.length > 0) {
|
|
96
|
+
providers['duojie-gemini'] = {
|
|
97
|
+
baseUrl: API_CONFIG.baseUrl,
|
|
98
|
+
apiKey,
|
|
99
|
+
api: 'openai-completions',
|
|
100
|
+
models: geminiModels,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
if (otherModels.length > 0) {
|
|
104
|
+
providers['duojie-other'] = {
|
|
105
|
+
baseUrl: API_CONFIG.baseUrl,
|
|
106
|
+
apiKey,
|
|
107
|
+
api: 'openai-completions',
|
|
108
|
+
models: otherModels,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// 选出默认模型,优先级:opus-4-6-kiro > opus-4-6 > opus+kiro > opus > sonnet > 第一个
|
|
113
|
+
let defaultProvider = 'duojie-claude';
|
|
114
|
+
let defaultModelId = 'claude-opus-4-6-kiro';
|
|
115
|
+
|
|
116
|
+
if (claudeModels.length > 0) {
|
|
117
|
+
defaultProvider = 'duojie-claude';
|
|
118
|
+
defaultModelId = (
|
|
119
|
+
claudeModels.find(m => m.id.includes('opus-4-6') && m.id.includes('kiro')) ||
|
|
120
|
+
claudeModels.find(m => m.id.includes('opus-4-6')) ||
|
|
121
|
+
claudeModels.find(m => m.id.includes('opus') && m.id.includes('kiro')) ||
|
|
122
|
+
claudeModels.find(m => m.id.includes('opus')) ||
|
|
123
|
+
claudeModels.find(m => m.id.includes('sonnet')) ||
|
|
124
|
+
claudeModels[0]
|
|
125
|
+
).id;
|
|
126
|
+
} else if (gptModels.length > 0) {
|
|
127
|
+
defaultProvider = 'duojie-gpt';
|
|
128
|
+
defaultModelId = gptModels[0].id;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// 构建模型 allowlist(用于 /model 切换)
|
|
132
|
+
const modelAllowList = {};
|
|
133
|
+
for (const m of claudeModels) modelAllowList[`duojie-claude/${m.id}`] = { alias: m.name };
|
|
134
|
+
for (const m of gptModels) modelAllowList[`duojie-gpt/${m.id}`] = { alias: m.name };
|
|
135
|
+
for (const m of geminiModels) modelAllowList[`duojie-gemini/${m.id}`] = { alias: m.name };
|
|
136
|
+
for (const m of otherModels) modelAllowList[`duojie-other/${m.id}`] = { alias: m.name };
|
|
137
|
+
|
|
138
|
+
const totalModels = claudeModels.length + gptModels.length + geminiModels.length + otherModels.length;
|
|
82
139
|
|
|
83
140
|
// 合并配置,保留用户已有的非 Duojie 设置
|
|
84
141
|
const newConfig = {
|
|
85
142
|
...existingConfig,
|
|
86
|
-
|
|
87
|
-
...existingConfig.
|
|
88
|
-
|
|
89
|
-
|
|
143
|
+
models: {
|
|
144
|
+
...existingConfig.models,
|
|
145
|
+
mode: 'merge',
|
|
146
|
+
providers: {
|
|
147
|
+
...existingConfig.models?.providers,
|
|
148
|
+
...providers,
|
|
149
|
+
},
|
|
90
150
|
},
|
|
91
151
|
agents: {
|
|
92
152
|
...existingConfig.agents,
|
|
93
153
|
defaults: {
|
|
94
154
|
...existingConfig.agents?.defaults,
|
|
95
155
|
model: {
|
|
156
|
+
primary: `${defaultProvider}/${defaultModelId}`,
|
|
96
157
|
...existingConfig.agents?.defaults?.model,
|
|
97
|
-
|
|
158
|
+
},
|
|
159
|
+
models: {
|
|
160
|
+
...existingConfig.agents?.defaults?.models,
|
|
161
|
+
...modelAllowList,
|
|
98
162
|
},
|
|
99
163
|
},
|
|
100
164
|
},
|
|
101
165
|
_duojie: {
|
|
102
166
|
configuredAt: new Date().toISOString(),
|
|
103
|
-
version: '0.2.
|
|
104
|
-
defaultModel: defaultModelId,
|
|
105
|
-
totalModels,
|
|
167
|
+
version: '0.2.8',
|
|
106
168
|
},
|
|
107
169
|
};
|
|
108
170
|
|
|
@@ -110,7 +172,7 @@ export async function configureOpenClaw(apiKey) {
|
|
|
110
172
|
|
|
111
173
|
return {
|
|
112
174
|
success: true,
|
|
113
|
-
message: `已配置 ${totalModels}
|
|
175
|
+
message: `已配置 ${totalModels} 个模型 → ${paths.configFile}`,
|
|
114
176
|
configPath: paths.configFile,
|
|
115
177
|
hint: '运行 openclaw gateway restart 使配置生效',
|
|
116
178
|
};
|
|
@@ -130,10 +192,10 @@ configureOpenClaw.checkStatus = async function () {
|
|
|
130
192
|
|
|
131
193
|
if (await fs.pathExists(paths.configFile)) {
|
|
132
194
|
const config = await readConfig(paths.configFile);
|
|
133
|
-
if (config._duojie || config.
|
|
195
|
+
if (config._duojie || config.models?.providers?.['duojie-claude']) {
|
|
134
196
|
return { configured: true, message: '已配置 Duojie API' };
|
|
135
197
|
}
|
|
136
|
-
if (config.
|
|
198
|
+
if (config.models?.providers) {
|
|
137
199
|
return { configured: true, message: '已配置(非 Duojie)' };
|
|
138
200
|
}
|
|
139
201
|
}
|
|
@@ -150,18 +212,12 @@ configureOpenClaw.revoke = async function () {
|
|
|
150
212
|
if (await fs.pathExists(paths.configFile)) {
|
|
151
213
|
const config = await readConfig(paths.configFile);
|
|
152
214
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
delete config.
|
|
156
|
-
delete config.
|
|
157
|
-
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
// 若 primary 是 duojie/ 开头则清除
|
|
161
|
-
if (config.agents?.defaults?.model?.primary?.startsWith('duojie/')) {
|
|
162
|
-
delete config.agents.defaults.model.primary;
|
|
215
|
+
if (config.models?.providers) {
|
|
216
|
+
delete config.models.providers['duojie-claude'];
|
|
217
|
+
delete config.models.providers['duojie-gpt'];
|
|
218
|
+
delete config.models.providers['duojie-gemini'];
|
|
219
|
+
delete config.models.providers['duojie-other'];
|
|
163
220
|
}
|
|
164
|
-
|
|
165
221
|
delete config._duojie;
|
|
166
222
|
|
|
167
223
|
await fs.writeJson(paths.configFile, config, { spaces: 2 });
|