@renxqoo/renx-code 0.0.6 → 0.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.
Files changed (29) hide show
  1. package/README.md +40 -45
  2. package/bin/renx.cjs +12 -7
  3. package/package.json +16 -4
  4. package/src/files/workspace-files.ts +4 -6
  5. package/vendor/agent-root/src/agent/tool/base-tool.ts +1 -1
  6. package/vendor/agent-root/src/agent/tool/bash.ts +1 -1
  7. package/vendor/agent-root/src/agent/tool/file-edit-tool.ts +1 -1
  8. package/vendor/agent-root/src/agent/tool/file-history-list.ts +1 -1
  9. package/vendor/agent-root/src/agent/tool/file-history-restore.ts +1 -1
  10. package/vendor/agent-root/src/agent/tool/file-read-tool.ts +1 -1
  11. package/vendor/agent-root/src/agent/tool/glob.ts +1 -1
  12. package/vendor/agent-root/src/agent/tool/grep.ts +2 -2
  13. package/vendor/agent-root/src/agent/tool/lsp.ts +1 -1
  14. package/vendor/agent-root/src/agent/tool/skill-tool.ts +1 -1
  15. package/vendor/agent-root/src/agent/tool/tool-manager.ts +8 -9
  16. package/vendor/agent-root/src/agent/tool/web-fetch.ts +1 -1
  17. package/vendor/agent-root/src/agent/tool/web-search.ts +1 -1
  18. package/vendor/agent-root/src/config/__tests__/load-config-to-env.test.ts +109 -0
  19. package/vendor/agent-root/src/config/__tests__/loader.test.ts +114 -0
  20. package/vendor/agent-root/src/config/index.ts +1 -0
  21. package/vendor/agent-root/src/config/loader.ts +67 -4
  22. package/vendor/agent-root/src/config/types.ts +26 -0
  23. package/vendor/agent-root/src/providers/__tests__/registry.test.ts +82 -8
  24. package/vendor/agent-root/src/providers/index.ts +1 -1
  25. package/vendor/agent-root/src/providers/registry/model-config.ts +291 -44
  26. package/vendor/agent-root/src/providers/registry/provider-factory.ts +8 -4
  27. package/vendor/agent-root/src/providers/registry.ts +8 -8
  28. package/vendor/agent-root/src/providers/types/index.ts +1 -1
  29. package/vendor/agent-root/src/providers/types/registry.ts +10 -30
@@ -1,21 +1,34 @@
1
1
  /**
2
- * 模型配置存储
3
- *
4
- * 集中管理所有模型的配置信息,可从外部加载
2
+ * Central model configuration storage.
5
3
  */
6
4
 
7
- import type { ModelConfig, ModelId } from '../types';
5
+ import type { ModelConfig, ModelId, BuiltinModelId, ProviderType } from '../types';
6
+
7
+ const CUSTOM_MODELS_ENV_VAR = 'RENX_CUSTOM_MODELS_JSON';
8
+
9
+ export type ModelDefinition = Omit<ModelConfig, 'apiKey'>;
10
+ type PartialModelDefinition = Partial<ModelDefinition>;
11
+
12
+ const VALID_PROVIDERS: ProviderType[] = [
13
+ 'anthropic',
14
+ 'kimi',
15
+ 'deepseek',
16
+ 'glm',
17
+ 'minimax',
18
+ 'openai',
19
+ 'openrouter',
20
+ 'qwen',
21
+ ];
8
22
 
9
23
  /**
10
- * 模型配置表(以模型 ID 为键,不包含 apiKey 的配置)
24
+ * Built-in model definitions.
11
25
  */
12
- export const MODEL_DEFINITIONS: Record<ModelId, Omit<ModelConfig, 'apiKey'>> = {
13
- // Anthropic 系列
26
+ export const MODEL_DEFINITIONS: Record<BuiltinModelId, ModelDefinition> = {
14
27
  'claude-opus-4.6': {
15
28
  id: 'claude-opus-4.6',
16
29
  provider: 'anthropic',
17
30
  name: 'Claude Opus 4.6',
18
- baseURL: '',
31
+ baseURL: 'https://api.anthropic.com',
19
32
  endpointPath: '/v1/messages',
20
33
  envApiKey: 'ANTHROPIC_API_KEY',
21
34
  envBaseURL: 'ANTHROPIC_API_BASE',
@@ -25,8 +38,6 @@ export const MODEL_DEFINITIONS: Record<ModelId, Omit<ModelConfig, 'apiKey'>> = {
25
38
  features: ['streaming', 'function-calling', 'vision'],
26
39
  modalities: { image: true },
27
40
  },
28
-
29
- // GLM 系列
30
41
  'glm-4.7': {
31
42
  id: 'glm-4.7',
32
43
  provider: 'glm',
@@ -41,7 +52,6 @@ export const MODEL_DEFINITIONS: Record<ModelId, Omit<ModelConfig, 'apiKey'>> = {
41
52
  features: ['streaming', 'function-calling', 'vision'],
42
53
  modalities: { image: true },
43
54
  },
44
- // GLM 系列
45
55
  'glm-5': {
46
56
  id: 'glm-5',
47
57
  provider: 'glm',
@@ -56,7 +66,6 @@ export const MODEL_DEFINITIONS: Record<ModelId, Omit<ModelConfig, 'apiKey'>> = {
56
66
  features: ['streaming', 'function-calling', 'vision'],
57
67
  modalities: { image: true },
58
68
  },
59
- // MiniMax 系列
60
69
  'minimax-2.5': {
61
70
  id: 'minimax-2.5',
62
71
  provider: 'minimax',
@@ -70,7 +79,6 @@ export const MODEL_DEFINITIONS: Record<ModelId, Omit<ModelConfig, 'apiKey'>> = {
70
79
  LLMMAX_TOKENS: 200 * 1000,
71
80
  features: ['streaming', 'function-calling'],
72
81
  },
73
- // Kimi 系列
74
82
  'kimi-k2.5': {
75
83
  id: 'kimi-k2.5',
76
84
  provider: 'kimi',
@@ -86,7 +94,6 @@ export const MODEL_DEFINITIONS: Record<ModelId, Omit<ModelConfig, 'apiKey'>> = {
86
94
  temperature: 0.6,
87
95
  thinking: false,
88
96
  },
89
- // DeepSeek 系列
90
97
  'deepseek-reasoner': {
91
98
  id: 'deepseek-reasoner',
92
99
  provider: 'deepseek',
@@ -100,12 +107,11 @@ export const MODEL_DEFINITIONS: Record<ModelId, Omit<ModelConfig, 'apiKey'>> = {
100
107
  LLMMAX_TOKENS: 128 * 1000,
101
108
  features: ['streaming', 'function-calling'],
102
109
  },
103
- // Qwen 系列
104
110
  'qwen3.5-plus': {
105
111
  id: 'qwen3.5-plus',
106
112
  provider: 'qwen',
107
113
  name: 'Qwen 3.5 Plus',
108
- baseURL: 'https://coding.dashscope.aliyuncs.com/v1',
114
+ baseURL: 'https://dashscope.aliyuncs.com/compatible-mode/v1',
109
115
  endpointPath: '/chat/completions',
110
116
  envApiKey: 'QWEN_API_KEY',
111
117
  envBaseURL: 'QWEN_API_BASE',
@@ -115,12 +121,11 @@ export const MODEL_DEFINITIONS: Record<ModelId, Omit<ModelConfig, 'apiKey'>> = {
115
121
  features: ['streaming', 'function-calling'],
116
122
  modalities: { image: true },
117
123
  },
118
- // Qwen 系列
119
124
  'qwen3.5-max': {
120
125
  id: 'qwen3.5-max',
121
126
  provider: 'qwen',
122
127
  name: 'Qwen 3.5 Max',
123
- baseURL: 'https://coding.dashscope.aliyuncs.com/v1',
128
+ baseURL: 'https://dashscope.aliyuncs.com/compatible-mode/v1',
124
129
  endpointPath: '/chat/completions',
125
130
  envApiKey: 'QWEN_API_KEY',
126
131
  envBaseURL: 'QWEN_API_BASE',
@@ -133,12 +138,12 @@ export const MODEL_DEFINITIONS: Record<ModelId, Omit<ModelConfig, 'apiKey'>> = {
133
138
  id: 'qwen-kimi-k2.5',
134
139
  provider: 'qwen',
135
140
  name: 'qwen kimi k2.5',
136
- baseURL: 'https://coding.dashscope.aliyuncs.com/v1',
141
+ baseURL: 'https://dashscope.aliyuncs.com/compatible-mode/v1',
137
142
  endpointPath: '/chat/completions',
138
143
  envApiKey: 'QWEN_API_KEY',
139
144
  envBaseURL: 'QWEN_API_BASE',
140
145
  model: 'kimi-k2.5',
141
- max_tokens: 8000,
146
+ max_tokens: 1000 * 32,
142
147
  LLMMAX_TOKENS: 200 * 1000,
143
148
  features: ['streaming', 'function-calling'],
144
149
  },
@@ -146,12 +151,12 @@ export const MODEL_DEFINITIONS: Record<ModelId, Omit<ModelConfig, 'apiKey'>> = {
146
151
  id: 'qwen-glm-5',
147
152
  provider: 'qwen',
148
153
  name: 'Qwen GLM 5',
149
- baseURL: 'https://coding.dashscope.aliyuncs.com/v1',
154
+ baseURL: 'https://dashscope.aliyuncs.com/compatible-mode/v1',
150
155
  endpointPath: '/chat/completions',
151
156
  envApiKey: 'QWEN_API_KEY',
152
157
  envBaseURL: 'QWEN_API_BASE',
153
158
  model: 'glm-5',
154
- max_tokens: 8000,
159
+ max_tokens: 1000 * 32,
155
160
  LLMMAX_TOKENS: 200 * 1000,
156
161
  features: ['streaming', 'function-calling'],
157
162
  },
@@ -159,40 +164,26 @@ export const MODEL_DEFINITIONS: Record<ModelId, Omit<ModelConfig, 'apiKey'>> = {
159
164
  id: 'qwen-minimax-2.5',
160
165
  provider: 'qwen',
161
166
  name: 'Qwen MiniMax 2.5',
162
- baseURL: 'https://coding.dashscope.aliyuncs.com/v1',
167
+ baseURL: 'https://dashscope.aliyuncs.com/compatible-mode/v1',
163
168
  endpointPath: '/chat/completions',
164
169
  envApiKey: 'QWEN_API_KEY',
165
170
  envBaseURL: 'QWEN_API_BASE',
166
171
  model: 'MiniMax-M2.5',
167
- max_tokens: 8000,
172
+ max_tokens: 1000 * 32,
168
173
  LLMMAX_TOKENS: 200 * 1000,
169
174
  features: ['streaming', 'function-calling'],
170
175
  },
171
- // 'claude-4.6': {
172
- // id: 'wr-claude-4.6',
173
- // provider: 'openai',
174
- // name: 'Claude Opus 4.6',
175
- // baseURL: '',
176
- // endpointPath: '/chat/completions',
177
- // envApiKey: 'ANTHROPIC_API_KEY',
178
- // envBaseURL: 'ANTHROPIC_API_BASE',
179
- // model: 'claude-opus-4-6',
180
- // max_tokens: 16384,
181
- // LLMMAX_TOKENS: 1000 * 1000,
182
- // features: ['streaming', 'function-calling', 'vision'],
183
- // modalities: { image: true },
184
- // },
185
176
  'gpt-5.3': {
186
177
  id: 'gpt-5.3',
187
178
  provider: 'openai',
188
179
  name: 'GPT-5.3',
189
- baseURL: 'https://gmncode.cn/v1',
180
+ baseURL: 'https://api.openai.com/v1',
190
181
  endpointPath: '/responses',
191
182
  envApiKey: 'OPENAI_API_KEY',
192
183
  envBaseURL: 'OPENAI_API_BASE',
193
184
  model: 'gpt-5.3-codex',
194
- max_tokens: 128 * 1000,
195
- LLMMAX_TOKENS: 400 * 1000,
185
+ max_tokens: 1000 * 32,
186
+ LLMMAX_TOKENS: 258 * 1000,
196
187
  model_reasoning_effort: 'high',
197
188
  features: ['streaming', 'function-calling', 'reasoning'],
198
189
  modalities: { image: true },
@@ -201,12 +192,12 @@ export const MODEL_DEFINITIONS: Record<ModelId, Omit<ModelConfig, 'apiKey'>> = {
201
192
  id: 'gpt-5.4',
202
193
  provider: 'openai',
203
194
  name: 'GPT-5.4',
204
- baseURL: 'https://gmncode.cn/v1',
195
+ baseURL: 'https://api.openai.com/v1',
205
196
  endpointPath: '/responses',
206
197
  envApiKey: 'OPENAI_API_KEY',
207
198
  envBaseURL: 'OPENAI_API_BASE',
208
199
  model: 'gpt-5.4',
209
- max_tokens: 10000,
200
+ max_tokens: 1000 * 32,
210
201
  LLMMAX_TOKENS: 200 * 1000,
211
202
  model_reasoning_effort: 'high',
212
203
  features: ['streaming', 'function-calling'],
@@ -221,10 +212,266 @@ export const MODEL_DEFINITIONS: Record<ModelId, Omit<ModelConfig, 'apiKey'>> = {
221
212
  envApiKey: 'OPENROUTER_API_KEY',
222
213
  envBaseURL: 'OPENROUTER_API_BASE',
223
214
  model: 'openrouter/hunter-alpha',
224
- max_tokens: 32 * 10,
215
+ max_tokens: 1000 * 32,
225
216
  LLMMAX_TOKENS: 200 * 1000,
226
217
  model_reasoning_effort: 'high',
227
218
  features: ['streaming', 'function-calling'],
228
219
  modalities: { image: true },
229
220
  },
230
221
  };
222
+
223
+ function isPlainObject(value: unknown): value is Record<string, unknown> {
224
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
225
+ }
226
+
227
+ function isValidProvider(value: unknown): value is ProviderType {
228
+ return typeof value === 'string' && VALID_PROVIDERS.includes(value as ProviderType);
229
+ }
230
+
231
+ function isValidNumber(value: unknown): value is number {
232
+ return typeof value === 'number' && Number.isFinite(value) && value > 0;
233
+ }
234
+
235
+ function isValidReasoningEffort(
236
+ value: unknown
237
+ ): value is NonNullable<ModelDefinition['model_reasoning_effort']> {
238
+ return value === 'low' || value === 'medium' || value === 'high';
239
+ }
240
+
241
+ function sanitizeModalities(value: unknown): ModelDefinition['modalities'] | undefined {
242
+ if (value === undefined) {
243
+ return undefined;
244
+ }
245
+
246
+ if (!isPlainObject(value)) {
247
+ return undefined;
248
+ }
249
+
250
+ const result: NonNullable<ModelDefinition['modalities']> = {};
251
+
252
+ if (typeof value.image === 'boolean') {
253
+ result.image = value.image;
254
+ }
255
+ if (typeof value.audio === 'boolean') {
256
+ result.audio = value.audio;
257
+ }
258
+ if (typeof value.video === 'boolean') {
259
+ result.video = value.video;
260
+ }
261
+
262
+ return Object.keys(result).length > 0 ? result : {};
263
+ }
264
+
265
+ function sanitizePartialModelDefinition(
266
+ modelId: string,
267
+ value: unknown
268
+ ): PartialModelDefinition | null {
269
+ if (!isPlainObject(value)) {
270
+ return null;
271
+ }
272
+
273
+ const sanitized: PartialModelDefinition = { id: modelId as ModelId };
274
+
275
+ if (value.provider !== undefined) {
276
+ if (!isValidProvider(value.provider)) {
277
+ return null;
278
+ }
279
+ sanitized.provider = value.provider;
280
+ }
281
+
282
+ if (value.name !== undefined) {
283
+ if (typeof value.name !== 'string' || value.name.trim() === '') {
284
+ return null;
285
+ }
286
+ sanitized.name = value.name;
287
+ }
288
+
289
+ if (value.endpointPath !== undefined) {
290
+ if (typeof value.endpointPath !== 'string' || value.endpointPath.trim() === '') {
291
+ return null;
292
+ }
293
+ sanitized.endpointPath = value.endpointPath;
294
+ }
295
+
296
+ if (value.envApiKey !== undefined) {
297
+ if (typeof value.envApiKey !== 'string' || value.envApiKey.trim() === '') {
298
+ return null;
299
+ }
300
+ sanitized.envApiKey = value.envApiKey;
301
+ }
302
+
303
+ if (value.envBaseURL !== undefined) {
304
+ if (typeof value.envBaseURL !== 'string' || value.envBaseURL.trim() === '') {
305
+ return null;
306
+ }
307
+ sanitized.envBaseURL = value.envBaseURL;
308
+ }
309
+
310
+ if (value.baseURL !== undefined) {
311
+ if (typeof value.baseURL !== 'string' || value.baseURL.trim() === '') {
312
+ return null;
313
+ }
314
+ sanitized.baseURL = value.baseURL;
315
+ }
316
+
317
+ if (value.model !== undefined) {
318
+ if (typeof value.model !== 'string' || value.model.trim() === '') {
319
+ return null;
320
+ }
321
+ sanitized.model = value.model;
322
+ }
323
+
324
+ if (value.max_tokens !== undefined) {
325
+ if (!isValidNumber(value.max_tokens)) {
326
+ return null;
327
+ }
328
+ sanitized.max_tokens = value.max_tokens;
329
+ }
330
+
331
+ if (value.LLMMAX_TOKENS !== undefined) {
332
+ if (!isValidNumber(value.LLMMAX_TOKENS)) {
333
+ return null;
334
+ }
335
+ sanitized.LLMMAX_TOKENS = value.LLMMAX_TOKENS;
336
+ }
337
+
338
+ if (value.features !== undefined) {
339
+ if (!Array.isArray(value.features) || value.features.some((feature) => typeof feature !== 'string')) {
340
+ return null;
341
+ }
342
+ sanitized.features = [...value.features];
343
+ }
344
+
345
+ if (value.modalities !== undefined) {
346
+ const modalities = sanitizeModalities(value.modalities);
347
+ if (modalities === undefined) {
348
+ return null;
349
+ }
350
+ sanitized.modalities = modalities;
351
+ }
352
+
353
+ if (value.temperature !== undefined) {
354
+ if (typeof value.temperature !== 'number' || !Number.isFinite(value.temperature)) {
355
+ return null;
356
+ }
357
+ sanitized.temperature = value.temperature;
358
+ }
359
+
360
+ if (value.tool_stream !== undefined) {
361
+ if (typeof value.tool_stream !== 'boolean') {
362
+ return null;
363
+ }
364
+ sanitized.tool_stream = value.tool_stream;
365
+ }
366
+
367
+ if (value.thinking !== undefined) {
368
+ if (typeof value.thinking !== 'boolean') {
369
+ return null;
370
+ }
371
+ sanitized.thinking = value.thinking;
372
+ }
373
+
374
+ if (value.timeout !== undefined) {
375
+ if (!isValidNumber(value.timeout)) {
376
+ return null;
377
+ }
378
+ sanitized.timeout = value.timeout;
379
+ }
380
+
381
+ if (value.model_reasoning_effort !== undefined) {
382
+ if (!isValidReasoningEffort(value.model_reasoning_effort)) {
383
+ return null;
384
+ }
385
+ sanitized.model_reasoning_effort = value.model_reasoning_effort;
386
+ }
387
+
388
+ return sanitized;
389
+ }
390
+
391
+ function isCompleteModelDefinition(value: PartialModelDefinition): value is ModelDefinition {
392
+ return (
393
+ typeof value.id === 'string' &&
394
+ isValidProvider(value.provider) &&
395
+ typeof value.name === 'string' &&
396
+ typeof value.endpointPath === 'string' &&
397
+ typeof value.envApiKey === 'string' &&
398
+ typeof value.envBaseURL === 'string' &&
399
+ typeof value.baseURL === 'string' &&
400
+ typeof value.model === 'string' &&
401
+ isValidNumber(value.max_tokens) &&
402
+ isValidNumber(value.LLMMAX_TOKENS) &&
403
+ Array.isArray(value.features) &&
404
+ value.features.every((feature) => typeof feature === 'string')
405
+ );
406
+ }
407
+
408
+ function mergeModelDefinition(
409
+ modelId: string,
410
+ base: PartialModelDefinition,
411
+ override: PartialModelDefinition
412
+ ): ModelDefinition | null {
413
+ const merged: PartialModelDefinition = {
414
+ ...base,
415
+ ...override,
416
+ id: modelId as ModelId,
417
+ };
418
+
419
+ if (base.modalities || override.modalities) {
420
+ merged.modalities = {
421
+ ...(base.modalities ?? {}),
422
+ ...(override.modalities ?? {}),
423
+ };
424
+ }
425
+
426
+ if (!isCompleteModelDefinition(merged)) {
427
+ return null;
428
+ }
429
+
430
+ return merged;
431
+ }
432
+
433
+ export function readCustomModelDefinitionsFromEnv(
434
+ env: NodeJS.ProcessEnv = process.env
435
+ ): Record<string, PartialModelDefinition> {
436
+ const raw = env[CUSTOM_MODELS_ENV_VAR];
437
+ if (!raw) {
438
+ return {};
439
+ }
440
+
441
+ try {
442
+ const parsed = JSON.parse(raw) as unknown;
443
+ if (!isPlainObject(parsed)) {
444
+ return {};
445
+ }
446
+
447
+ const result: Record<string, PartialModelDefinition> = {};
448
+ for (const [modelId, modelConfig] of Object.entries(parsed)) {
449
+ const sanitized = sanitizePartialModelDefinition(modelId, modelConfig);
450
+ if (sanitized) {
451
+ result[modelId] = sanitized;
452
+ }
453
+ }
454
+
455
+ return result;
456
+ } catch {
457
+ return {};
458
+ }
459
+ }
460
+
461
+ export function getResolvedModelDefinitions(
462
+ env: NodeJS.ProcessEnv = process.env
463
+ ): Record<ModelId, ModelDefinition> {
464
+ const customDefinitions = readCustomModelDefinitionsFromEnv(env);
465
+ const resolved: Record<string, ModelDefinition> = { ...MODEL_DEFINITIONS };
466
+
467
+ for (const [modelId, customDefinition] of Object.entries(customDefinitions)) {
468
+ const baseDefinition = resolved[modelId] ?? ({ id: modelId as ModelId } as PartialModelDefinition);
469
+ const mergedDefinition = mergeModelDefinition(modelId, baseDefinition, customDefinition);
470
+
471
+ if (mergedDefinition) {
472
+ resolved[modelId] = mergedDefinition;
473
+ }
474
+ }
475
+
476
+ return resolved as Record<ModelId, ModelDefinition>;
477
+ }
@@ -9,7 +9,7 @@ import { AnthropicAdapter } from '../adapters/anthropic';
9
9
  import { OpenAICompatibleProvider, OpenAICompatibleConfig } from '../openai-compatible';
10
10
  import type { BaseProviderConfig, ModelId } from '../types';
11
11
  import type { BaseAPIAdapter } from '../adapters/base';
12
- import { MODEL_DEFINITIONS } from './model-config';
12
+ import { getResolvedModelDefinitions } from './model-config';
13
13
  import { KimiAdapter } from '../adapters/kimi';
14
14
  import { ResponsesAdapter } from '../adapters/responses';
15
15
 
@@ -32,7 +32,7 @@ export class ProviderFactory {
32
32
  throw new Error('ModelId is required.');
33
33
  }
34
34
 
35
- const modelConfig = MODEL_DEFINITIONS[modelId];
35
+ const modelConfig = getResolvedModelDefinitions()[modelId];
36
36
  if (!modelConfig) {
37
37
  throw new Error(`Unknown model: ${modelId}`);
38
38
  }
@@ -73,7 +73,7 @@ export class ProviderFactory {
73
73
  * @returns OpenAI Compatible Provider 实例
74
74
  */
75
75
  static create(modelId: ModelId, config: BaseProviderConfig): OpenAICompatibleProvider {
76
- const modelConfig = MODEL_DEFINITIONS[modelId];
76
+ const modelConfig = getResolvedModelDefinitions()[modelId];
77
77
  if (!modelConfig) {
78
78
  throw new Error(`Unknown model: ${modelId}`);
79
79
  }
@@ -92,7 +92,11 @@ export class ProviderFactory {
92
92
  modelId: ModelId,
93
93
  logger?: OpenAICompatibleConfig['logger']
94
94
  ): BaseAPIAdapter {
95
- const modelConfig = MODEL_DEFINITIONS[modelId];
95
+ const modelConfig = getResolvedModelDefinitions()[modelId];
96
+ if (!modelConfig) {
97
+ throw new Error(`Unknown model: ${modelId}`);
98
+ }
99
+
96
100
  if (modelConfig.provider === 'anthropic') {
97
101
  return new AnthropicAdapter({
98
102
  defaultModel: modelConfig.model,
@@ -6,11 +6,11 @@
6
6
  */
7
7
 
8
8
  import { ProviderFactory } from './registry/provider-factory';
9
- import { MODEL_DEFINITIONS } from './registry/model-config';
9
+ import { getResolvedModelDefinitions, MODEL_DEFINITIONS } from './registry/model-config';
10
10
  import type { ModelConfig, ModelId, ProviderType } from './types';
11
11
 
12
12
  // 导出类型
13
- export type { ProviderType, ModelId, ModelConfig } from './types';
13
+ export type { ProviderType, BuiltinModelId, ModelId, ModelConfig } from './types';
14
14
 
15
15
  // 导出模型配置
16
16
  export { MODEL_DEFINITIONS as MODEL_CONFIGS } from './registry/model-config';
@@ -47,7 +47,7 @@ export class ProviderRegistry {
47
47
  * 获取所有模型配置
48
48
  */
49
49
  static listModels(): ModelConfig[] {
50
- return Object.values(MODEL_DEFINITIONS).map((config) => ({
50
+ return Object.values(getResolvedModelDefinitions()).map((config) => ({
51
51
  ...config,
52
52
  apiKey: undefined,
53
53
  }));
@@ -57,7 +57,7 @@ export class ProviderRegistry {
57
57
  * 获取指定厂商的所有模型
58
58
  */
59
59
  static listModelsByProvider(provider: ProviderType): ModelConfig[] {
60
- return Object.values(MODEL_DEFINITIONS)
60
+ return Object.values(getResolvedModelDefinitions())
61
61
  .filter((m) => m.provider === provider)
62
62
  .map((config) => ({
63
63
  ...config,
@@ -69,14 +69,14 @@ export class ProviderRegistry {
69
69
  * 获取所有模型 ID
70
70
  */
71
71
  static getModelIds(): ModelId[] {
72
- return Object.keys(MODEL_DEFINITIONS) as ModelId[];
72
+ return Object.keys(getResolvedModelDefinitions()) as ModelId[];
73
73
  }
74
74
 
75
75
  /**
76
76
  * 获取指定模型的配置
77
77
  */
78
78
  static getModelConfig(modelId: ModelId): ModelConfig {
79
- const config = MODEL_DEFINITIONS[modelId];
79
+ const config = getResolvedModelDefinitions()[modelId];
80
80
  if (!config) {
81
81
  throw new Error(`Unknown model: ${modelId}`);
82
82
  }
@@ -87,7 +87,7 @@ export class ProviderRegistry {
87
87
  * 获取模型显示名称
88
88
  */
89
89
  static getModelName(modelId: ModelId): string {
90
- return MODEL_DEFINITIONS[modelId]?.name || modelId;
90
+ return getResolvedModelDefinitions()[modelId]?.name || modelId;
91
91
  }
92
92
 
93
93
  /**
@@ -95,7 +95,7 @@ export class ProviderRegistry {
95
95
  */
96
96
  static getProviders(): ProviderType[] {
97
97
  const providers = new Set<ProviderType>();
98
- Object.values(MODEL_DEFINITIONS).forEach((m) => providers.add(m.provider));
98
+ Object.values(getResolvedModelDefinitions()).forEach((m) => providers.add(m.provider));
99
99
  return Array.from(providers);
100
100
  }
101
101
  }
@@ -69,4 +69,4 @@ export {
69
69
  export type { BackoffConfig } from './errors';
70
70
 
71
71
  // Registry 相关类型
72
- export type { ProviderType, ModelId, ModelConfig } from './registry';
72
+ export type { ProviderType, BuiltinModelId, ModelId, ModelConfig } from './registry';
@@ -1,11 +1,9 @@
1
1
  /**
2
- * Registry 相关类型定义
3
- *
4
- * Provider Registry 相关的类型定义
2
+ * Registry related type definitions.
5
3
  */
6
4
 
7
5
  /**
8
- * Provider 厂商类型
6
+ * Supported provider types.
9
7
  */
10
8
  export type ProviderType =
11
9
  | 'anthropic'
@@ -18,22 +16,14 @@ export type ProviderType =
18
16
  | 'qwen';
19
17
 
20
18
  /**
21
- * 模型唯一标识
19
+ * Built-in model IDs shipped with the CLI.
22
20
  */
23
- export type ModelId =
24
- // Anthropic 系列
21
+ export type BuiltinModelId =
25
22
  | 'claude-opus-4.6'
26
- // GLM 系列
27
23
  | 'glm-4.7'
28
- // MiniMax 系列
29
24
  | 'minimax-2.5'
30
- // Kimi 系列
31
25
  | 'kimi-k2.5'
32
- // DeepSeek 系列
33
- // | 'deepseek-chat'
34
- // GLM 5.0 系列
35
26
  | 'glm-5'
36
- // Qwen 系列
37
27
  | 'qwen3.5-plus'
38
28
  | 'qwen-kimi-k2.5'
39
29
  | 'qwen-glm-5'
@@ -45,42 +35,32 @@ export type ModelId =
45
35
  | 'openrouter/hunter-alpha';
46
36
 
47
37
  /**
48
- * 模型配置
38
+ * Model IDs can include built-ins and user-defined config.json entries.
39
+ */
40
+ export type ModelId = BuiltinModelId | (string & {});
41
+
42
+ /**
43
+ * Model configuration.
49
44
  */
50
45
  export interface ModelConfig {
51
- /** 模型唯一标识 */
52
46
  id: ModelId;
53
- /** 所属厂商 */
54
47
  provider: ProviderType;
55
- /** 显示名称 */
56
48
  name: string;
57
- /** API 端点路径 */
58
49
  endpointPath: string;
59
- /** API Key 环境变量名 */
60
50
  envApiKey: string;
61
- /** Base URL 环境变量名 */
62
51
  envBaseURL: string;
63
- /** API 基础 URL */
64
52
  baseURL: string;
65
- /** API 模型名称 */
66
53
  model: string;
67
- /** 最大输出 token 数 */
68
54
  max_tokens: number;
69
- /** 最大上下文 token 数 */
70
55
  LLMMAX_TOKENS: number;
71
- /** 支持的特性 */
72
56
  features: string[];
73
- /** 多模态输入能力 */
74
57
  modalities?: {
75
58
  image?: boolean;
76
59
  audio?: boolean;
77
60
  video?: boolean;
78
61
  };
79
- /** API 密钥(可选) */
80
62
  apiKey?: string;
81
- /** 温度(可选) */
82
63
  temperature?: number;
83
- /** 默认工具流式输出(可选) */
84
64
  tool_stream?: boolean;
85
65
  thinking?: boolean;
86
66
  timeout?: number;