@adversity/coding-tool-x 3.1.2 → 3.1.3
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/CHANGELOG.md +17 -0
- package/dist/web/assets/Analytics-BIqc8Rin.css +1 -0
- package/dist/web/assets/Analytics-D2V09DHH.js +39 -0
- package/dist/web/assets/{ConfigTemplates-DvcbKKdS.js → ConfigTemplates-Bf_11LhH.js} +1 -1
- package/dist/web/assets/{Home-Cw-F_Wnu.js → Home-BRnW4FTS.js} +1 -1
- package/dist/web/assets/{Home-BJKPCBuk.css → Home-CyCIx4BA.css} +1 -1
- package/dist/web/assets/{PluginManager-jy_4GVxI.js → PluginManager-B9J32GhW.js} +1 -1
- package/dist/web/assets/{ProjectList-Df1-NcNr.js → ProjectList-5a19MWJk.js} +1 -1
- package/dist/web/assets/SessionList-CXUr6S7w.css +1 -0
- package/dist/web/assets/SessionList-Cxg5bAdT.js +1 -0
- package/dist/web/assets/{SkillManager-IRdseMKB.js → SkillManager-CVBr0CLi.js} +1 -1
- package/dist/web/assets/{Terminal-BasTyDut.js → Terminal-D2Xe_Q0H.js} +1 -1
- package/dist/web/assets/{WorkspaceManager-D-D2kK1V.js → WorkspaceManager-C7dwV94C.js} +1 -1
- package/dist/web/assets/icons-BxcwoY5F.js +1 -0
- package/dist/web/assets/index-BS9RA6SN.js +2 -0
- package/dist/web/assets/index-DUNAVDGb.css +1 -0
- package/dist/web/assets/naive-ui-BIXcURHZ.js +1 -0
- package/dist/web/assets/{vendors-CO3Upi1d.js → vendors-i5CBGnlm.js} +1 -1
- package/dist/web/assets/{vue-vendor-DqyWIXEb.js → vue-vendor-PKd8utv_.js} +1 -1
- package/dist/web/index.html +6 -6
- package/package.json +1 -1
- package/src/config/default.js +7 -29
- package/src/config/loader.js +6 -3
- package/src/config/model-metadata.js +102 -350
- package/src/config/model-metadata.json +125 -0
- package/src/server/api/channels.js +16 -39
- package/src/server/api/codex-channels.js +15 -43
- package/src/server/api/commands.js +0 -77
- package/src/server/api/config.js +4 -1
- package/src/server/api/gemini-channels.js +16 -40
- package/src/server/api/opencode-channels.js +25 -51
- package/src/server/api/opencode-proxy.js +1 -1
- package/src/server/api/opencode-sessions.js +0 -7
- package/src/server/api/sessions.js +11 -68
- package/src/server/api/settings.js +66 -39
- package/src/server/api/skills.js +0 -44
- package/src/server/api/statistics.js +115 -1
- package/src/server/codex-proxy-server.js +26 -55
- package/src/server/gemini-proxy-server.js +15 -14
- package/src/server/index.js +0 -3
- package/src/server/opencode-proxy-server.js +45 -121
- package/src/server/proxy-server.js +2 -4
- package/src/server/services/commands-service.js +0 -29
- package/src/server/services/config-templates-service.js +38 -28
- package/src/server/services/env-checker.js +73 -8
- package/src/server/services/plugins-service.js +37 -28
- package/src/server/services/pty-manager.js +22 -18
- package/src/server/services/skill-service.js +1 -49
- package/src/server/services/speed-test.js +40 -3
- package/src/server/services/statistics-service.js +238 -1
- package/src/server/utils/pricing.js +51 -60
- package/dist/web/assets/SessionList-BGJWyneI.css +0 -1
- package/dist/web/assets/SessionList-UWcZtC2r.js +0 -1
- package/dist/web/assets/icons-kcfLIMBB.js +0 -1
- package/dist/web/assets/index-CoB3zF0K.css +0 -1
- package/dist/web/assets/index-CryrSLv8.js +0 -2
- package/dist/web/assets/naive-ui-CSrLusZZ.js +0 -1
- package/src/server/api/convert.js +0 -260
- package/src/server/services/session-converter.js +0 -577
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
{
|
|
2
|
+
"lastUpdated": "2026-02-27",
|
|
3
|
+
"defaultModels": {
|
|
4
|
+
"claude": [
|
|
5
|
+
"claude-opus-4-6",
|
|
6
|
+
"claude-sonnet-4-6",
|
|
7
|
+
"claude-opus-4-5-20251101",
|
|
8
|
+
"claude-sonnet-4-5-20250929",
|
|
9
|
+
"claude-haiku-4-5-20251001"
|
|
10
|
+
],
|
|
11
|
+
"codex": [
|
|
12
|
+
"gpt-5.3-codex",
|
|
13
|
+
"gpt-5.2-codex",
|
|
14
|
+
"gpt-5.1-codex-max",
|
|
15
|
+
"gpt-5.1-codex",
|
|
16
|
+
"gpt-5.1-codex-mini",
|
|
17
|
+
"gpt-5-codex",
|
|
18
|
+
"gpt-5.2",
|
|
19
|
+
"gpt-5.1",
|
|
20
|
+
"gpt-5"
|
|
21
|
+
],
|
|
22
|
+
"gemini": [
|
|
23
|
+
"gemini-3.1-pro",
|
|
24
|
+
"gemini-3-pro-preview",
|
|
25
|
+
"gemini-3-flash-preview",
|
|
26
|
+
"gemini-2.5-pro",
|
|
27
|
+
"gemini-2.5-flash",
|
|
28
|
+
"gemini-2.5-flash-lite"
|
|
29
|
+
]
|
|
30
|
+
},
|
|
31
|
+
"defaultSpeedTestModels": {
|
|
32
|
+
"claude": "claude-haiku-4-5",
|
|
33
|
+
"codex": "gpt-5.2",
|
|
34
|
+
"gemini": "gemini-2.5-pro"
|
|
35
|
+
},
|
|
36
|
+
"aliases": {
|
|
37
|
+
"claude-opus-4-6": "claude-opus-4-6",
|
|
38
|
+
"claude-sonnet-4-6": "claude-sonnet-4-6",
|
|
39
|
+
"claude-opus-4-5": "claude-opus-4-5-20251101",
|
|
40
|
+
"claude-sonnet-4-5": "claude-sonnet-4-5-20250929",
|
|
41
|
+
"claude-haiku-4-5": "claude-haiku-4-5-20251001"
|
|
42
|
+
},
|
|
43
|
+
"models": {
|
|
44
|
+
"claude-opus-4-6": {
|
|
45
|
+
"limit": { "context": 200000, "output": 32000 },
|
|
46
|
+
"pricing": { "input": 15, "output": 75, "cacheCreation": 18.75, "cacheRead": 1.5 }
|
|
47
|
+
},
|
|
48
|
+
"claude-sonnet-4-6": {
|
|
49
|
+
"limit": { "context": 200000, "output": 64000 },
|
|
50
|
+
"pricing": { "input": 3, "output": 15, "cacheCreation": 3.75, "cacheRead": 0.3 }
|
|
51
|
+
},
|
|
52
|
+
"claude-opus-4-5-20251101": {
|
|
53
|
+
"limit": { "context": 200000, "output": 32000 },
|
|
54
|
+
"pricing": { "input": 5, "output": 25, "cacheCreation": 6.25, "cacheRead": 0.5 }
|
|
55
|
+
},
|
|
56
|
+
"claude-sonnet-4-5-20250929": {
|
|
57
|
+
"limit": { "context": 200000, "output": 64000 },
|
|
58
|
+
"pricing": { "input": 3, "output": 15, "cacheCreation": 3.75, "cacheRead": 0.3 }
|
|
59
|
+
},
|
|
60
|
+
"claude-haiku-4-5-20251001": {
|
|
61
|
+
"limit": { "context": 200000, "output": 8096 },
|
|
62
|
+
"pricing": { "input": 1, "output": 5, "cacheCreation": 1.25, "cacheRead": 0.1 }
|
|
63
|
+
},
|
|
64
|
+
"gpt-5": {
|
|
65
|
+
"limit": { "context": 1000000, "output": 32768 },
|
|
66
|
+
"pricing": { "input": 2, "output": 8 }
|
|
67
|
+
},
|
|
68
|
+
"gpt-5.1": {
|
|
69
|
+
"limit": { "context": 1000000, "output": 32768 },
|
|
70
|
+
"pricing": { "input": 2, "output": 8 }
|
|
71
|
+
},
|
|
72
|
+
"gpt-5.2": {
|
|
73
|
+
"limit": { "context": 1000000, "output": 32768 },
|
|
74
|
+
"pricing": { "input": 2, "output": 8 }
|
|
75
|
+
},
|
|
76
|
+
"gpt-5-codex": {
|
|
77
|
+
"limit": { "context": 1000000, "output": 32768 },
|
|
78
|
+
"pricing": { "input": 2, "output": 8 }
|
|
79
|
+
},
|
|
80
|
+
"gpt-5.1-codex": {
|
|
81
|
+
"limit": { "context": 1000000, "output": 32768 },
|
|
82
|
+
"pricing": { "input": 2, "output": 8 }
|
|
83
|
+
},
|
|
84
|
+
"gpt-5.1-codex-mini": {
|
|
85
|
+
"limit": { "context": 1000000, "output": 32768 },
|
|
86
|
+
"pricing": { "input": 1.5, "output": 6 }
|
|
87
|
+
},
|
|
88
|
+
"gpt-5.1-codex-max": {
|
|
89
|
+
"limit": { "context": 1000000, "output": 32768 },
|
|
90
|
+
"pricing": { "input": 3, "output": 12 }
|
|
91
|
+
},
|
|
92
|
+
"gpt-5.2-codex": {
|
|
93
|
+
"limit": { "context": 1000000, "output": 32768 },
|
|
94
|
+
"pricing": { "input": 2, "output": 8 }
|
|
95
|
+
},
|
|
96
|
+
"gpt-5.3-codex": {
|
|
97
|
+
"limit": { "context": 1000000, "output": 32768 },
|
|
98
|
+
"pricing": { "input": 2, "output": 8 }
|
|
99
|
+
},
|
|
100
|
+
"gemini-3.1-pro": {
|
|
101
|
+
"limit": { "context": 2097152, "output": 65536 },
|
|
102
|
+
"pricing": { "input": 2.5, "output": 15 }
|
|
103
|
+
},
|
|
104
|
+
"gemini-3-pro-preview": {
|
|
105
|
+
"limit": { "context": 2097152, "output": 65536 },
|
|
106
|
+
"pricing": { "input": 2.5, "output": 15 }
|
|
107
|
+
},
|
|
108
|
+
"gemini-3-flash-preview": {
|
|
109
|
+
"limit": { "context": 1048576, "output": 65536 },
|
|
110
|
+
"pricing": { "input": 0.3, "output": 1.2 }
|
|
111
|
+
},
|
|
112
|
+
"gemini-2.5-pro": {
|
|
113
|
+
"limit": { "context": 1048576, "output": 65536 },
|
|
114
|
+
"pricing": { "input": 1.25, "output": 10 }
|
|
115
|
+
},
|
|
116
|
+
"gemini-2.5-flash": {
|
|
117
|
+
"limit": { "context": 1048576, "output": 65536 },
|
|
118
|
+
"pricing": { "input": 0.15, "output": 0.6 }
|
|
119
|
+
},
|
|
120
|
+
"gemini-2.5-flash-lite": {
|
|
121
|
+
"limit": { "context": 1048576, "output": 65536 },
|
|
122
|
+
"pricing": { "input": 0.1, "output": 0.4 }
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
@@ -18,14 +18,15 @@ const {
|
|
|
18
18
|
sanitizeBatchConcurrency,
|
|
19
19
|
runWithConcurrencyLimit
|
|
20
20
|
} = require('../services/speed-test');
|
|
21
|
-
const {
|
|
22
|
-
probeModelAvailability,
|
|
23
|
-
fetchModelsFromProvider
|
|
24
|
-
} = require('../services/model-detector');
|
|
21
|
+
const { getDefaultSpeedTestModelByToolType } = require('../../config/model-metadata');
|
|
25
22
|
const { broadcastLog, broadcastProxyState, broadcastSchedulerState } = require('../websocket-server');
|
|
26
23
|
const { clearRedirectCache } = require('../proxy-server');
|
|
27
24
|
const CLAUDE_GATEWAY_SOURCE_TYPE = 'claude';
|
|
28
25
|
|
|
26
|
+
function getDefaultClaudeModel() {
|
|
27
|
+
return getDefaultSpeedTestModelByToolType('claude');
|
|
28
|
+
}
|
|
29
|
+
|
|
29
30
|
// GET /api/channels - Get all channels with health status
|
|
30
31
|
router.get('/', (req, res) => {
|
|
31
32
|
try {
|
|
@@ -271,41 +272,17 @@ router.get('/:id/models', async (req, res) => {
|
|
|
271
272
|
}
|
|
272
273
|
|
|
273
274
|
const gatewaySourceType = CLAUDE_GATEWAY_SOURCE_TYPE;
|
|
274
|
-
const
|
|
275
|
-
const
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
error: null,
|
|
286
|
-
errorHint: null
|
|
287
|
-
};
|
|
288
|
-
} else {
|
|
289
|
-
const usingConfiguredProbe = !!listResult.disabledByConfig;
|
|
290
|
-
const probe = await probeModelAvailability(channel, gatewaySourceType, {
|
|
291
|
-
stopOnFirstAvailable: false
|
|
292
|
-
});
|
|
293
|
-
const probedModels = Array.isArray(probe.availableModels) ? probe.availableModels : [];
|
|
294
|
-
|
|
295
|
-
result = {
|
|
296
|
-
models: probedModels,
|
|
297
|
-
supported: probedModels.length > 0,
|
|
298
|
-
cached: !!probe.cached || !!listResult.cached,
|
|
299
|
-
fallbackUsed: probedModels.length > 0,
|
|
300
|
-
lastChecked: probe.lastChecked || listResult.lastChecked || new Date().toISOString(),
|
|
301
|
-
error: probedModels.length > 0 ? null : (listResult.error || '无法获取可用模型'),
|
|
302
|
-
errorHint: probedModels.length > 0
|
|
303
|
-
? (usingConfiguredProbe ? '已按设置跳过 /v1/models,使用默认模型探测结果' : '模型列表接口不可用,已使用模型探测结果')
|
|
304
|
-
: (listResult.errorHint || (usingConfiguredProbe
|
|
305
|
-
? '已按设置跳过 /v1/models,且默认模型探测无可用结果'
|
|
306
|
-
: '模型列表接口不可用且模型探测无可用结果'))
|
|
307
|
-
};
|
|
308
|
-
}
|
|
275
|
+
const models = [getDefaultClaudeModel()];
|
|
276
|
+
const now = new Date().toISOString();
|
|
277
|
+
const result = {
|
|
278
|
+
models,
|
|
279
|
+
supported: models.length > 0,
|
|
280
|
+
cached: false,
|
|
281
|
+
fallbackUsed: false,
|
|
282
|
+
lastChecked: now,
|
|
283
|
+
error: models.length > 0 ? null : '未配置默认模型列表',
|
|
284
|
+
errorHint: models.length > 0 ? null : '请在设置中配置 Claude 默认模型'
|
|
285
|
+
};
|
|
309
286
|
|
|
310
287
|
res.json({
|
|
311
288
|
channelId: id,
|
|
@@ -20,12 +20,13 @@ const {
|
|
|
20
20
|
runWithConcurrencyLimit
|
|
21
21
|
} = require('../services/speed-test');
|
|
22
22
|
const { clearCodexRedirectCache } = require('../codex-proxy-server');
|
|
23
|
-
const {
|
|
24
|
-
fetchModelsFromProvider,
|
|
25
|
-
probeModelAvailability,
|
|
26
|
-
} = require('../services/model-detector');
|
|
23
|
+
const { getDefaultSpeedTestModelByToolType } = require('../../config/model-metadata');
|
|
27
24
|
const CODEX_GATEWAY_SOURCE_TYPE = 'codex';
|
|
28
25
|
|
|
26
|
+
function getDefaultCodexModel() {
|
|
27
|
+
return getDefaultSpeedTestModelByToolType('codex');
|
|
28
|
+
}
|
|
29
|
+
|
|
29
30
|
module.exports = (config) => {
|
|
30
31
|
/**
|
|
31
32
|
* GET /api/codex/channels
|
|
@@ -68,45 +69,16 @@ module.exports = (config) => {
|
|
|
68
69
|
}
|
|
69
70
|
|
|
70
71
|
const gatewaySourceType = CODEX_GATEWAY_SOURCE_TYPE;
|
|
71
|
-
const
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
if (probedModels.length > 0) {
|
|
83
|
-
result = {
|
|
84
|
-
models: probedModels,
|
|
85
|
-
supported: true,
|
|
86
|
-
cached: !!probe.cached,
|
|
87
|
-
fallbackUsed: false,
|
|
88
|
-
lastChecked: probe.lastChecked || listResult.lastChecked || new Date().toISOString(),
|
|
89
|
-
error: null,
|
|
90
|
-
errorHint: listResult.error
|
|
91
|
-
? (usingConfiguredProbe
|
|
92
|
-
? '已按设置跳过 /v1/models,使用默认模型探测结果'
|
|
93
|
-
: '模型列表接口不可用,已自动切换为模型探测结果')
|
|
94
|
-
: null
|
|
95
|
-
};
|
|
96
|
-
} else {
|
|
97
|
-
result = {
|
|
98
|
-
models: [],
|
|
99
|
-
supported: false,
|
|
100
|
-
cached: !!probe.cached || !!listResult.cached,
|
|
101
|
-
fallbackUsed: false,
|
|
102
|
-
lastChecked: probe.lastChecked || listResult.lastChecked || new Date().toISOString(),
|
|
103
|
-
error: listResult.error || '无法探测可用模型',
|
|
104
|
-
errorHint: listResult.errorHint || (usingConfiguredProbe
|
|
105
|
-
? '已按设置跳过 /v1/models,且默认模型探测无可用结果'
|
|
106
|
-
: '模型列表接口不可用且模型探测无可用结果')
|
|
107
|
-
};
|
|
108
|
-
}
|
|
109
|
-
}
|
|
72
|
+
const models = [getDefaultCodexModel()];
|
|
73
|
+
const result = {
|
|
74
|
+
models,
|
|
75
|
+
supported: models.length > 0,
|
|
76
|
+
cached: false,
|
|
77
|
+
fallbackUsed: false,
|
|
78
|
+
lastChecked: new Date().toISOString(),
|
|
79
|
+
error: models.length > 0 ? null : '未配置默认模型列表',
|
|
80
|
+
errorHint: models.length > 0 ? null : '请在设置中配置 Codex 默认模型'
|
|
81
|
+
};
|
|
110
82
|
|
|
111
83
|
res.json({
|
|
112
84
|
channelId: id,
|
|
@@ -479,81 +479,4 @@ router.post('/uninstall', (req, res) => {
|
|
|
479
479
|
}
|
|
480
480
|
});
|
|
481
481
|
|
|
482
|
-
// ==================== 格式转换 API ====================
|
|
483
|
-
|
|
484
|
-
/**
|
|
485
|
-
* 转换命令格式
|
|
486
|
-
* POST /api/commands/convert
|
|
487
|
-
* Body: { content, targetFormat }
|
|
488
|
-
* - content: 命令内容
|
|
489
|
-
* - targetFormat: 目标格式 ('claude' | 'codex')
|
|
490
|
-
*/
|
|
491
|
-
router.post('/convert', (req, res) => {
|
|
492
|
-
try {
|
|
493
|
-
const { platform, service } = getCommandsService(req);
|
|
494
|
-
const { content, targetFormat } = req.body;
|
|
495
|
-
|
|
496
|
-
if (!content) {
|
|
497
|
-
return res.status(400).json({
|
|
498
|
-
success: false,
|
|
499
|
-
message: '请提供命令内容'
|
|
500
|
-
});
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
if (!['claude', 'codex'].includes(targetFormat)) {
|
|
504
|
-
return res.status(400).json({
|
|
505
|
-
success: false,
|
|
506
|
-
message: '目标格式必须是 claude 或 codex'
|
|
507
|
-
});
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
const result = service.convertCommandFormat(content, targetFormat);
|
|
511
|
-
|
|
512
|
-
res.json({
|
|
513
|
-
success: true,
|
|
514
|
-
platform,
|
|
515
|
-
...result
|
|
516
|
-
});
|
|
517
|
-
} catch (err) {
|
|
518
|
-
console.error('[Commands API] Convert command error:', err);
|
|
519
|
-
res.status(500).json({
|
|
520
|
-
success: false,
|
|
521
|
-
message: err.message
|
|
522
|
-
});
|
|
523
|
-
}
|
|
524
|
-
});
|
|
525
|
-
|
|
526
|
-
/**
|
|
527
|
-
* 检测命令格式
|
|
528
|
-
* POST /api/commands/detect-format
|
|
529
|
-
* Body: { content }
|
|
530
|
-
*/
|
|
531
|
-
router.post('/detect-format', (req, res) => {
|
|
532
|
-
try {
|
|
533
|
-
const { platform, service } = getCommandsService(req);
|
|
534
|
-
const { content } = req.body;
|
|
535
|
-
|
|
536
|
-
if (!content) {
|
|
537
|
-
return res.status(400).json({
|
|
538
|
-
success: false,
|
|
539
|
-
message: '请提供命令内容'
|
|
540
|
-
});
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
const format = service.detectFormat(content);
|
|
544
|
-
|
|
545
|
-
res.json({
|
|
546
|
-
success: true,
|
|
547
|
-
platform,
|
|
548
|
-
format
|
|
549
|
-
});
|
|
550
|
-
} catch (err) {
|
|
551
|
-
console.error('[Commands API] Detect format error:', err);
|
|
552
|
-
res.status(500).json({
|
|
553
|
-
success: false,
|
|
554
|
-
message: err.message
|
|
555
|
-
});
|
|
556
|
-
}
|
|
557
|
-
});
|
|
558
|
-
|
|
559
482
|
module.exports = router;
|
package/src/server/api/config.js
CHANGED
|
@@ -44,9 +44,12 @@ function sanitizePricing(inputPricing, currentPricing) {
|
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
function normalizeModelDiscovery(modelDiscovery, currentValue = DEFAULT_CONFIG.modelDiscovery) {
|
|
47
|
+
const defaultModelDiscovery = DEFAULT_CONFIG.modelDiscovery && typeof DEFAULT_CONFIG.modelDiscovery === 'object'
|
|
48
|
+
? DEFAULT_CONFIG.modelDiscovery
|
|
49
|
+
: { useV1ModelsEndpoint: false };
|
|
47
50
|
const current = currentValue && typeof currentValue === 'object'
|
|
48
51
|
? currentValue
|
|
49
|
-
:
|
|
52
|
+
: defaultModelDiscovery;
|
|
50
53
|
const input = modelDiscovery && typeof modelDiscovery === 'object'
|
|
51
54
|
? modelDiscovery
|
|
52
55
|
: {};
|
|
@@ -19,12 +19,13 @@ const {
|
|
|
19
19
|
runWithConcurrencyLimit
|
|
20
20
|
} = require('../services/speed-test');
|
|
21
21
|
const { clearGeminiRedirectCache } = require('../gemini-proxy-server');
|
|
22
|
-
const {
|
|
23
|
-
probeModelAvailability,
|
|
24
|
-
fetchModelsFromProvider
|
|
25
|
-
} = require('../services/model-detector');
|
|
22
|
+
const { getDefaultSpeedTestModelByToolType } = require('../../config/model-metadata');
|
|
26
23
|
const GEMINI_GATEWAY_SOURCE_TYPE = 'gemini';
|
|
27
24
|
|
|
25
|
+
function getDefaultGeminiModel() {
|
|
26
|
+
return getDefaultSpeedTestModelByToolType('gemini');
|
|
27
|
+
}
|
|
28
|
+
|
|
28
29
|
module.exports = (config) => {
|
|
29
30
|
/**
|
|
30
31
|
* GET /api/gemini/channels
|
|
@@ -67,41 +68,16 @@ module.exports = (config) => {
|
|
|
67
68
|
}
|
|
68
69
|
|
|
69
70
|
const gatewaySourceType = GEMINI_GATEWAY_SOURCE_TYPE;
|
|
70
|
-
const
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
lastChecked: listResult.lastChecked || new Date().toISOString(),
|
|
81
|
-
error: null,
|
|
82
|
-
errorHint: null
|
|
83
|
-
};
|
|
84
|
-
} else {
|
|
85
|
-
const usingConfiguredProbe = !!listResult.disabledByConfig;
|
|
86
|
-
const probe = await probeModelAvailability(channel, gatewaySourceType, {
|
|
87
|
-
stopOnFirstAvailable: false
|
|
88
|
-
});
|
|
89
|
-
const probedModels = Array.isArray(probe.availableModels) ? probe.availableModels : [];
|
|
90
|
-
|
|
91
|
-
result = {
|
|
92
|
-
models: probedModels,
|
|
93
|
-
supported: probedModels.length > 0,
|
|
94
|
-
cached: !!probe.cached || !!listResult.cached,
|
|
95
|
-
fallbackUsed: false,
|
|
96
|
-
lastChecked: probe.lastChecked || listResult.lastChecked || new Date().toISOString(),
|
|
97
|
-
error: probedModels.length > 0 ? null : (listResult.error || '无法获取可用模型'),
|
|
98
|
-
errorHint: probedModels.length > 0
|
|
99
|
-
? (usingConfiguredProbe ? '已按设置跳过 /v1/models,使用默认模型探测结果' : '模型列表接口不可用,已自动切换为模型探测结果')
|
|
100
|
-
: (listResult.errorHint || (usingConfiguredProbe
|
|
101
|
-
? '已按设置跳过 /v1/models,且默认模型探测无可用结果'
|
|
102
|
-
: '模型列表接口不可用且模型探测无可用结果'))
|
|
103
|
-
};
|
|
104
|
-
}
|
|
71
|
+
const models = [getDefaultGeminiModel()];
|
|
72
|
+
const result = {
|
|
73
|
+
models,
|
|
74
|
+
supported: models.length > 0,
|
|
75
|
+
cached: false,
|
|
76
|
+
fallbackUsed: false,
|
|
77
|
+
lastChecked: new Date().toISOString(),
|
|
78
|
+
error: models.length > 0 ? null : '未配置默认模型列表',
|
|
79
|
+
errorHint: models.length > 0 ? null : '请在设置中配置 Gemini 默认模型'
|
|
80
|
+
};
|
|
105
81
|
|
|
106
82
|
res.json({
|
|
107
83
|
channelId: id,
|
|
@@ -157,7 +133,7 @@ module.exports = (config) => {
|
|
|
157
133
|
return res.status(400).json({ error: 'Missing required fields: apiKey' });
|
|
158
134
|
}
|
|
159
135
|
|
|
160
|
-
const channel = createChannel(name, baseUrl, apiKey, model ||
|
|
136
|
+
const channel = createChannel(name, baseUrl, apiKey, model || getDefaultGeminiModel(), {
|
|
161
137
|
websiteUrl,
|
|
162
138
|
enabled,
|
|
163
139
|
weight,
|
|
@@ -24,9 +24,9 @@ const {
|
|
|
24
24
|
const { setProxyConfig } = require('../services/opencode-settings-manager');
|
|
25
25
|
const {
|
|
26
26
|
fetchModelsFromProvider,
|
|
27
|
-
probeModelAvailability,
|
|
28
27
|
clearCache
|
|
29
28
|
} = require('../services/model-detector');
|
|
29
|
+
const { getDefaultSpeedTestModelByToolType } = require('../../config/model-metadata');
|
|
30
30
|
|
|
31
31
|
module.exports = (config) => {
|
|
32
32
|
function uniqueModels(models = []) {
|
|
@@ -44,31 +44,6 @@ module.exports = (config) => {
|
|
|
44
44
|
return result;
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
function collectChannelPreferredModels(channel) {
|
|
48
|
-
const candidates = [];
|
|
49
|
-
if (!channel || typeof channel !== 'object') return candidates;
|
|
50
|
-
|
|
51
|
-
candidates.push(channel.model);
|
|
52
|
-
candidates.push(channel.speedTestModel);
|
|
53
|
-
|
|
54
|
-
const modelConfig = channel.modelConfig;
|
|
55
|
-
if (modelConfig && typeof modelConfig === 'object') {
|
|
56
|
-
candidates.push(modelConfig.model);
|
|
57
|
-
candidates.push(modelConfig.opusModel);
|
|
58
|
-
candidates.push(modelConfig.sonnetModel);
|
|
59
|
-
candidates.push(modelConfig.haikuModel);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
if (Array.isArray(channel.modelRedirects)) {
|
|
63
|
-
channel.modelRedirects.forEach((rule) => {
|
|
64
|
-
candidates.push(rule?.from);
|
|
65
|
-
candidates.push(rule?.to);
|
|
66
|
-
});
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
return uniqueModels(candidates);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
47
|
function resolveGatewaySourceType(channel) {
|
|
73
48
|
const value = String(channel?.gatewaySourceType || '').trim().toLowerCase();
|
|
74
49
|
if (value === 'claude') return 'claude';
|
|
@@ -80,11 +55,17 @@ module.exports = (config) => {
|
|
|
80
55
|
return resolveGatewaySourceType(channel);
|
|
81
56
|
}
|
|
82
57
|
|
|
83
|
-
function
|
|
58
|
+
function isConverterEntryChannel(channel) {
|
|
84
59
|
const presetId = String(channel?.presetId || '').trim().toLowerCase();
|
|
85
60
|
return presetId === 'entry_claude' || presetId === 'entry_codex' || presetId === 'entry_gemini';
|
|
86
61
|
}
|
|
87
62
|
|
|
63
|
+
function getDefaultModelsByGatewaySourceType(gatewaySourceType) {
|
|
64
|
+
if (gatewaySourceType === 'claude') return [getDefaultSpeedTestModelByToolType('claude')];
|
|
65
|
+
if (gatewaySourceType === 'gemini') return [getDefaultSpeedTestModelByToolType('gemini')];
|
|
66
|
+
return [getDefaultSpeedTestModelByToolType('codex')];
|
|
67
|
+
}
|
|
68
|
+
|
|
88
69
|
function refreshEditedChannelModelCache(channelId) {
|
|
89
70
|
if (!channelId) return;
|
|
90
71
|
clearCache(channelId);
|
|
@@ -216,10 +197,24 @@ module.exports = (config) => {
|
|
|
216
197
|
}
|
|
217
198
|
|
|
218
199
|
const gatewaySourceType = resolveGatewaySourceType(channel);
|
|
219
|
-
|
|
200
|
+
if (isConverterEntryChannel(channel)) {
|
|
201
|
+
const models = uniqueModels(getDefaultModelsByGatewaySourceType(gatewaySourceType));
|
|
202
|
+
const now = new Date().toISOString();
|
|
203
|
+
return res.json({
|
|
204
|
+
channelId: channelId,
|
|
205
|
+
gatewaySourceType,
|
|
206
|
+
models,
|
|
207
|
+
supported: models.length > 0,
|
|
208
|
+
cached: false,
|
|
209
|
+
fallbackUsed: false,
|
|
210
|
+
fetchedAt: now,
|
|
211
|
+
error: models.length > 0 ? null : '未配置默认模型列表',
|
|
212
|
+
errorHint: models.length > 0 ? null : '请在设置中配置对应工具类型的默认模型'
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
|
|
220
216
|
const listResult = await fetchModelsFromProvider(channel, 'openai_compatible');
|
|
221
217
|
const listedModels = Array.isArray(listResult.models) ? uniqueModels(listResult.models) : [];
|
|
222
|
-
const shouldProbeByDefault = !!listResult.disabledByConfig;
|
|
223
218
|
let result;
|
|
224
219
|
|
|
225
220
|
if (listedModels.length > 0) {
|
|
@@ -232,28 +227,7 @@ module.exports = (config) => {
|
|
|
232
227
|
error: null,
|
|
233
228
|
errorHint: null
|
|
234
229
|
};
|
|
235
|
-
} else if (shouldProbeByDefault || isConverterPresetChannel(channel)) {
|
|
236
|
-
const probe = await probeModelAvailability(channel, gatewaySourceType, {
|
|
237
|
-
stopOnFirstAvailable: false,
|
|
238
|
-
preferredModels
|
|
239
|
-
});
|
|
240
|
-
const probedModels = Array.isArray(probe.availableModels) ? uniqueModels(probe.availableModels) : [];
|
|
241
|
-
|
|
242
|
-
result = {
|
|
243
|
-
models: probedModels,
|
|
244
|
-
supported: probedModels.length > 0,
|
|
245
|
-
cached: !!probe.cached || !!listResult.cached,
|
|
246
|
-
fallbackUsed: false,
|
|
247
|
-
lastChecked: probe.lastChecked || listResult.lastChecked || new Date().toISOString(),
|
|
248
|
-
error: probedModels.length > 0 ? null : (listResult.error || '无法获取可用模型'),
|
|
249
|
-
errorHint: probedModels.length > 0
|
|
250
|
-
? (shouldProbeByDefault ? '已按设置跳过 /v1/models,使用默认模型探测结果' : '模型列表接口不可用,已自动切换为模型探测结果')
|
|
251
|
-
: (listResult.errorHint || (shouldProbeByDefault
|
|
252
|
-
? '已按设置跳过 /v1/models,且默认模型探测无可用结果'
|
|
253
|
-
: '模型列表接口不可用且模型探测无可用结果'))
|
|
254
|
-
};
|
|
255
230
|
} else {
|
|
256
|
-
// 非入口转换器渠道:只请求 /v1/models,失败则返回空列表
|
|
257
231
|
result = {
|
|
258
232
|
models: [],
|
|
259
233
|
supported: false,
|
|
@@ -261,7 +235,7 @@ module.exports = (config) => {
|
|
|
261
235
|
fallbackUsed: false,
|
|
262
236
|
lastChecked: listResult.lastChecked || new Date().toISOString(),
|
|
263
237
|
error: listResult.error || '该渠道未返回可用模型列表',
|
|
264
|
-
errorHint: listResult.errorHint || '
|
|
238
|
+
errorHint: listResult.errorHint || '仅 OpenCode 非转换入口使用 /v1/models,请检查接口配置'
|
|
265
239
|
};
|
|
266
240
|
}
|
|
267
241
|
|
|
@@ -103,7 +103,7 @@ router.post('/start', async (req, res) => {
|
|
|
103
103
|
// 4. 设置代理配置(写入 OpenCode 配置文件)
|
|
104
104
|
// 收集每个渠道的模型列表,生成 per-channel provider 配置
|
|
105
105
|
|
|
106
|
-
//
|
|
106
|
+
// 若渠道未显式填写模型,回退使用代理聚合模型(来自 /v1/models)。
|
|
107
107
|
let detectedModels = [];
|
|
108
108
|
try {
|
|
109
109
|
detectedModels = await collectProxyModelList(enabledChannels, {
|
|
@@ -289,13 +289,6 @@ module.exports = (config) => {
|
|
|
289
289
|
|
|
290
290
|
const { exec } = require('child_process');
|
|
291
291
|
const { projectName, sessionId } = req.params;
|
|
292
|
-
const { targetTool } = req.body || {};
|
|
293
|
-
|
|
294
|
-
if (targetTool && targetTool !== 'opencode') {
|
|
295
|
-
return res.status(400).json({
|
|
296
|
-
error: 'OpenCode 会话暂不支持直接切换到其他 CLI,请使用 OpenCode 启动'
|
|
297
|
-
});
|
|
298
|
-
}
|
|
299
292
|
|
|
300
293
|
const session = getSessionById(sessionId);
|
|
301
294
|
if (!session) {
|