@lobehub/chat 1.111.10 → 1.111.11

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 (51) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/changelog/v1.json +9 -0
  3. package/docs/development/state-management/state-management-intro.mdx +2 -2
  4. package/docs/development/state-management/state-management-intro.zh-CN.mdx +2 -2
  5. package/package.json +1 -1
  6. package/packages/model-runtime/src/RouterRuntime/createRuntime.ts +9 -1
  7. package/packages/model-runtime/src/ai302/index.ts +1 -1
  8. package/packages/model-runtime/src/aihubmix/index.ts +28 -71
  9. package/packages/model-runtime/src/anthropic/index.ts +6 -26
  10. package/packages/model-runtime/src/giteeai/index.ts +2 -37
  11. package/packages/model-runtime/src/github/index.ts +33 -44
  12. package/packages/model-runtime/src/modelscope/index.ts +2 -38
  13. package/packages/model-runtime/src/moonshot/index.ts +2 -36
  14. package/packages/model-runtime/src/novita/__snapshots__/index.test.ts.snap +40 -22
  15. package/packages/model-runtime/src/novita/index.ts +1 -32
  16. package/packages/model-runtime/src/nvidia/index.ts +1 -1
  17. package/packages/model-runtime/src/openai/__snapshots__/index.test.ts.snap +63 -7
  18. package/packages/model-runtime/src/openai/index.ts +1 -1
  19. package/packages/model-runtime/src/openrouter/__snapshots__/index.test.ts.snap +6 -21
  20. package/packages/model-runtime/src/openrouter/index.ts +29 -37
  21. package/packages/model-runtime/src/qiniu/index.ts +3 -27
  22. package/packages/model-runtime/src/qwen/index.ts +1 -1
  23. package/packages/model-runtime/src/siliconcloud/index.ts +1 -1
  24. package/packages/model-runtime/src/utils/modelParse.test.ts +6 -6
  25. package/packages/model-runtime/src/utils/modelParse.ts +238 -40
  26. package/packages/model-runtime/src/utils/openaiCompatibleFactory/index.test.ts +18 -0
  27. package/packages/model-runtime/src/utils/streams/openai/openai.ts +12 -0
  28. package/packages/model-runtime/src/v0/index.ts +2 -2
  29. package/packages/model-runtime/src/volcengine/index.ts +1 -1
  30. package/packages/model-runtime/src/xai/index.ts +2 -24
  31. package/packages/model-runtime/src/zhipu/index.ts +1 -1
  32. package/src/config/aiModels/aihubmix.ts +1 -7
  33. package/src/config/aiModels/anthropic.ts +24 -4
  34. package/src/config/aiModels/fal.ts +20 -3
  35. package/src/config/aiModels/google.ts +60 -6
  36. package/src/config/aiModels/groq.ts +4 -21
  37. package/src/config/aiModels/hunyuan.ts +1 -1
  38. package/src/config/aiModels/mistral.ts +22 -5
  39. package/src/config/aiModels/moonshot.ts +20 -0
  40. package/src/config/aiModels/openai.ts +0 -43
  41. package/src/config/aiModels/qwen.ts +113 -3
  42. package/src/config/aiModels/sensenova.ts +6 -6
  43. package/src/config/aiModels/siliconcloud.ts +80 -0
  44. package/src/config/aiModels/stepfun.ts +38 -4
  45. package/src/config/aiModels/zhipu.ts +33 -8
  46. package/src/config/modelProviders/aihubmix.ts +1 -1
  47. package/src/config/modelProviders/mistral.ts +1 -0
  48. package/src/config/modelProviders/openai.ts +1 -1
  49. package/src/config/modelProviders/qwen.ts +1 -1
  50. package/src/config/modelProviders/v0.ts +1 -0
  51. package/src/config/modelProviders/volcengine.ts +1 -0
@@ -1,5 +1,7 @@
1
1
  import type { ChatModelCard } from '@/types/llm';
2
2
 
3
+ import type { ModelProviderKey } from '../types';
4
+
3
5
  export interface ModelProcessorConfig {
4
6
  excludeKeywords?: readonly string[]; // 对符合的模型不添加标签
5
7
  functionCallKeywords?: readonly string[];
@@ -26,7 +28,12 @@ export const MODEL_LIST_CONFIGS = {
26
28
  llama: {
27
29
  functionCallKeywords: ['llama-3.2', 'llama-3.3', 'llama-4'],
28
30
  reasoningKeywords: [],
29
- visionKeywords: [],
31
+ visionKeywords: ['llava'],
32
+ },
33
+ moonshot: {
34
+ functionCallKeywords: ['moonshot', 'kimi'],
35
+ reasoningKeywords: ['thinking'],
36
+ visionKeywords: ['vision', 'kimi-latest', 'kimi-thinking-preview'],
30
37
  },
31
38
  openai: {
32
39
  excludeKeywords: ['audio'],
@@ -45,7 +52,7 @@ export const MODEL_LIST_CONFIGS = {
45
52
  'qwen2.5',
46
53
  'qwen3',
47
54
  ],
48
- reasoningKeywords: ['qvq', 'qwq', 'qwen3'],
55
+ reasoningKeywords: ['qvq', 'qwq', 'qwen3', '!-instruct-', '!-coder-'],
49
56
  visionKeywords: ['qvq', 'vl'],
50
57
  },
51
58
  v0: {
@@ -54,9 +61,14 @@ export const MODEL_LIST_CONFIGS = {
54
61
  visionKeywords: ['v0'],
55
62
  },
56
63
  volcengine: {
57
- functionCallKeywords: ['doubao-1.5'],
58
- reasoningKeywords: ['thinking', '-r1'],
59
- visionKeywords: ['vision', '-m'],
64
+ functionCallKeywords: ['1.5', '1-5', '1.6', '1-6'],
65
+ reasoningKeywords: ['thinking', 'seed', 'ui-tars'],
66
+ visionKeywords: ['vision', '-m', 'seed', 'ui-tars'],
67
+ },
68
+ xai: {
69
+ functionCallKeywords: ['grok'],
70
+ reasoningKeywords: ['mini', 'grok-4'],
71
+ visionKeywords: ['vision', 'grok-4'],
60
72
  },
61
73
  zeroone: {
62
74
  functionCallKeywords: ['fc'],
@@ -65,7 +77,7 @@ export const MODEL_LIST_CONFIGS = {
65
77
  zhipu: {
66
78
  functionCallKeywords: ['glm-4', 'glm-z1'],
67
79
  reasoningKeywords: ['glm-zero', 'glm-z1', 'glm-4.5'],
68
- visionKeywords: ['glm-4v'],
80
+ visionKeywords: ['glm-4v', 'glm-4.1v', 'glm-4.5v'],
69
81
  },
70
82
  } as const;
71
83
 
@@ -74,15 +86,107 @@ export const PROVIDER_DETECTION_CONFIG = {
74
86
  anthropic: ['claude'],
75
87
  deepseek: ['deepseek'],
76
88
  google: ['gemini'],
77
- llama: ['llama'],
89
+ llama: ['llama', 'llava'],
90
+ moonshot: ['moonshot', 'kimi'],
78
91
  openai: ['o1', 'o3', 'o4', 'gpt-'],
79
92
  qwen: ['qwen', 'qwq', 'qvq'],
80
93
  v0: ['v0'],
81
94
  volcengine: ['doubao'],
95
+ xai: ['grok'],
82
96
  zeroone: ['yi-'],
83
97
  zhipu: ['glm'],
84
98
  } as const;
85
99
 
100
+ // 图像模型关键词配置
101
+ export const IMAGE_MODEL_KEYWORDS = [
102
+ 'dall-e',
103
+ 'dalle',
104
+ 'midjourney',
105
+ 'stable-diffusion',
106
+ 'sd',
107
+ 'flux',
108
+ 'imagen',
109
+ 'firefly',
110
+ 'cogview',
111
+ 'wanxiang',
112
+ 'DESCRIBE',
113
+ 'UPSCALE',
114
+ '-image',
115
+ '^V3',
116
+ '^V_2',
117
+ '^V_1',
118
+ ] as const;
119
+
120
+ /**
121
+ * 检测关键词列表是否匹配模型ID(支持多种匹配模式)
122
+ * @param modelId 模型ID(小写)
123
+ * @param keywords 关键词列表,支持以下前缀:
124
+ * - ^ 开头:只在模型ID开头匹配
125
+ * - ! 开头:排除匹配,优先级最高
126
+ * - 无前缀:包含匹配(默认行为)
127
+ * @returns 是否匹配(排除逻辑优先)
128
+ */
129
+ const isKeywordListMatch = (modelId: string, keywords: readonly string[]): boolean => {
130
+ // 先检查排除规则(感叹号开头)
131
+ const excludeKeywords = keywords.filter((keyword) => keyword.startsWith('!'));
132
+ const includeKeywords = keywords.filter((keyword) => !keyword.startsWith('!'));
133
+
134
+ // 如果匹配任何排除规则,直接返回 false
135
+ for (const excludeKeyword of excludeKeywords) {
136
+ const keywordWithoutPrefix = excludeKeyword.slice(1);
137
+ const isMatch = keywordWithoutPrefix.startsWith('^')
138
+ ? modelId.startsWith(keywordWithoutPrefix.slice(1))
139
+ : modelId.includes(keywordWithoutPrefix);
140
+
141
+ if (isMatch) {
142
+ return false;
143
+ }
144
+ }
145
+
146
+ // 检查包含规则
147
+ return includeKeywords.some((keyword) => {
148
+ if (keyword.startsWith('^')) {
149
+ // ^ 开头则只在开头匹配
150
+ const keywordWithoutPrefix = keyword.slice(1);
151
+ return modelId.startsWith(keywordWithoutPrefix);
152
+ }
153
+ // 默认行为:包含匹配
154
+ return modelId.includes(keyword);
155
+ });
156
+ };
157
+
158
+ /**
159
+ * 根据提供商类型查找对应的本地模型配置
160
+ * @param modelId 模型ID
161
+ * @param provider 提供商类型
162
+ * @returns 匹配的本地模型配置
163
+ */
164
+ const findKnownModelByProvider = async (
165
+ modelId: string,
166
+ provider: keyof typeof MODEL_LIST_CONFIGS,
167
+ ): Promise<any> => {
168
+ const lowerModelId = modelId.toLowerCase();
169
+
170
+ try {
171
+ // 动态构建导入路径
172
+ const modulePath = `@/config/aiModels/${provider}`;
173
+
174
+ // 尝试动态导入对应的配置文件
175
+ const moduleImport = await import(modulePath);
176
+ const providerModels = moduleImport.default;
177
+
178
+ // 如果导入成功且有数据,进行查找
179
+ if (Array.isArray(providerModels)) {
180
+ return providerModels.find((m) => m.id.toLowerCase() === lowerModelId);
181
+ }
182
+
183
+ return null;
184
+ } catch {
185
+ // 如果导入失败(文件不存在或其他错误),返回 null
186
+ return null;
187
+ }
188
+ };
189
+
86
190
  /**
87
191
  * 检测单个模型的提供商类型
88
192
  * @param modelId 模型ID
@@ -92,7 +196,7 @@ export const detectModelProvider = (modelId: string): keyof typeof MODEL_LIST_CO
92
196
  const lowerModelId = modelId.toLowerCase();
93
197
 
94
198
  for (const [provider, keywords] of Object.entries(PROVIDER_DETECTION_CONFIG)) {
95
- const hasKeyword = keywords.some((keyword) => lowerModelId.includes(keyword));
199
+ const hasKeyword = isKeywordListMatch(lowerModelId, keywords);
96
200
 
97
201
  if (hasKeyword && provider in MODEL_LIST_CONFIGS) {
98
202
  return provider as keyof typeof MODEL_LIST_CONFIGS;
@@ -102,6 +206,44 @@ export const detectModelProvider = (modelId: string): keyof typeof MODEL_LIST_CO
102
206
  return 'openai';
103
207
  };
104
208
 
209
+ /**
210
+ * 将时间戳转换为日期字符串
211
+ * @param timestamp 时间戳(秒)
212
+ * @returns 格式化的日期字符串 (YYYY-MM-DD)
213
+ */
214
+ const formatTimestampToDate = (timestamp: number): string => {
215
+ const date = new Date(timestamp * 1000); // 将秒转换为毫秒
216
+ return date.toISOString().split('T')[0]; // 返回 YYYY-MM-DD 格式
217
+ };
218
+
219
+ /**
220
+ * 处理 releasedAt 字段
221
+ * @param model 模型对象
222
+ * @param knownModel 已知模型配置
223
+ * @returns 处理后的 releasedAt 值
224
+ */
225
+ const processReleasedAt = (model: any, knownModel?: any): string | undefined => {
226
+ // 优先检查 model.created
227
+ if (model.created !== undefined && model.created !== null) {
228
+ // 检查是否为时间戳格式
229
+ if (typeof model.created === 'number' && model.created > 1_630_000_000) {
230
+ // AiHubMix 错误时间戳为 1626777600
231
+ return formatTimestampToDate(model.created);
232
+ }
233
+ // 如果 created 是字符串且已经是日期格式,直接返回
234
+ if (typeof model.created === 'string') {
235
+ // Anthropic:若为 '2025-02-19T00:00:00Z' 只取日期部分
236
+ if (/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/.test(model.created)) {
237
+ return model.created.split('T')[0];
238
+ }
239
+ return model.created;
240
+ }
241
+ }
242
+
243
+ // 回退到原有逻辑
244
+ return model.releasedAt ?? knownModel?.releasedAt ?? undefined;
245
+ };
246
+
105
247
  /**
106
248
  * 处理模型卡片的通用逻辑
107
249
  */
@@ -117,31 +259,41 @@ const processModelCard = (
117
259
  excludeKeywords = [],
118
260
  } = config;
119
261
 
120
- const isExcludedModel = excludeKeywords.some((keyword) =>
121
- model.id.toLowerCase().includes(keyword),
122
- );
262
+ const isExcludedModel = isKeywordListMatch(model.id.toLowerCase(), excludeKeywords);
123
263
 
124
264
  return {
125
265
  contextWindowTokens: model.contextWindowTokens ?? knownModel?.contextWindowTokens ?? undefined,
126
- displayName: model.displayName ?? knownModel?.displayName ?? model.id,
127
- enabled: knownModel?.enabled || false,
266
+ description: model.description ?? knownModel?.description ?? '',
267
+ displayName: (model.displayName ?? knownModel?.displayName ?? model.id)
268
+ .replaceAll(/\s*[((][^))]*[))]\s*/g, '')
269
+ .trim(), // 去除括号内容
270
+ enabled: model?.enabled || false,
128
271
  functionCall:
129
- (functionCallKeywords.some((keyword) => model.id.toLowerCase().includes(keyword)) &&
130
- !isExcludedModel) ||
131
- knownModel?.abilities?.functionCall ||
132
- false,
272
+ model.functionCall ??
273
+ knownModel?.abilities?.functionCall ??
274
+ ((isKeywordListMatch(model.id.toLowerCase(), functionCallKeywords) && !isExcludedModel) ||
275
+ false),
133
276
  id: model.id,
134
277
  maxOutput: model.maxOutput ?? knownModel?.maxOutput ?? undefined,
278
+ // pricing: knownModel?.pricing ?? undefined,
135
279
  reasoning:
136
- reasoningKeywords.some((keyword) => model.id.toLowerCase().includes(keyword)) ||
137
- knownModel?.abilities?.reasoning ||
138
- false,
139
- type: model.type || knownModel?.type || 'chat',
280
+ model.reasoning ??
281
+ knownModel?.abilities?.reasoning ??
282
+ (isKeywordListMatch(model.id.toLowerCase(), reasoningKeywords) || false),
283
+ releasedAt: processReleasedAt(model, knownModel),
284
+ type:
285
+ model.type ||
286
+ knownModel?.type ||
287
+ (isKeywordListMatch(
288
+ model.id.toLowerCase(),
289
+ IMAGE_MODEL_KEYWORDS.map((k) => k.toLowerCase()),
290
+ )
291
+ ? 'image'
292
+ : 'chat'),
140
293
  vision:
141
- (visionKeywords.some((keyword) => model.id.toLowerCase().includes(keyword)) &&
142
- !isExcludedModel) ||
143
- knownModel?.abilities?.vision ||
144
- false,
294
+ model.vision ??
295
+ knownModel?.abilities?.vision ??
296
+ ((isKeywordListMatch(model.id.toLowerCase(), visionKeywords) && !isExcludedModel) || false),
145
297
  };
146
298
  };
147
299
 
@@ -149,45 +301,91 @@ const processModelCard = (
149
301
  * 处理单一提供商的模型列表
150
302
  * @param modelList 模型列表
151
303
  * @param config 提供商配置
304
+ * @param provider 提供商类型(可选,用于优先匹配对应的本地配置)
152
305
  * @returns 处理后的模型卡片列表
153
306
  */
154
307
  export const processModelList = async (
155
308
  modelList: Array<{ id: string }>,
156
309
  config: ModelProcessorConfig,
310
+ provider?: keyof typeof MODEL_LIST_CONFIGS,
157
311
  ): Promise<ChatModelCard[]> => {
158
312
  const { LOBE_DEFAULT_MODEL_LIST } = await import('@/config/aiModels');
159
313
 
160
- return modelList
161
- .map((model) => {
162
- const knownModel = LOBE_DEFAULT_MODEL_LIST.find(
163
- (m) => model.id.toLowerCase() === m.id.toLowerCase(),
164
- );
314
+ return Promise.all(
315
+ modelList.map(async (model) => {
316
+ let knownModel: any = null;
317
+
318
+ // 如果提供了provider,优先使用提供商特定的配置
319
+ if (provider) {
320
+ knownModel = await findKnownModelByProvider(model.id, provider);
321
+ }
322
+
323
+ // 如果未找到,回退到全局配置
324
+ if (!knownModel) {
325
+ knownModel = LOBE_DEFAULT_MODEL_LIST.find(
326
+ (m) => model.id.toLowerCase() === m.id.toLowerCase(),
327
+ );
328
+ }
165
329
 
166
330
  return processModelCard(model, config, knownModel);
167
- })
168
- .filter(Boolean);
331
+ }),
332
+ ).then((results) => results.filter(Boolean));
169
333
  };
170
334
 
171
335
  /**
172
336
  * 处理混合提供商的模型列表
173
337
  * @param modelList 模型列表
338
+ * @param providerid 可选的提供商ID,用于获取其本地配置文件
174
339
  * @returns 处理后的模型卡片列表
175
340
  */
176
341
  export const processMultiProviderModelList = async (
177
342
  modelList: Array<{ id: string }>,
343
+ providerid?: ModelProviderKey,
178
344
  ): Promise<ChatModelCard[]> => {
179
345
  const { LOBE_DEFAULT_MODEL_LIST } = await import('@/config/aiModels');
180
346
 
181
- return modelList
182
- .map((model) => {
347
+ // 如果提供了 providerid,尝试获取该提供商的本地配置
348
+ let providerLocalConfig: any[] | null = null;
349
+ if (providerid) {
350
+ try {
351
+ const modulePath = `@/config/aiModels/${providerid}`;
352
+ const moduleImport = await import(modulePath);
353
+ providerLocalConfig = moduleImport.default;
354
+ } catch {
355
+ // 如果配置文件不存在或导入失败,保持为 null
356
+ providerLocalConfig = null;
357
+ }
358
+ }
359
+
360
+ return Promise.all(
361
+ modelList.map(async (model) => {
183
362
  const detectedProvider = detectModelProvider(model.id);
184
363
  const config = MODEL_LIST_CONFIGS[detectedProvider];
185
364
 
186
- const knownModel = LOBE_DEFAULT_MODEL_LIST.find(
187
- (m) => model.id.toLowerCase() === m.id.toLowerCase(),
188
- );
365
+ // 优先使用提供商特定的配置
366
+ let knownModel = await findKnownModelByProvider(model.id, detectedProvider);
189
367
 
190
- return processModelCard(model, config, knownModel);
191
- })
192
- .filter(Boolean);
368
+ // 如果未找到,回退到全局配置
369
+ if (!knownModel) {
370
+ knownModel = LOBE_DEFAULT_MODEL_LIST.find(
371
+ (m) => model.id.toLowerCase() === m.id.toLowerCase(),
372
+ );
373
+ }
374
+
375
+ // 如果提供了 providerid 且有本地配置,尝试从中获取模型的 enabled 状态
376
+ let providerLocalModelConfig = null;
377
+ if (providerLocalConfig && Array.isArray(providerLocalConfig)) {
378
+ providerLocalModelConfig = providerLocalConfig.find((m) => m.id === model.id);
379
+ }
380
+
381
+ const processedModel = processModelCard(model, config, knownModel);
382
+
383
+ // 如果找到了本地配置中的模型,使用其 enabled 状态
384
+ if (providerLocalModelConfig && typeof providerLocalModelConfig.enabled === 'boolean') {
385
+ processedModel.enabled = providerLocalModelConfig.enabled;
386
+ }
387
+
388
+ return processedModel;
389
+ }),
390
+ ).then((results) => results.filter(Boolean));
193
391
  };
@@ -1392,6 +1392,12 @@ describe('LobeOpenAICompatibleFactory', () => {
1392
1392
  maxOutput: 4096,
1393
1393
  pricing: {
1394
1394
  units: [
1395
+ {
1396
+ name: 'textInput_cacheRead',
1397
+ rate: 0.03,
1398
+ strategy: 'fixed',
1399
+ unit: 'millionTokens',
1400
+ },
1395
1401
  {
1396
1402
  name: 'textInput',
1397
1403
  rate: 0.25,
@@ -1404,6 +1410,18 @@ describe('LobeOpenAICompatibleFactory', () => {
1404
1410
  strategy: 'fixed',
1405
1411
  unit: 'millionTokens',
1406
1412
  },
1413
+ {
1414
+ lookup: {
1415
+ prices: {
1416
+ '1h': 0.5,
1417
+ '5m': 0.3,
1418
+ },
1419
+ pricingParams: ['ttl'],
1420
+ },
1421
+ name: 'textInput_cacheWrite',
1422
+ strategy: 'lookup',
1423
+ unit: 'millionTokens',
1424
+ },
1407
1425
  ],
1408
1426
  },
1409
1427
  providerId: 'anthropic',
@@ -188,6 +188,18 @@ const transformOpenAIStream = (
188
188
  let reasoning_content = (() => {
189
189
  if ('reasoning_content' in item.delta) return item.delta.reasoning_content;
190
190
  if ('reasoning' in item.delta) return item.delta.reasoning;
191
+ // Handle content array format with thinking blocks (e.g. mistral AI Magistral model)
192
+ if ('content' in item.delta && Array.isArray(item.delta.content)) {
193
+ return item.delta.content
194
+ .filter((block: any) => block.type === 'thinking' && Array.isArray(block.thinking))
195
+ .map((block: any) =>
196
+ block.thinking
197
+ .filter((thinkItem: any) => thinkItem.type === 'text' && thinkItem.text)
198
+ .map((thinkItem: any) => thinkItem.text)
199
+ .join('')
200
+ )
201
+ .join('');
202
+ }
191
203
  return null;
192
204
  })();
193
205
 
@@ -1,5 +1,5 @@
1
1
  import { ModelProvider } from '../types';
2
- import { processMultiProviderModelList } from '../utils/modelParse';
2
+ import { MODEL_LIST_CONFIGS, processModelList } from '../utils/modelParse';
3
3
  import { createOpenAICompatibleRuntime } from '../utils/openaiCompatibleFactory';
4
4
 
5
5
  export interface V0ModelCard {
@@ -15,7 +15,7 @@ export const LobeV0AI = createOpenAICompatibleRuntime({
15
15
  const modelsPage = (await client.models.list()) as any;
16
16
  const modelList: V0ModelCard[] = modelsPage.data;
17
17
 
18
- return processMultiProviderModelList(modelList);
18
+ return processModelList(modelList, MODEL_LIST_CONFIGS.v0, 'v0');
19
19
  },
20
20
  provider: ModelProvider.V0,
21
21
  });
@@ -37,7 +37,7 @@ export const LobeVolcengineAI = createOpenAICompatibleRuntime({
37
37
  const modelsPage = (await client.models.list()) as any;
38
38
  const modelList: VolcengineModelCard[] = modelsPage.data;
39
39
 
40
- return processModelList(modelList, MODEL_LIST_CONFIGS.volcengine);
40
+ return processModelList(modelList, MODEL_LIST_CONFIGS.volcengine, 'volcengine');
41
41
  },
42
42
  provider: ModelProvider.Volcengine,
43
43
  });
@@ -1,6 +1,5 @@
1
- import type { ChatModelCard } from '@/types/llm';
2
-
3
1
  import { ModelProvider } from '../types';
2
+ import { MODEL_LIST_CONFIGS, processModelList } from '../utils/modelParse';
4
3
  import { createOpenAICompatibleRuntime } from '../utils/openaiCompatibleFactory';
5
4
 
6
5
  export interface XAIModelCard {
@@ -55,31 +54,10 @@ export const LobeXAI = createOpenAICompatibleRuntime({
55
54
  chatCompletion: () => process.env.DEBUG_XAI_CHAT_COMPLETION === '1',
56
55
  },
57
56
  models: async ({ client }) => {
58
- const { LOBE_DEFAULT_MODEL_LIST } = await import('@/config/aiModels');
59
-
60
57
  const modelsPage = (await client.models.list()) as any;
61
58
  const modelList: XAIModelCard[] = modelsPage.data;
62
59
 
63
- return modelList
64
- .map((model) => {
65
- const knownModel = LOBE_DEFAULT_MODEL_LIST.find(
66
- (m) => model.id.toLowerCase() === m.id.toLowerCase(),
67
- );
68
-
69
- return {
70
- contextWindowTokens: knownModel?.contextWindowTokens ?? undefined,
71
- displayName: knownModel?.displayName ?? undefined,
72
- enabled: knownModel?.enabled || false,
73
- functionCall: knownModel?.abilities?.functionCall || false,
74
- id: model.id,
75
- reasoning: knownModel?.abilities?.reasoning || false,
76
- vision:
77
- model.id.toLowerCase().includes('vision') ||
78
- knownModel?.abilities?.functionCall ||
79
- false,
80
- };
81
- })
82
- .filter(Boolean) as ChatModelCard[];
60
+ return processModelList(modelList, MODEL_LIST_CONFIGS.xai, 'xai');
83
61
  },
84
62
  provider: ModelProvider.XAI,
85
63
  });
@@ -134,7 +134,7 @@ export const LobeZhipuAI = createOpenAICompatibleRuntime({
134
134
  displayName: model.modelName,
135
135
  id: model.modelCode,
136
136
  }));
137
- return processModelList(standardModelList, MODEL_LIST_CONFIGS.zhipu);
137
+ return processModelList(standardModelList, MODEL_LIST_CONFIGS.zhipu, 'zhipu');
138
138
  },
139
139
  provider: ModelProvider.ZhiPu,
140
140
  });
@@ -328,7 +328,6 @@ const aihubmixModels: AIChatModelCard[] = [
328
328
  {
329
329
  abilities: {
330
330
  functionCall: true,
331
- search: true,
332
331
  },
333
332
  contextWindowTokens: 131_072,
334
333
  description:
@@ -344,16 +343,12 @@ const aihubmixModels: AIChatModelCard[] = [
344
343
  ],
345
344
  },
346
345
  releasedAt: '2025-04-03',
347
- settings: {
348
- searchImpl: 'params',
349
- },
350
346
  type: 'chat',
351
347
  },
352
348
  {
353
349
  abilities: {
354
350
  functionCall: true,
355
351
  reasoning: true,
356
- search: true,
357
352
  },
358
353
  contextWindowTokens: 131_072,
359
354
  description:
@@ -370,7 +365,6 @@ const aihubmixModels: AIChatModelCard[] = [
370
365
  releasedAt: '2025-04-03',
371
366
  settings: {
372
367
  extendParams: ['reasoningEffort'],
373
- searchImpl: 'params',
374
368
  },
375
369
  type: 'chat',
376
370
  },
@@ -780,4 +774,4 @@ const aihubmixModels: AIChatModelCard[] = [
780
774
  },
781
775
  ];
782
776
 
783
- export default aihubmixModels;
777
+ export default aihubmixModels;
@@ -113,7 +113,6 @@ const anthropicChatModels: AIChatModelCard[] = [
113
113
  description:
114
114
  'Claude 3.7 Sonnet 是 Anthropic 迄今为止最智能的模型,也是市场上首个混合推理模型。Claude 3.7 Sonnet 可以产生近乎即时的响应或延长的逐步思考,用户可以清晰地看到这些过程。Sonnet 特别擅长编程、数据科学、视觉处理、代理任务。',
115
115
  displayName: 'Claude 3.7 Sonnet',
116
- enabled: true,
117
116
  id: 'claude-3-7-sonnet-20250219',
118
117
  maxOutput: 64_000,
119
118
  pricing: {
@@ -213,9 +212,9 @@ const anthropicChatModels: AIChatModelCard[] = [
213
212
  maxOutput: 8192,
214
213
  pricing: {
215
214
  units: [
216
- { name: 'textInput_cacheRead', rate: 0.1, strategy: 'fixed', unit: 'millionTokens' },
217
- { name: 'textInput', rate: 1, strategy: 'fixed', unit: 'millionTokens' },
218
- { name: 'textOutput', rate: 5, strategy: 'fixed', unit: 'millionTokens' },
215
+ { name: 'textInput_cacheRead', rate: 0.08, strategy: 'fixed', unit: 'millionTokens' },
216
+ { name: 'textInput', rate: 0.8, strategy: 'fixed', unit: 'millionTokens' },
217
+ { name: 'textOutput', rate: 4, strategy: 'fixed', unit: 'millionTokens' },
219
218
  {
220
219
  lookup: { prices: { '1h': 1.6, '5m': 1 }, pricingParams: ['ttl'] },
221
220
  name: 'textInput_cacheWrite',
@@ -243,8 +242,15 @@ const anthropicChatModels: AIChatModelCard[] = [
243
242
  maxOutput: 4096,
244
243
  pricing: {
245
244
  units: [
245
+ { name: 'textInput_cacheRead', rate: 0.03, strategy: 'fixed', unit: 'millionTokens' },
246
246
  { name: 'textInput', rate: 0.25, strategy: 'fixed', unit: 'millionTokens' },
247
247
  { name: 'textOutput', rate: 1.25, strategy: 'fixed', unit: 'millionTokens' },
248
+ {
249
+ lookup: { prices: { '1h': 0.5, '5m': 0.3 }, pricingParams: ['ttl'] },
250
+ name: 'textInput_cacheWrite',
251
+ strategy: 'lookup',
252
+ unit: 'millionTokens',
253
+ },
248
254
  ],
249
255
  },
250
256
  releasedAt: '2024-03-07',
@@ -266,8 +272,15 @@ const anthropicChatModels: AIChatModelCard[] = [
266
272
  maxOutput: 4096,
267
273
  pricing: {
268
274
  units: [
275
+ { name: 'textInput_cacheRead', rate: 0.3, strategy: 'fixed', unit: 'millionTokens' },
269
276
  { name: 'textInput', rate: 3, strategy: 'fixed', unit: 'millionTokens' },
270
277
  { name: 'textOutput', rate: 15, strategy: 'fixed', unit: 'millionTokens' },
278
+ {
279
+ lookup: { prices: { '1h': 6, '5m': 3.75 }, pricingParams: ['ttl'] },
280
+ name: 'textInput_cacheWrite',
281
+ strategy: 'lookup',
282
+ unit: 'millionTokens',
283
+ },
271
284
  ],
272
285
  },
273
286
  releasedAt: '2024-02-29',
@@ -286,8 +299,15 @@ const anthropicChatModels: AIChatModelCard[] = [
286
299
  maxOutput: 4096,
287
300
  pricing: {
288
301
  units: [
302
+ { name: 'textInput_cacheRead', rate: 1.5, strategy: 'fixed', unit: 'millionTokens' },
289
303
  { name: 'textInput', rate: 15, strategy: 'fixed', unit: 'millionTokens' },
290
304
  { name: 'textOutput', rate: 75, strategy: 'fixed', unit: 'millionTokens' },
305
+ {
306
+ lookup: { prices: { '1h': 30, '5m': 18.75 }, pricingParams: ['ttl'] },
307
+ name: 'textInput_cacheWrite',
308
+ strategy: 'lookup',
309
+ unit: 'millionTokens',
310
+ },
291
311
  ],
292
312
  },
293
313
  releasedAt: '2024-02-29',