@lobehub/chat 1.91.1 → 1.91.2

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.
@@ -1,8 +1,7 @@
1
- import type { ChatModelCard } from '@/types/llm';
2
-
3
1
  import { ModelProvider } from '../types';
4
2
  import { createOpenAICompatibleRuntime } from '../utils/openaiCompatibleFactory';
5
3
  import { pruneReasoningPayload } from '../utils/openaiHelpers';
4
+ import { processMultiProviderModelList } from '../utils/modelParse';
6
5
 
7
6
  export interface OpenAIModelCard {
8
7
  id: string;
@@ -45,45 +44,11 @@ export const LobeOpenAI = createOpenAICompatibleRuntime({
45
44
  chatCompletion: () => process.env.DEBUG_OPENAI_CHAT_COMPLETION === '1',
46
45
  },
47
46
  models: async ({ client }) => {
48
- const { LOBE_DEFAULT_MODEL_LIST } = await import('@/config/aiModels');
49
-
50
- const functionCallKeywords = ['4o', '4.1', 'o3', 'o4'];
51
-
52
- const visionKeywords = ['4o', '4.1', 'o4'];
53
-
54
- const reasoningKeywords = ['o1', 'o3', 'o4'];
55
-
56
47
  const modelsPage = (await client.models.list()) as any;
57
48
  const modelList: OpenAIModelCard[] = modelsPage.data;
58
49
 
59
- return modelList
60
- .map((model) => {
61
- const knownModel = LOBE_DEFAULT_MODEL_LIST.find(
62
- (m) => model.id.toLowerCase() === m.id.toLowerCase(),
63
- );
64
-
65
- return {
66
- contextWindowTokens: knownModel?.contextWindowTokens ?? undefined,
67
- displayName: knownModel?.displayName ?? undefined,
68
- enabled: knownModel?.enabled || false,
69
- functionCall:
70
- (functionCallKeywords.some((keyword) => model.id.toLowerCase().includes(keyword)) &&
71
- !model.id.toLowerCase().includes('audio')) ||
72
- knownModel?.abilities?.functionCall ||
73
- false,
74
- id: model.id,
75
- reasoning:
76
- reasoningKeywords.some((keyword) => model.id.toLowerCase().includes(keyword)) ||
77
- knownModel?.abilities?.reasoning ||
78
- false,
79
- vision:
80
- (visionKeywords.some((keyword) => model.id.toLowerCase().includes(keyword)) &&
81
- !model.id.toLowerCase().includes('audio')) ||
82
- knownModel?.abilities?.vision ||
83
- false,
84
- };
85
- })
86
- .filter(Boolean) as ChatModelCard[];
50
+ // 自动检测模型提供商并选择相应配置
51
+ return processMultiProviderModelList(modelList);
87
52
  },
88
53
  provider: ModelProvider.OpenAI,
89
54
  });
@@ -13,6 +13,7 @@ _These are free, rate-limited endpoints for [Reflection 70B](/models/mattshumer/
13
13
  "enabled": false,
14
14
  "functionCall": true,
15
15
  "id": "mattshumer/reflection-70b:free",
16
+ "maxOutput": undefined,
16
17
  "maxTokens": 4096,
17
18
  "pricing": {
18
19
  "input": 0,
@@ -38,6 +39,7 @@ _These are free, rate-limited endpoints for [Reflection 70B](/models/mattshumer/
38
39
  "enabled": false,
39
40
  "functionCall": false,
40
41
  "id": "mattshumer/reflection-70b:free",
42
+ "maxOutput": undefined,
41
43
  "maxTokens": 4096,
42
44
  "pricing": {
43
45
  "input": 0,
@@ -63,6 +65,7 @@ _These are free, rate-limited endpoints for [Reflection 70B](/models/mattshumer/
63
65
  "enabled": false,
64
66
  "functionCall": false,
65
67
  "id": "mattshumer/reflection-70b:free",
68
+ "maxOutput": undefined,
66
69
  "maxTokens": 4096,
67
70
  "pricing": {
68
71
  "input": 0,
@@ -1,6 +1,7 @@
1
1
  import type { ChatModelCard } from '@/types/llm';
2
2
 
3
3
  import { ModelProvider } from '../types';
4
+ import { processMultiProviderModelList } from '../utils/modelParse';
4
5
  import { createOpenAICompatibleRuntime } from '../utils/openaiCompatibleFactory';
5
6
  import { OpenRouterModelCard, OpenRouterModelExtraInfo, OpenRouterReasoning } from './type';
6
7
 
@@ -40,20 +41,9 @@ export const LobeOpenRouterAI = createOpenAICompatibleRuntime({
40
41
  chatCompletion: () => process.env.DEBUG_OPENROUTER_CHAT_COMPLETION === '1',
41
42
  },
42
43
  models: async ({ client }) => {
43
- const { LOBE_DEFAULT_MODEL_LIST } = await import('@/config/aiModels');
44
-
45
- const reasoningKeywords = [
46
- 'deepseek/deepseek-r1',
47
- 'openai/o1',
48
- 'openai/o3',
49
- 'qwen/qvq',
50
- 'qwen/qwq',
51
- 'thinking',
52
- ];
53
-
54
44
  const modelsPage = (await client.models.list()) as any;
55
45
  const modelList: OpenRouterModelCard[] = modelsPage.data;
56
-
46
+
57
47
  const modelsExtraInfo: OpenRouterModelExtraInfo[] = [];
58
48
  try {
59
49
  const response = await fetch('https://openrouter.ai/api/frontend/models');
@@ -62,50 +52,51 @@ export const LobeOpenRouterAI = createOpenAICompatibleRuntime({
62
52
  modelsExtraInfo.push(...data['data']);
63
53
  }
64
54
  } catch (error) {
65
- // 忽略 fetch 错误,使用空的 modelsExtraInfo 数组继续处理
66
55
  console.error('Failed to fetch OpenRouter frontend models:', error);
67
56
  }
68
-
69
- return modelList
70
- .map((model) => {
71
- const knownModel = LOBE_DEFAULT_MODEL_LIST.find(
72
- (m) => model.id.toLowerCase() === m.id.toLowerCase(),
73
- );
74
- const extraInfo = modelsExtraInfo.find(
75
- (m) => m.slug.toLowerCase() === model.id.toLowerCase(),
76
- );
77
-
78
- return {
79
- contextWindowTokens: model.context_length,
80
- description: model.description,
81
- displayName: model.name,
82
- enabled: knownModel?.enabled || false,
83
- functionCall:
84
- model.description.includes('function calling') ||
85
- model.description.includes('tools') ||
86
- extraInfo?.endpoint?.supports_tool_parameters ||
87
- knownModel?.abilities?.functionCall ||
88
- false,
89
- id: model.id,
90
- maxTokens:
91
- typeof model.top_provider.max_completion_tokens === 'number'
92
- ? model.top_provider.max_completion_tokens
93
- : undefined,
94
- pricing: {
95
- input: formatPrice(model.pricing.prompt),
96
- output: formatPrice(model.pricing.completion),
97
- },
98
- reasoning:
99
- reasoningKeywords.some((keyword) => model.id.toLowerCase().includes(keyword)) ||
100
- extraInfo?.endpoint?.supports_reasoning ||
101
- knownModel?.abilities?.reasoning ||
102
- false,
103
- releasedAt: new Date(model.created * 1000).toISOString().split('T')[0],
104
- vision:
105
- model.architecture.modality.includes('image') || knownModel?.abilities?.vision || false,
106
- };
107
- })
108
- .filter(Boolean) as ChatModelCard[];
57
+
58
+ // 解析模型能力
59
+ const baseModels = await processMultiProviderModelList(modelList);
60
+
61
+ // 合并 OpenRouter 获取的模型信息
62
+ return baseModels.map((baseModel) => {
63
+ const model = modelList.find(m => m.id === baseModel.id);
64
+ const extraInfo = modelsExtraInfo.find(
65
+ (m) => m.slug.toLowerCase() === baseModel.id.toLowerCase(),
66
+ );
67
+
68
+ if (!model) return baseModel;
69
+
70
+ return {
71
+ ...baseModel,
72
+ contextWindowTokens: model.context_length,
73
+ description: model.description,
74
+ displayName: model.name,
75
+ functionCall:
76
+ baseModel.functionCall ||
77
+ model.description.includes('function calling') ||
78
+ model.description.includes('tools') ||
79
+ extraInfo?.endpoint?.supports_tool_parameters ||
80
+ false,
81
+ maxTokens:
82
+ typeof model.top_provider.max_completion_tokens === 'number'
83
+ ? model.top_provider.max_completion_tokens
84
+ : undefined,
85
+ pricing: {
86
+ input: formatPrice(model.pricing.prompt),
87
+ output: formatPrice(model.pricing.completion),
88
+ },
89
+ reasoning:
90
+ baseModel.reasoning ||
91
+ extraInfo?.endpoint?.supports_reasoning ||
92
+ false,
93
+ releasedAt: new Date(model.created * 1000).toISOString().split('T')[0],
94
+ vision:
95
+ baseModel.vision ||
96
+ model.architecture.modality.includes('image') ||
97
+ false,
98
+ };
99
+ }).filter(Boolean) as ChatModelCard[];
109
100
  },
110
101
  provider: ModelProvider.OpenRouter,
111
102
  });
@@ -1,6 +1,5 @@
1
- import type { ChatModelCard } from '@/types/llm';
2
-
3
1
  import { ModelProvider } from '../types';
2
+ import { processMultiProviderModelList } from '../utils/modelParse';
4
3
  import { createOpenAICompatibleRuntime } from '../utils/openaiCompatibleFactory';
5
4
  import { QwenAIStream } from '../utils/streams';
6
5
 
@@ -78,52 +77,10 @@ export const LobeQwenAI = createOpenAICompatibleRuntime({
78
77
  chatCompletion: () => process.env.DEBUG_QWEN_CHAT_COMPLETION === '1',
79
78
  },
80
79
  models: async ({ client }) => {
81
- const { LOBE_DEFAULT_MODEL_LIST } = await import('@/config/aiModels');
82
-
83
- const functionCallKeywords = [
84
- 'qwen-max',
85
- 'qwen-plus',
86
- 'qwen-turbo',
87
- 'qwen-long',
88
- 'qwen1.5',
89
- 'qwen2',
90
- 'qwen2.5',
91
- 'qwen3',
92
- ];
93
-
94
- const visionKeywords = ['qvq', 'vl'];
95
-
96
- const reasoningKeywords = ['qvq', 'qwq', 'deepseek-r1', 'qwen3'];
97
-
98
80
  const modelsPage = (await client.models.list()) as any;
99
81
  const modelList: QwenModelCard[] = modelsPage.data;
100
82
 
101
- return modelList
102
- .map((model) => {
103
- const knownModel = LOBE_DEFAULT_MODEL_LIST.find(
104
- (m) => model.id.toLowerCase() === m.id.toLowerCase(),
105
- );
106
-
107
- return {
108
- contextWindowTokens: knownModel?.contextWindowTokens ?? undefined,
109
- displayName: knownModel?.displayName ?? undefined,
110
- enabled: knownModel?.enabled || false,
111
- functionCall:
112
- functionCallKeywords.some((keyword) => model.id.toLowerCase().includes(keyword)) ||
113
- knownModel?.abilities?.functionCall ||
114
- false,
115
- id: model.id,
116
- reasoning:
117
- reasoningKeywords.some((keyword) => model.id.toLowerCase().includes(keyword)) ||
118
- knownModel?.abilities?.reasoning ||
119
- false,
120
- vision:
121
- visionKeywords.some((keyword) => model.id.toLowerCase().includes(keyword)) ||
122
- knownModel?.abilities?.vision ||
123
- false,
124
- };
125
- })
126
- .filter(Boolean) as ChatModelCard[];
83
+ return processMultiProviderModelList(modelList);
127
84
  },
128
85
  provider: ModelProvider.Qwen,
129
86
  });
@@ -1,7 +1,6 @@
1
- import type { ChatModelCard } from '@/types/llm';
2
-
3
1
  import { AgentRuntimeErrorType } from '../error';
4
2
  import { ChatCompletionErrorPayload, ModelProvider } from '../types';
3
+ import { processMultiProviderModelList } from '../utils/modelParse';
5
4
  import { createOpenAICompatibleRuntime } from '../utils/openaiCompatibleFactory';
6
5
 
7
6
  export interface SiliconCloudModelCard {
@@ -69,58 +68,10 @@ export const LobeSiliconCloudAI = createOpenAICompatibleRuntime({
69
68
  invalidAPIKey: AgentRuntimeErrorType.InvalidProviderAPIKey,
70
69
  },
71
70
  models: async ({ client }) => {
72
- const { LOBE_DEFAULT_MODEL_LIST } = await import('@/config/aiModels');
73
-
74
- const functionCallKeywords = [
75
- 'qwen/qwen3',
76
- 'qwen/qwen2.5',
77
- 'thudm/glm-4',
78
- 'deepseek-ai/deepseek',
79
- 'internlm/internlm2_5',
80
- 'meta-llama/meta-llama-3.1',
81
- 'meta-llama/meta-llama-3.3',
82
- ];
83
-
84
- const visionKeywords = [
85
- 'opengvlab/internvl',
86
- 'qwen/qvq',
87
- 'qwen/qwen2-vl',
88
- 'teleai/telemm',
89
- 'deepseek-ai/deepseek-vl',
90
- ];
91
-
92
- const reasoningKeywords = ['deepseek-ai/deepseek-r1', 'qwen/qvq', 'qwen/qwq', 'qwen/qwen3'];
93
-
94
71
  const modelsPage = (await client.models.list()) as any;
95
72
  const modelList: SiliconCloudModelCard[] = modelsPage.data;
96
73
 
97
- return modelList
98
- .map((model) => {
99
- const knownModel = LOBE_DEFAULT_MODEL_LIST.find(
100
- (m) => model.id.toLowerCase() === m.id.toLowerCase(),
101
- );
102
-
103
- return {
104
- contextWindowTokens: knownModel?.contextWindowTokens ?? undefined,
105
- displayName: knownModel?.displayName ?? undefined,
106
- enabled: knownModel?.enabled || false,
107
- functionCall:
108
- (functionCallKeywords.some((keyword) => model.id.toLowerCase().includes(keyword)) &&
109
- !model.id.toLowerCase().includes('deepseek-r1')) ||
110
- knownModel?.abilities?.functionCall ||
111
- false,
112
- id: model.id,
113
- reasoning:
114
- reasoningKeywords.some((keyword) => model.id.toLowerCase().includes(keyword)) ||
115
- knownModel?.abilities?.reasoning ||
116
- false,
117
- vision:
118
- visionKeywords.some((keyword) => model.id.toLowerCase().includes(keyword)) ||
119
- knownModel?.abilities?.vision ||
120
- false,
121
- };
122
- })
123
- .filter(Boolean) as ChatModelCard[];
74
+ return processMultiProviderModelList(modelList);
124
75
  },
125
76
  provider: ModelProvider.SiliconCloud,
126
77
  });