@lobehub/chat 1.19.35 → 1.20.0
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 +50 -0
- package/Dockerfile +2 -0
- package/Dockerfile.database +2 -0
- package/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/package.json +1 -1
- package/src/app/(main)/settings/llm/ProviderList/providers.tsx +2 -0
- package/src/app/api/chat/agentRuntime.ts +7 -0
- package/src/config/llm.ts +8 -0
- package/src/config/modelProviders/hunyuan.ts +137 -0
- package/src/config/modelProviders/index.ts +4 -0
- package/src/config/modelProviders/openrouter.ts +36 -13
- package/src/const/settings/llm.ts +5 -0
- package/src/libs/agent-runtime/AgentRuntime.ts +7 -0
- package/src/libs/agent-runtime/hunyuan/index.test.ts +255 -0
- package/src/libs/agent-runtime/hunyuan/index.ts +10 -0
- package/src/libs/agent-runtime/openrouter/__snapshots__/index.test.ts.snap +4 -4
- package/src/libs/agent-runtime/types/type.ts +1 -0
- package/src/server/globalConfig/index.ts +12 -0
- package/src/types/user/settings/keyVaults.ts +1 -0
package/CHANGELOG.md
CHANGED
@@ -2,6 +2,56 @@
|
|
2
2
|
|
3
3
|
# Changelog
|
4
4
|
|
5
|
+
## [Version 1.20.0](https://github.com/lobehub/lobe-chat/compare/v1.19.36...v1.20.0)
|
6
|
+
|
7
|
+
<sup>Released on **2024-09-27**</sup>
|
8
|
+
|
9
|
+
#### ✨ Features
|
10
|
+
|
11
|
+
- **misc**: Add Hunyuan(Tencent) model provider.
|
12
|
+
|
13
|
+
<br/>
|
14
|
+
|
15
|
+
<details>
|
16
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
17
|
+
|
18
|
+
#### What's improved
|
19
|
+
|
20
|
+
- **misc**: Add Hunyuan(Tencent) model provider, closes [#4147](https://github.com/lobehub/lobe-chat/issues/4147) ([8ddb41b](https://github.com/lobehub/lobe-chat/commit/8ddb41b))
|
21
|
+
|
22
|
+
</details>
|
23
|
+
|
24
|
+
<div align="right">
|
25
|
+
|
26
|
+
[](#readme-top)
|
27
|
+
|
28
|
+
</div>
|
29
|
+
|
30
|
+
### [Version 1.19.36](https://github.com/lobehub/lobe-chat/compare/v1.19.35...v1.19.36)
|
31
|
+
|
32
|
+
<sup>Released on **2024-09-27**</sup>
|
33
|
+
|
34
|
+
#### 💄 Styles
|
35
|
+
|
36
|
+
- **misc**: Add llama3.2 model for openrouter provider.
|
37
|
+
|
38
|
+
<br/>
|
39
|
+
|
40
|
+
<details>
|
41
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
42
|
+
|
43
|
+
#### Styles
|
44
|
+
|
45
|
+
- **misc**: Add llama3.2 model for openrouter provider, closes [#4151](https://github.com/lobehub/lobe-chat/issues/4151) ([6f1a966](https://github.com/lobehub/lobe-chat/commit/6f1a966))
|
46
|
+
|
47
|
+
</details>
|
48
|
+
|
49
|
+
<div align="right">
|
50
|
+
|
51
|
+
[](#readme-top)
|
52
|
+
|
53
|
+
</div>
|
54
|
+
|
5
55
|
### [Version 1.19.35](https://github.com/lobehub/lobe-chat/compare/v1.19.34...v1.19.35)
|
6
56
|
|
7
57
|
<sup>Released on **2024-09-27**</sup>
|
package/Dockerfile
CHANGED
package/Dockerfile.database
CHANGED
package/README.md
CHANGED
@@ -253,7 +253,7 @@ In addition, these plugins are not limited to news aggregation, but can also ext
|
|
253
253
|
| [Savvy Trader AI](https://chat-preview.lobehub.com/settings/agent)<br/><sup>By **savvytrader** on **2024-06-27**</sup> | Realtime stock, crypto and other investment data.<br/>`stock` `analyze` |
|
254
254
|
| [Search1API](https://chat-preview.lobehub.com/settings/agent)<br/><sup>By **fatwang2** on **2024-05-06**</sup> | Search aggregation service, specifically designed for LLMs<br/>`web` `search` |
|
255
255
|
|
256
|
-
> 📊 Total plugins: [<kbd>**
|
256
|
+
> 📊 Total plugins: [<kbd>**49**</kbd>](https://github.com/lobehub/lobe-chat-plugins)
|
257
257
|
|
258
258
|
<!-- PLUGIN LIST -->
|
259
259
|
|
package/README.zh-CN.md
CHANGED
@@ -245,7 +245,7 @@ LobeChat 的插件生态系统是其核心功能的重要扩展,它极大地
|
|
245
245
|
| [Savvy Trader AI](https://chat-preview.lobehub.com/settings/agent)<br/><sup>By **savvytrader** on **2024-06-27**</sup> | 实时股票、加密货币和其他投资数据。<br/>`股票` `分析` |
|
246
246
|
| [Search1API](https://chat-preview.lobehub.com/settings/agent)<br/><sup>By **fatwang2** on **2024-05-06**</sup> | 搜索聚合服务,专为 LLMs 设计<br/>`web` `search` |
|
247
247
|
|
248
|
-
> 📊 Total plugins: [<kbd>**
|
248
|
+
> 📊 Total plugins: [<kbd>**49**</kbd>](https://github.com/lobehub/lobe-chat-plugins)
|
249
249
|
|
250
250
|
<!-- PLUGIN LIST -->
|
251
251
|
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@lobehub/chat",
|
3
|
-
"version": "1.
|
3
|
+
"version": "1.20.0",
|
4
4
|
"description": "Lobe Chat - an open-source, high-performance chatbot framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.",
|
5
5
|
"keywords": [
|
6
6
|
"framework",
|
@@ -9,6 +9,7 @@ import {
|
|
9
9
|
FireworksAIProviderCard,
|
10
10
|
GoogleProviderCard,
|
11
11
|
GroqProviderCard,
|
12
|
+
HunyuanProviderCard,
|
12
13
|
MinimaxProviderCard,
|
13
14
|
MistralProviderCard,
|
14
15
|
MoonshotProviderCard,
|
@@ -60,6 +61,7 @@ export const useProviderList = (): ProviderItem[] => {
|
|
60
61
|
Ai21ProviderCard,
|
61
62
|
UpstageProviderCard,
|
62
63
|
QwenProviderCard,
|
64
|
+
HunyuanProviderCard,
|
63
65
|
SparkProviderCard,
|
64
66
|
ZhiPuProviderCard,
|
65
67
|
ZeroOneProviderCard,
|
@@ -244,6 +244,13 @@ const getLlmOptionsFromPayload = (provider: string, payload: JWTPayload) => {
|
|
244
244
|
|
245
245
|
const apiKey = apiKeyManager.pick(payload?.apiKey || AI21_API_KEY);
|
246
246
|
|
247
|
+
return { apiKey };
|
248
|
+
}
|
249
|
+
case ModelProvider.Hunyuan: {
|
250
|
+
const { HUNYUAN_API_KEY } = getLLMConfig();
|
251
|
+
|
252
|
+
const apiKey = apiKeyManager.pick(payload?.apiKey || HUNYUAN_API_KEY);
|
253
|
+
|
247
254
|
return { apiKey };
|
248
255
|
}
|
249
256
|
}
|
package/src/config/llm.ts
CHANGED
@@ -117,6 +117,10 @@ export const getLLMConfig = () => {
|
|
117
117
|
|
118
118
|
ENABLED_AI21: z.boolean(),
|
119
119
|
AI21_API_KEY: z.string().optional(),
|
120
|
+
|
121
|
+
ENABLED_HUNYUAN: z.boolean(),
|
122
|
+
HUNYUAN_API_KEY: z.string().optional(),
|
123
|
+
HUNYUAN_MODEL_LIST: z.string().optional(),
|
120
124
|
},
|
121
125
|
runtimeEnv: {
|
122
126
|
API_KEY_SELECT_MODE: process.env.API_KEY_SELECT_MODE,
|
@@ -231,6 +235,10 @@ export const getLLMConfig = () => {
|
|
231
235
|
|
232
236
|
ENABLED_AI21: !!process.env.AI21_API_KEY,
|
233
237
|
AI21_API_KEY: process.env.AI21_API_KEY,
|
238
|
+
|
239
|
+
ENABLED_HUNYUAN: !!process.env.HUNYUAN_API_KEY,
|
240
|
+
HUNYUAN_API_KEY: process.env.HUNYUAN_API_KEY,
|
241
|
+
HUNYUAN_MODEL_LIST: process.env.HUNYUAN_MODEL_LIST,
|
234
242
|
},
|
235
243
|
});
|
236
244
|
};
|
@@ -0,0 +1,137 @@
|
|
1
|
+
import { ModelProviderCard } from '@/types/llm';
|
2
|
+
|
3
|
+
// ref https://cloud.tencent.com/document/product/1729/104753
|
4
|
+
const Hunyuan: ModelProviderCard = {
|
5
|
+
chatModels: [
|
6
|
+
{
|
7
|
+
description: '升级为 MOE 结构,上下文窗口为 256k ,在 NLP,代码,数学,行业等多项评测集上领先众多开源模型。',
|
8
|
+
displayName: 'Hunyuan Lite',
|
9
|
+
enabled: true,
|
10
|
+
id: 'hunyuan-lite',
|
11
|
+
maxOutput: 6000,
|
12
|
+
pricing: {
|
13
|
+
currency: 'CNY',
|
14
|
+
input: 0,
|
15
|
+
output: 0,
|
16
|
+
},
|
17
|
+
tokens: 256_000,
|
18
|
+
},
|
19
|
+
{
|
20
|
+
description: '采用更优的路由策略,同时缓解了负载均衡和专家趋同的问题。长文方面,大海捞针指标达到99.9%。MOE-32K 性价比相对更高,在平衡效果、价格的同时,可对实现对长文本输入的处理。',
|
21
|
+
displayName: 'Hunyuan Standard',
|
22
|
+
enabled: true,
|
23
|
+
id: 'hunyuan-standard',
|
24
|
+
maxOutput: 2000,
|
25
|
+
pricing: {
|
26
|
+
currency: 'CNY',
|
27
|
+
input: 4.5,
|
28
|
+
output: 5,
|
29
|
+
},
|
30
|
+
tokens: 32_000,
|
31
|
+
},
|
32
|
+
{
|
33
|
+
description: '采用更优的路由策略,同时缓解了负载均衡和专家趋同的问题。长文方面,大海捞针指标达到99.9%。MOE-256K 在长度和效果上进一步突破,极大的扩展了可输入长度。',
|
34
|
+
displayName: 'Hunyuan Standard 256K',
|
35
|
+
enabled: true,
|
36
|
+
id: 'hunyuan-standard-256K',
|
37
|
+
maxOutput: 6000,
|
38
|
+
pricing: {
|
39
|
+
currency: 'CNY',
|
40
|
+
input: 15,
|
41
|
+
output: 60,
|
42
|
+
},
|
43
|
+
tokens: 256_000,
|
44
|
+
},
|
45
|
+
{
|
46
|
+
description: '混元全新一代大语言模型的预览版,采用全新的混合专家模型(MoE)结构,相比hunyuan-pro推理效率更快,效果表现更强。',
|
47
|
+
displayName: 'Hunyuan Turbo',
|
48
|
+
enabled: true,
|
49
|
+
functionCall: true,
|
50
|
+
id: 'hunyuan-turbo',
|
51
|
+
maxOutput: 4000,
|
52
|
+
pricing: {
|
53
|
+
currency: 'CNY',
|
54
|
+
input: 15,
|
55
|
+
output: 50,
|
56
|
+
},
|
57
|
+
tokens: 32_000,
|
58
|
+
},
|
59
|
+
{
|
60
|
+
description: '万亿级参数规模 MOE-32K 长文模型。在各种 benchmark 上达到绝对领先的水平,复杂指令和推理,具备复杂数学能力,支持 functioncall,在多语言翻译、金融法律医疗等领域应用重点优化。',
|
61
|
+
displayName: 'Hunyuan Pro',
|
62
|
+
enabled: true,
|
63
|
+
functionCall: true,
|
64
|
+
id: 'hunyuan-pro',
|
65
|
+
maxOutput: 4000,
|
66
|
+
pricing: {
|
67
|
+
currency: 'CNY',
|
68
|
+
input: 30,
|
69
|
+
output: 100,
|
70
|
+
},
|
71
|
+
tokens: 32_000,
|
72
|
+
},
|
73
|
+
{
|
74
|
+
description: '混元最新代码生成模型,经过 200B 高质量代码数据增训基座模型,迭代半年高质量 SFT 数据训练,上下文长窗口长度增大到 8K,五大语言代码生成自动评测指标上位居前列;五大语言10项考量各方面综合代码任务人工高质量评测上,性能处于第一梯队',
|
75
|
+
displayName: 'Hunyuan Code',
|
76
|
+
enabled: true,
|
77
|
+
id: 'hunyuan-code',
|
78
|
+
maxOutput: 4000,
|
79
|
+
pricing: {
|
80
|
+
currency: 'CNY',
|
81
|
+
input: 4,
|
82
|
+
output: 8,
|
83
|
+
},
|
84
|
+
tokens: 8000,
|
85
|
+
},
|
86
|
+
{
|
87
|
+
description: '混元最新多模态模型,支持图片+文本输入生成文本内容。',
|
88
|
+
displayName: 'Hunyuan Vision',
|
89
|
+
enabled: true,
|
90
|
+
id: 'hunyuan-vision',
|
91
|
+
maxOutput: 4000,
|
92
|
+
pricing: {
|
93
|
+
currency: 'CNY',
|
94
|
+
input: 18,
|
95
|
+
output: 18,
|
96
|
+
},
|
97
|
+
tokens: 8000,
|
98
|
+
vision: true,
|
99
|
+
},
|
100
|
+
{
|
101
|
+
description: '混元最新 MOE 架构 FunctionCall 模型,经过高质量的 FunctionCall 数据训练,上下文窗口达 32K,在多个维度的评测指标上处于领先。',
|
102
|
+
displayName: 'Hunyuan FunctionCall',
|
103
|
+
functionCall: true,
|
104
|
+
id: 'hunyuan-functioncall',
|
105
|
+
maxOutput: 4000,
|
106
|
+
pricing: {
|
107
|
+
currency: 'CNY',
|
108
|
+
input: 4,
|
109
|
+
output: 8,
|
110
|
+
},
|
111
|
+
tokens: 32_000,
|
112
|
+
},
|
113
|
+
{
|
114
|
+
description: '混元最新版角色扮演模型,混元官方精调训练推出的角色扮演模型,基于混元模型结合角色扮演场景数据集进行增训,在角色扮演场景具有更好的基础效果。',
|
115
|
+
displayName: 'Hunyuan Role',
|
116
|
+
id: 'hunyuan-role',
|
117
|
+
maxOutput: 4000,
|
118
|
+
pricing: {
|
119
|
+
currency: 'CNY',
|
120
|
+
input: 4,
|
121
|
+
output: 8,
|
122
|
+
},
|
123
|
+
tokens: 8000,
|
124
|
+
},
|
125
|
+
],
|
126
|
+
checkModel: 'hunyuan-lite',
|
127
|
+
description:
|
128
|
+
'由腾讯研发的大语言模型,具备强大的中文创作能力,复杂语境下的逻辑推理能力,以及可靠的任务执行能力',
|
129
|
+
disableBrowserRequest: true,
|
130
|
+
id: 'hunyuan',
|
131
|
+
modelList: { showModelFetcher: true },
|
132
|
+
modelsUrl: 'https://cloud.tencent.com/document/product/1729/104753',
|
133
|
+
name: 'Hunyuan',
|
134
|
+
url: 'https://hunyuan.tencent.com',
|
135
|
+
};
|
136
|
+
|
137
|
+
export default Hunyuan;
|
@@ -11,6 +11,7 @@ import FireworksAIProvider from './fireworksai';
|
|
11
11
|
import GithubProvider from './github';
|
12
12
|
import GoogleProvider from './google';
|
13
13
|
import GroqProvider from './groq';
|
14
|
+
import HunyuanProvider from './hunyuan';
|
14
15
|
import MinimaxProvider from './minimax';
|
15
16
|
import MistralProvider from './mistral';
|
16
17
|
import MoonshotProvider from './moonshot';
|
@@ -57,6 +58,7 @@ export const LOBE_DEFAULT_MODEL_LIST: ChatModelCard[] = [
|
|
57
58
|
UpstageProvider.chatModels,
|
58
59
|
SparkProvider.chatModels,
|
59
60
|
Ai21Provider.chatModels,
|
61
|
+
HunyuanProvider.chatModels,
|
60
62
|
].flat();
|
61
63
|
|
62
64
|
export const DEFAULT_MODEL_PROVIDER_LIST = [
|
@@ -78,6 +80,7 @@ export const DEFAULT_MODEL_PROVIDER_LIST = [
|
|
78
80
|
Ai21Provider,
|
79
81
|
UpstageProvider,
|
80
82
|
QwenProvider,
|
83
|
+
HunyuanProvider,
|
81
84
|
SparkProvider,
|
82
85
|
ZhiPuProvider,
|
83
86
|
ZeroOneProvider,
|
@@ -110,6 +113,7 @@ export { default as FireworksAIProviderCard } from './fireworksai';
|
|
110
113
|
export { default as GithubProviderCard } from './github';
|
111
114
|
export { default as GoogleProviderCard } from './google';
|
112
115
|
export { default as GroqProviderCard } from './groq';
|
116
|
+
export { default as HunyuanProviderCard } from './hunyuan';
|
113
117
|
export { default as MinimaxProviderCard } from './minimax';
|
114
118
|
export { default as MistralProviderCard } from './mistral';
|
115
119
|
export { default as MoonshotProviderCard } from './moonshot';
|
@@ -79,8 +79,10 @@ const OpenRouter: ModelProviderCard = {
|
|
79
79
|
id: 'anthropic/claude-3-haiku',
|
80
80
|
maxOutput: 4096,
|
81
81
|
pricing: {
|
82
|
+
cachedInput: 0.025,
|
82
83
|
input: 0.25,
|
83
84
|
output: 1.25,
|
85
|
+
writeCacheInput: 0.3125,
|
84
86
|
},
|
85
87
|
releasedAt: '2024-03-07',
|
86
88
|
tokens: 200_000,
|
@@ -113,43 +115,41 @@ const OpenRouter: ModelProviderCard = {
|
|
113
115
|
id: 'anthropic/claude-3-opus',
|
114
116
|
maxOutput: 4096,
|
115
117
|
pricing: {
|
118
|
+
cachedInput: 1.5,
|
116
119
|
input: 15,
|
117
120
|
output: 75,
|
121
|
+
writeCacheInput: 18.75,
|
118
122
|
},
|
119
123
|
releasedAt: '2024-02-29',
|
120
124
|
tokens: 200_000,
|
121
125
|
vision: true,
|
122
126
|
},
|
123
127
|
{
|
124
|
-
description: 'Gemini 1.5 Flash
|
125
|
-
displayName: 'Gemini 1.5 Flash
|
128
|
+
description: 'Gemini 1.5 Flash 提供了优化后的多模态处理能力,适用多种复杂任务场景。',
|
129
|
+
displayName: 'Gemini 1.5 Flash',
|
126
130
|
enabled: true,
|
127
131
|
functionCall: true,
|
128
|
-
id: 'google/gemini-flash-1.5
|
132
|
+
id: 'google/gemini-flash-1.5',
|
129
133
|
maxOutput: 8192,
|
130
134
|
pricing: {
|
131
|
-
cachedInput: 0.018_75,
|
132
135
|
input: 0.075,
|
133
136
|
output: 0.3,
|
134
137
|
},
|
135
|
-
|
136
|
-
tokens: 1_048_576 + 8192,
|
138
|
+
tokens: 1_000_000 + 8192,
|
137
139
|
vision: true,
|
138
140
|
},
|
139
141
|
{
|
140
|
-
description: 'Gemini 1.5 Pro
|
141
|
-
displayName: 'Gemini 1.5 Pro
|
142
|
+
description: 'Gemini 1.5 Pro 结合最新优化技术,带来更高效的多模态数据处理能力。',
|
143
|
+
displayName: 'Gemini 1.5 Pro',
|
142
144
|
enabled: true,
|
143
145
|
functionCall: true,
|
144
|
-
id: 'google/gemini-pro-1.5
|
146
|
+
id: 'google/gemini-pro-1.5',
|
145
147
|
maxOutput: 8192,
|
146
148
|
pricing: {
|
147
|
-
cachedInput: 0.875,
|
148
149
|
input: 3.5,
|
149
150
|
output: 10.5,
|
150
151
|
},
|
151
|
-
|
152
|
-
tokens: 2_097_152 + 8192,
|
152
|
+
tokens: 2_000_000 + 8192,
|
153
153
|
vision: true,
|
154
154
|
},
|
155
155
|
{
|
@@ -160,13 +160,36 @@ const OpenRouter: ModelProviderCard = {
|
|
160
160
|
functionCall: true,
|
161
161
|
id: 'deepseek/deepseek-chat',
|
162
162
|
pricing: {
|
163
|
-
cachedInput: 0.014,
|
164
163
|
input: 0.14,
|
165
164
|
output: 0.28,
|
166
165
|
},
|
167
166
|
releasedAt: '2024-09-05',
|
168
167
|
tokens: 128_000,
|
169
168
|
},
|
169
|
+
{
|
170
|
+
description: 'LLaMA 3.2 旨在处理结合视觉和文本数据的任务。它在图像描述和视觉问答等任务中表现出色,跨越了语言生成和视觉推理之间的鸿沟。',
|
171
|
+
displayName: 'Llama 3.2 11B Vision',
|
172
|
+
enabled: true,
|
173
|
+
id: 'meta-llama/llama-3.2-11b-vision-instruct',
|
174
|
+
pricing: {
|
175
|
+
input: 0.162,
|
176
|
+
output: 0.162,
|
177
|
+
},
|
178
|
+
tokens: 131_072,
|
179
|
+
vision: true,
|
180
|
+
},
|
181
|
+
{
|
182
|
+
description: 'LLaMA 3.2 旨在处理结合视觉和文本数据的任务。它在图像描述和视觉问答等任务中表现出色,跨越了语言生成和视觉推理之间的鸿沟。',
|
183
|
+
displayName: 'Llama 3.2 90B Vision',
|
184
|
+
enabled: true,
|
185
|
+
id: 'meta-llama/llama-3.2-90b-vision-instruct',
|
186
|
+
pricing: {
|
187
|
+
input: 0.4,
|
188
|
+
output: 0.4,
|
189
|
+
},
|
190
|
+
tokens: 131_072,
|
191
|
+
vision: true,
|
192
|
+
},
|
170
193
|
{
|
171
194
|
description: 'Qwen2 是全新的大型语言模型系列,具有更强的理解和生成能力。',
|
172
195
|
displayName: 'Qwen2 7B (Free)',
|
@@ -9,6 +9,7 @@ import {
|
|
9
9
|
GithubProviderCard,
|
10
10
|
GoogleProviderCard,
|
11
11
|
GroqProviderCard,
|
12
|
+
HunyuanProviderCard,
|
12
13
|
MinimaxProviderCard,
|
13
14
|
MistralProviderCard,
|
14
15
|
MoonshotProviderCard,
|
@@ -75,6 +76,10 @@ export const DEFAULT_LLM_CONFIG: UserModelProviderConfig = {
|
|
75
76
|
enabled: false,
|
76
77
|
enabledModels: filterEnabledModels(GroqProviderCard),
|
77
78
|
},
|
79
|
+
hunyuan: {
|
80
|
+
enabled: false,
|
81
|
+
enabledModels: filterEnabledModels(HunyuanProviderCard),
|
82
|
+
},
|
78
83
|
minimax: {
|
79
84
|
enabled: false,
|
80
85
|
enabledModels: filterEnabledModels(MinimaxProviderCard),
|
@@ -14,6 +14,7 @@ import { LobeFireworksAI } from './fireworksai';
|
|
14
14
|
import { LobeGithubAI } from './github';
|
15
15
|
import { LobeGoogleAI } from './google';
|
16
16
|
import { LobeGroq } from './groq';
|
17
|
+
import { LobeHunyuanAI } from './hunyuan';
|
17
18
|
import { LobeMinimaxAI } from './minimax';
|
18
19
|
import { LobeMistralAI } from './mistral';
|
19
20
|
import { LobeMoonshotAI } from './moonshot';
|
@@ -133,6 +134,7 @@ class AgentRuntime {
|
|
133
134
|
github: Partial<ClientOptions>;
|
134
135
|
google: { apiKey?: string; baseURL?: string };
|
135
136
|
groq: Partial<ClientOptions>;
|
137
|
+
hunyuan: Partial<ClientOptions>;
|
136
138
|
minimax: Partial<ClientOptions>;
|
137
139
|
mistral: Partial<ClientOptions>;
|
138
140
|
moonshot: Partial<ClientOptions>;
|
@@ -300,6 +302,11 @@ class AgentRuntime {
|
|
300
302
|
runtimeModel = new LobeAi21AI(params.ai21);
|
301
303
|
break;
|
302
304
|
}
|
305
|
+
|
306
|
+
case ModelProvider.Hunyuan: {
|
307
|
+
runtimeModel = new LobeHunyuanAI(params.hunyuan);
|
308
|
+
break;
|
309
|
+
}
|
303
310
|
}
|
304
311
|
|
305
312
|
return new AgentRuntime(runtimeModel);
|
@@ -0,0 +1,255 @@
|
|
1
|
+
// @vitest-environment node
|
2
|
+
import OpenAI from 'openai';
|
3
|
+
import { Mock, afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
4
|
+
|
5
|
+
import {
|
6
|
+
ChatStreamCallbacks,
|
7
|
+
LobeOpenAICompatibleRuntime,
|
8
|
+
ModelProvider,
|
9
|
+
} from '@/libs/agent-runtime';
|
10
|
+
|
11
|
+
import * as debugStreamModule from '../utils/debugStream';
|
12
|
+
import { LobeHunyuanAI } from './index';
|
13
|
+
|
14
|
+
const provider = ModelProvider.Hunyuan;
|
15
|
+
const defaultBaseURL = 'https://api.hunyuan.cloud.tencent.com/v1';
|
16
|
+
|
17
|
+
const bizErrorType = 'ProviderBizError';
|
18
|
+
const invalidErrorType = 'InvalidProviderAPIKey';
|
19
|
+
|
20
|
+
// Mock the console.error to avoid polluting test output
|
21
|
+
vi.spyOn(console, 'error').mockImplementation(() => {});
|
22
|
+
|
23
|
+
let instance: LobeOpenAICompatibleRuntime;
|
24
|
+
|
25
|
+
beforeEach(() => {
|
26
|
+
instance = new LobeHunyuanAI({ apiKey: 'test' });
|
27
|
+
|
28
|
+
// 使用 vi.spyOn 来模拟 chat.completions.create 方法
|
29
|
+
vi.spyOn(instance['client'].chat.completions, 'create').mockResolvedValue(
|
30
|
+
new ReadableStream() as any,
|
31
|
+
);
|
32
|
+
});
|
33
|
+
|
34
|
+
afterEach(() => {
|
35
|
+
vi.clearAllMocks();
|
36
|
+
});
|
37
|
+
|
38
|
+
describe('LobeHunyuanAI', () => {
|
39
|
+
describe('init', () => {
|
40
|
+
it('should correctly initialize with an API key', async () => {
|
41
|
+
const instance = new LobeHunyuanAI({ apiKey: 'test_api_key' });
|
42
|
+
expect(instance).toBeInstanceOf(LobeHunyuanAI);
|
43
|
+
expect(instance.baseURL).toEqual(defaultBaseURL);
|
44
|
+
});
|
45
|
+
});
|
46
|
+
|
47
|
+
describe('chat', () => {
|
48
|
+
describe('Error', () => {
|
49
|
+
it('should return OpenAIBizError with an openai error response when OpenAI.APIError is thrown', async () => {
|
50
|
+
// Arrange
|
51
|
+
const apiError = new OpenAI.APIError(
|
52
|
+
400,
|
53
|
+
{
|
54
|
+
status: 400,
|
55
|
+
error: {
|
56
|
+
message: 'Bad Request',
|
57
|
+
},
|
58
|
+
},
|
59
|
+
'Error message',
|
60
|
+
{},
|
61
|
+
);
|
62
|
+
|
63
|
+
vi.spyOn(instance['client'].chat.completions, 'create').mockRejectedValue(apiError);
|
64
|
+
|
65
|
+
// Act
|
66
|
+
try {
|
67
|
+
await instance.chat({
|
68
|
+
messages: [{ content: 'Hello', role: 'user' }],
|
69
|
+
model: 'hunyuan-lite',
|
70
|
+
temperature: 0,
|
71
|
+
});
|
72
|
+
} catch (e) {
|
73
|
+
expect(e).toEqual({
|
74
|
+
endpoint: defaultBaseURL,
|
75
|
+
error: {
|
76
|
+
error: { message: 'Bad Request' },
|
77
|
+
status: 400,
|
78
|
+
},
|
79
|
+
errorType: bizErrorType,
|
80
|
+
provider,
|
81
|
+
});
|
82
|
+
}
|
83
|
+
});
|
84
|
+
|
85
|
+
it('should throw AgentRuntimeError with NoOpenAIAPIKey if no apiKey is provided', async () => {
|
86
|
+
try {
|
87
|
+
new LobeHunyuanAI({});
|
88
|
+
} catch (e) {
|
89
|
+
expect(e).toEqual({ errorType: invalidErrorType });
|
90
|
+
}
|
91
|
+
});
|
92
|
+
|
93
|
+
it('should return OpenAIBizError with the cause when OpenAI.APIError is thrown with cause', async () => {
|
94
|
+
// Arrange
|
95
|
+
const errorInfo = {
|
96
|
+
stack: 'abc',
|
97
|
+
cause: {
|
98
|
+
message: 'api is undefined',
|
99
|
+
},
|
100
|
+
};
|
101
|
+
const apiError = new OpenAI.APIError(400, errorInfo, 'module error', {});
|
102
|
+
|
103
|
+
vi.spyOn(instance['client'].chat.completions, 'create').mockRejectedValue(apiError);
|
104
|
+
|
105
|
+
// Act
|
106
|
+
try {
|
107
|
+
await instance.chat({
|
108
|
+
messages: [{ content: 'Hello', role: 'user' }],
|
109
|
+
model: 'hunyuan-lite',
|
110
|
+
temperature: 0,
|
111
|
+
});
|
112
|
+
} catch (e) {
|
113
|
+
expect(e).toEqual({
|
114
|
+
endpoint: defaultBaseURL,
|
115
|
+
error: {
|
116
|
+
cause: { message: 'api is undefined' },
|
117
|
+
stack: 'abc',
|
118
|
+
},
|
119
|
+
errorType: bizErrorType,
|
120
|
+
provider,
|
121
|
+
});
|
122
|
+
}
|
123
|
+
});
|
124
|
+
|
125
|
+
it('should return OpenAIBizError with an cause response with desensitize Url', async () => {
|
126
|
+
// Arrange
|
127
|
+
const errorInfo = {
|
128
|
+
stack: 'abc',
|
129
|
+
cause: { message: 'api is undefined' },
|
130
|
+
};
|
131
|
+
const apiError = new OpenAI.APIError(400, errorInfo, 'module error', {});
|
132
|
+
|
133
|
+
instance = new LobeHunyuanAI({
|
134
|
+
apiKey: 'test',
|
135
|
+
|
136
|
+
baseURL: 'https://api.abc.com/v1',
|
137
|
+
});
|
138
|
+
|
139
|
+
vi.spyOn(instance['client'].chat.completions, 'create').mockRejectedValue(apiError);
|
140
|
+
|
141
|
+
// Act
|
142
|
+
try {
|
143
|
+
await instance.chat({
|
144
|
+
messages: [{ content: 'Hello', role: 'user' }],
|
145
|
+
model: 'hunyuan-lite',
|
146
|
+
temperature: 0,
|
147
|
+
});
|
148
|
+
} catch (e) {
|
149
|
+
expect(e).toEqual({
|
150
|
+
endpoint: 'https://api.***.com/v1',
|
151
|
+
error: {
|
152
|
+
cause: { message: 'api is undefined' },
|
153
|
+
stack: 'abc',
|
154
|
+
},
|
155
|
+
errorType: bizErrorType,
|
156
|
+
provider,
|
157
|
+
});
|
158
|
+
}
|
159
|
+
});
|
160
|
+
|
161
|
+
it('should throw an InvalidHunyuanAPIKey error type on 401 status code', async () => {
|
162
|
+
// Mock the API call to simulate a 401 error
|
163
|
+
const error = new Error('Unauthorized') as any;
|
164
|
+
error.status = 401;
|
165
|
+
vi.mocked(instance['client'].chat.completions.create).mockRejectedValue(error);
|
166
|
+
|
167
|
+
try {
|
168
|
+
await instance.chat({
|
169
|
+
messages: [{ content: 'Hello', role: 'user' }],
|
170
|
+
model: 'hunyuan-lite',
|
171
|
+
temperature: 0,
|
172
|
+
});
|
173
|
+
} catch (e) {
|
174
|
+
// Expect the chat method to throw an error with InvalidHunyuanAPIKey
|
175
|
+
expect(e).toEqual({
|
176
|
+
endpoint: defaultBaseURL,
|
177
|
+
error: new Error('Unauthorized'),
|
178
|
+
errorType: invalidErrorType,
|
179
|
+
provider,
|
180
|
+
});
|
181
|
+
}
|
182
|
+
});
|
183
|
+
|
184
|
+
it('should return AgentRuntimeError for non-OpenAI errors', async () => {
|
185
|
+
// Arrange
|
186
|
+
const genericError = new Error('Generic Error');
|
187
|
+
|
188
|
+
vi.spyOn(instance['client'].chat.completions, 'create').mockRejectedValue(genericError);
|
189
|
+
|
190
|
+
// Act
|
191
|
+
try {
|
192
|
+
await instance.chat({
|
193
|
+
messages: [{ content: 'Hello', role: 'user' }],
|
194
|
+
model: 'hunyuan-lite',
|
195
|
+
temperature: 0,
|
196
|
+
});
|
197
|
+
} catch (e) {
|
198
|
+
expect(e).toEqual({
|
199
|
+
endpoint: defaultBaseURL,
|
200
|
+
errorType: 'AgentRuntimeError',
|
201
|
+
provider,
|
202
|
+
error: {
|
203
|
+
name: genericError.name,
|
204
|
+
cause: genericError.cause,
|
205
|
+
message: genericError.message,
|
206
|
+
stack: genericError.stack,
|
207
|
+
},
|
208
|
+
});
|
209
|
+
}
|
210
|
+
});
|
211
|
+
});
|
212
|
+
|
213
|
+
describe('DEBUG', () => {
|
214
|
+
it('should call debugStream and return StreamingTextResponse when DEBUG_HUNYUAN_CHAT_COMPLETION is 1', async () => {
|
215
|
+
// Arrange
|
216
|
+
const mockProdStream = new ReadableStream() as any; // 模拟的 prod 流
|
217
|
+
const mockDebugStream = new ReadableStream({
|
218
|
+
start(controller) {
|
219
|
+
controller.enqueue('Debug stream content');
|
220
|
+
controller.close();
|
221
|
+
},
|
222
|
+
}) as any;
|
223
|
+
mockDebugStream.toReadableStream = () => mockDebugStream; // 添加 toReadableStream 方法
|
224
|
+
|
225
|
+
// 模拟 chat.completions.create 返回值,包括模拟的 tee 方法
|
226
|
+
(instance['client'].chat.completions.create as Mock).mockResolvedValue({
|
227
|
+
tee: () => [mockProdStream, { toReadableStream: () => mockDebugStream }],
|
228
|
+
});
|
229
|
+
|
230
|
+
// 保存原始环境变量值
|
231
|
+
const originalDebugValue = process.env.DEBUG_HUNYUAN_CHAT_COMPLETION;
|
232
|
+
|
233
|
+
// 模拟环境变量
|
234
|
+
process.env.DEBUG_HUNYUAN_CHAT_COMPLETION = '1';
|
235
|
+
vi.spyOn(debugStreamModule, 'debugStream').mockImplementation(() => Promise.resolve());
|
236
|
+
|
237
|
+
// 执行测试
|
238
|
+
// 运行你的测试函数,确保它会在条件满足时调用 debugStream
|
239
|
+
// 假设的测试函数调用,你可能需要根据实际情况调整
|
240
|
+
await instance.chat({
|
241
|
+
messages: [{ content: 'Hello', role: 'user' }],
|
242
|
+
model: 'hunyuan-lite',
|
243
|
+
stream: true,
|
244
|
+
temperature: 0,
|
245
|
+
});
|
246
|
+
|
247
|
+
// 验证 debugStream 被调用
|
248
|
+
expect(debugStreamModule.debugStream).toHaveBeenCalled();
|
249
|
+
|
250
|
+
// 恢复原始环境变量值
|
251
|
+
process.env.DEBUG_HUNYUAN_CHAT_COMPLETION = originalDebugValue;
|
252
|
+
});
|
253
|
+
});
|
254
|
+
});
|
255
|
+
});
|
@@ -0,0 +1,10 @@
|
|
1
|
+
import { ModelProvider } from '../types';
|
2
|
+
import { LobeOpenAICompatibleFactory } from '../utils/openaiCompatibleFactory';
|
3
|
+
|
4
|
+
export const LobeHunyuanAI = LobeOpenAICompatibleFactory({
|
5
|
+
baseURL: 'https://api.hunyuan.cloud.tencent.com/v1',
|
6
|
+
debug: {
|
7
|
+
chatCompletion: () => process.env.DEBUG_HUNYUAN_CHAT_COMPLETION === '1',
|
8
|
+
},
|
9
|
+
provider: ModelProvider.Hunyuan,
|
10
|
+
});
|
@@ -83,7 +83,7 @@ Usage of Gemini is subject to Google's [Gemini Terms of Use](https://ai.google.d
|
|
83
83
|
|
84
84
|
Note: This model is experimental and not suited for production use-cases. It may be removed or redirected to another model in the future.",
|
85
85
|
"displayName": "Google: Gemini Flash 1.5 Experimental",
|
86
|
-
"enabled":
|
86
|
+
"enabled": false,
|
87
87
|
"functionCall": false,
|
88
88
|
"id": "google/gemini-flash-1.5-exp",
|
89
89
|
"maxTokens": 32768,
|
@@ -291,7 +291,7 @@ Usage of Gemini is subject to Google's [Gemini Terms of Use](https://ai.google.d
|
|
291
291
|
|
292
292
|
Note: This model is experimental and not suited for production use-cases. It may be removed or redirected to another model in the future.",
|
293
293
|
"displayName": "Google: Gemini Pro 1.5 Experimental",
|
294
|
-
"enabled":
|
294
|
+
"enabled": false,
|
295
295
|
"functionCall": false,
|
296
296
|
"id": "google/gemini-pro-1.5-exp",
|
297
297
|
"maxTokens": 32768,
|
@@ -878,7 +878,7 @@ Usage of Gemini is subject to Google's [Gemini Terms of Use](https://ai.google.d
|
|
878
878
|
|
879
879
|
#multimodal",
|
880
880
|
"displayName": "Google: Gemini Flash 1.5",
|
881
|
-
"enabled":
|
881
|
+
"enabled": true,
|
882
882
|
"functionCall": false,
|
883
883
|
"id": "google/gemini-flash-1.5",
|
884
884
|
"maxTokens": 32768,
|
@@ -1249,7 +1249,7 @@ Usage of Gemini is subject to Google's [Gemini Terms of Use](https://ai.google.d
|
|
1249
1249
|
|
1250
1250
|
#multimodal",
|
1251
1251
|
"displayName": "Google: Gemini Pro 1.5",
|
1252
|
-
"enabled":
|
1252
|
+
"enabled": true,
|
1253
1253
|
"functionCall": false,
|
1254
1254
|
"id": "google/gemini-pro-1.5",
|
1255
1255
|
"maxTokens": 32768,
|
@@ -9,6 +9,7 @@ import {
|
|
9
9
|
GithubProviderCard,
|
10
10
|
GoogleProviderCard,
|
11
11
|
GroqProviderCard,
|
12
|
+
HunyuanProviderCard,
|
12
13
|
NovitaProviderCard,
|
13
14
|
OllamaProviderCard,
|
14
15
|
OpenAIProviderCard,
|
@@ -49,6 +50,9 @@ export const getServerGlobalConfig = () => {
|
|
49
50
|
ENABLED_GITHUB,
|
50
51
|
GITHUB_MODEL_LIST,
|
51
52
|
|
53
|
+
ENABLED_HUNYUAN,
|
54
|
+
HUNYUAN_MODEL_LIST,
|
55
|
+
|
52
56
|
ENABLED_DEEPSEEK,
|
53
57
|
ENABLED_PERPLEXITY,
|
54
58
|
ENABLED_ANTHROPIC,
|
@@ -160,6 +164,14 @@ export const getServerGlobalConfig = () => {
|
|
160
164
|
modelString: GROQ_MODEL_LIST,
|
161
165
|
}),
|
162
166
|
},
|
167
|
+
hunyuan: {
|
168
|
+
enabled: ENABLED_HUNYUAN,
|
169
|
+
enabledModels: extractEnabledModels(HUNYUAN_MODEL_LIST),
|
170
|
+
serverModelCards: transformToChatModelCards({
|
171
|
+
defaultChatModels: HunyuanProviderCard.chatModels,
|
172
|
+
modelString: HUNYUAN_MODEL_LIST,
|
173
|
+
}),
|
174
|
+
},
|
163
175
|
minimax: { enabled: ENABLED_MINIMAX },
|
164
176
|
mistral: { enabled: ENABLED_MISTRAL },
|
165
177
|
moonshot: { enabled: ENABLED_MOONSHOT },
|
@@ -28,6 +28,7 @@ export interface UserKeyVaults {
|
|
28
28
|
github?: OpenAICompatibleKeyVault;
|
29
29
|
google?: OpenAICompatibleKeyVault;
|
30
30
|
groq?: OpenAICompatibleKeyVault;
|
31
|
+
hunyuan?: OpenAICompatibleKeyVault;
|
31
32
|
lobehub?: any;
|
32
33
|
minimax?: OpenAICompatibleKeyVault;
|
33
34
|
mistral?: OpenAICompatibleKeyVault;
|