@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.
- package/CHANGELOG.md +25 -0
- package/changelog/v1.json +9 -0
- package/package.json +2 -2
- package/src/app/(backend)/middleware/auth/utils.ts +2 -1
- package/src/libs/model-runtime/google/index.ts +30 -40
- package/src/libs/model-runtime/novita/__snapshots__/index.test.ts.snap +19 -1
- package/src/libs/model-runtime/novita/index.ts +14 -15
- package/src/libs/model-runtime/nvidia/index.ts +2 -21
- package/src/libs/model-runtime/openai/__snapshots__/index.test.ts.snap +39 -11
- package/src/libs/model-runtime/openai/index.ts +3 -38
- package/src/libs/model-runtime/openrouter/__snapshots__/index.test.ts.snap +3 -0
- package/src/libs/model-runtime/openrouter/index.ts +45 -54
- package/src/libs/model-runtime/qwen/index.ts +2 -45
- package/src/libs/model-runtime/siliconcloud/index.ts +2 -51
- package/src/libs/model-runtime/utils/modelParse.test.ts +761 -0
- package/src/libs/model-runtime/utils/modelParse.ts +186 -0
- package/src/libs/model-runtime/volcengine/index.ts +11 -0
- package/src/libs/model-runtime/zeroone/index.ts +2 -23
- package/src/libs/model-runtime/zhipu/index.ts +7 -34
@@ -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
|
-
|
60
|
-
|
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
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
)
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
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
|
});
|