@lobehub/chat 1.3.5 → 1.4.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/README.md +2 -2
- package/README.zh-CN.md +2 -2
- package/netlify.toml +1 -1
- package/package.json +1 -1
- package/src/app/(main)/settings/llm/ProviderList/providers.tsx +7 -0
- package/src/app/api/chat/agentRuntime.ts +14 -2
- package/src/components/ModelIcon/index.tsx +2 -0
- package/src/components/ModelProviderIcon/index.tsx +5 -0
- package/src/components/ModelTag/ModelIcon.tsx +2 -0
- package/src/config/llm.ts +6 -0
- package/src/config/modelProviders/ai360.ts +38 -0
- package/src/config/modelProviders/index.ts +4 -0
- package/src/const/settings/llm.ts +5 -0
- package/src/features/Conversation/Error/APIKeyForm/ProviderAvatar.tsx +5 -0
- package/src/libs/agent-runtime/AgentRuntime.ts +26 -19
- package/src/libs/agent-runtime/ai360/index.test.ts +255 -0
- package/src/libs/agent-runtime/ai360/index.ts +10 -0
- package/src/libs/agent-runtime/anthropic/index.ts +1 -1
- package/src/libs/agent-runtime/bedrock/index.ts +1 -1
- package/src/libs/agent-runtime/google/index.ts +1 -1
- package/src/libs/agent-runtime/minimax/index.ts +1 -1
- package/src/libs/agent-runtime/ollama/index.ts +1 -1
- package/src/libs/agent-runtime/types/type.ts +1 -0
- package/src/libs/agent-runtime/utils/openaiCompatibleFactory/index.ts +61 -90
- package/src/libs/agent-runtime/zhipu/index.ts +1 -1
- package/src/server/globalConfig/index.ts +2 -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.4.0](https://github.com/lobehub/lobe-chat/compare/v1.3.6...v1.4.0)
|
|
6
|
+
|
|
7
|
+
<sup>Released on **2024-07-12**</sup>
|
|
8
|
+
|
|
9
|
+
#### ✨ Features
|
|
10
|
+
|
|
11
|
+
- **misc**: Add 360AI 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 360AI model provider, closes [#3130](https://github.com/lobehub/lobe-chat/issues/3130) ([79c5f86](https://github.com/lobehub/lobe-chat/commit/79c5f86))
|
|
21
|
+
|
|
22
|
+
</details>
|
|
23
|
+
|
|
24
|
+
<div align="right">
|
|
25
|
+
|
|
26
|
+
[](#readme-top)
|
|
27
|
+
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
### [Version 1.3.6](https://github.com/lobehub/lobe-chat/compare/v1.3.5...v1.3.6)
|
|
31
|
+
|
|
32
|
+
<sup>Released on **2024-07-11**</sup>
|
|
33
|
+
|
|
34
|
+
#### ♻ Code Refactoring
|
|
35
|
+
|
|
36
|
+
- **misc**: Improve agent runtime code.
|
|
37
|
+
|
|
38
|
+
<br/>
|
|
39
|
+
|
|
40
|
+
<details>
|
|
41
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
42
|
+
|
|
43
|
+
#### Code refactoring
|
|
44
|
+
|
|
45
|
+
- **misc**: Improve agent runtime code, closes [#3199](https://github.com/lobehub/lobe-chat/issues/3199) ([9f211e2](https://github.com/lobehub/lobe-chat/commit/9f211e2))
|
|
46
|
+
|
|
47
|
+
</details>
|
|
48
|
+
|
|
49
|
+
<div align="right">
|
|
50
|
+
|
|
51
|
+
[](#readme-top)
|
|
52
|
+
|
|
53
|
+
</div>
|
|
54
|
+
|
|
5
55
|
### [Version 1.3.5](https://github.com/lobehub/lobe-chat/compare/v1.3.4...v1.3.5)
|
|
6
56
|
|
|
7
57
|
<sup>Released on **2024-07-10**</sup>
|
package/README.md
CHANGED
|
@@ -267,12 +267,12 @@ Our marketplace is not just a showcase platform but also a collaborative space.
|
|
|
267
267
|
|
|
268
268
|
| Recent Submits | Description |
|
|
269
269
|
| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
270
|
+
| [Ducky Programming Assistant](https://chat-preview.lobehub.com/market?agent=rubber-duck-programming)<br/><sup>By **[JiyuShao](https://github.com/JiyuShao)** on **2024-07-10**</sup> | Ducky Programming Assistant<br/>`programming` |
|
|
270
271
|
| [AOSP Source Code Expert](https://chat-preview.lobehub.com/market?agent=aosp-development)<br/><sup>By **[viruscoding](https://github.com/viruscoding)** on **2024-06-24**</sup> | An expert in AOSP (Android Open Source Project) for Android, with a deep understanding and analytical ability of the latest AOSP source code.<br/>`aosp` |
|
|
271
272
|
| [Fastapi Project Development Assistant](https://chat-preview.lobehub.com/market?agent=fastapi-development)<br/><sup>By **[xwxw098](https://github.com/xwxw098)** on **2024-06-19**</sup> | Proficient in Python modular development, skilled in using FastAPI, PostgreSQL, Tortoise-ORM, and other technologies, able to provide clear code structure and detailed comments for large projects.<br/>`fast-api` `python` `modular-development` |
|
|
272
273
|
| [IT Systems Architect](https://chat-preview.lobehub.com/market?agent=it-system-architect)<br/><sup>By **[a562314](https://github.com/a562314)** on **2024-06-19**</sup> | Senior IT architect specializing in requirements analysis, system design, technology selection, and cross-platform system optimization. With over 5 years of experience, proficient in Windows, macOS, and Linux operating systems, skilled in troubleshooting, and security protection.<br/>`it-architecture-design` `problem-solving` `agile-development` `system-optimization` `cross-platform-skills` `teamwork` |
|
|
273
|
-
| [Linux Kernel Expert](https://chat-preview.lobehub.com/market?agent=linux-kernel)<br/><sup>By **[wming126](https://github.com/wming126)** on **2024-06-19**</sup> | Role Description: I am an expert in Linux kernel, with a deep understanding and analytical ability of the latest kernel source code (as of June 2024). I can provide users with detailed and accurate information about the Linux kernel.<br/>`linux` `kernel` |
|
|
274
274
|
|
|
275
|
-
> 📊 Total agents: [<kbd>**
|
|
275
|
+
> 📊 Total agents: [<kbd>**294**</kbd> ](https://github.com/lobehub/lobe-chat-agents)
|
|
276
276
|
|
|
277
277
|
<!-- AGENT LIST -->
|
|
278
278
|
|
package/README.zh-CN.md
CHANGED
|
@@ -256,12 +256,12 @@ LobeChat 的插件生态系统是其核心功能的重要扩展,它极大地
|
|
|
256
256
|
|
|
257
257
|
| 最近新增 | 助手说明 |
|
|
258
258
|
| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
259
|
+
| [小黄鸭编程助手](https://chat-preview.lobehub.com/market?agent=rubber-duck-programming)<br/><sup>By **[JiyuShao](https://github.com/JiyuShao)** on **2024-07-10**</sup> | 小黄鸭编程助手<br/>`programming` |
|
|
259
260
|
| [AOSP 源码专家](https://chat-preview.lobehub.com/market?agent=aosp-development)<br/><sup>By **[viruscoding](https://github.com/viruscoding)** on **2024-06-24**</sup> | 一位精通 AOSP(Android Open Source Project)安卓的专家,对最新 AOSP 源代码有着深入的理解和分析能力。<br/>`aosp` |
|
|
260
261
|
| [Fastapi 项目开发助手](https://chat-preview.lobehub.com/market?agent=fastapi-development)<br/><sup>By **[xwxw098](https://github.com/xwxw098)** on **2024-06-19**</sup> | 擅长 Python 模块化开发,熟练运用 FastAPI、PostgreSQL、Tortoise-ORM 等技术栈,能为大型项目提供清晰的代码结构并添加详细注释。<br/>`fast-api` `python` `模块化开发` |
|
|
261
262
|
| [IT 系统架构师](https://chat-preview.lobehub.com/market?agent=it-system-architect)<br/><sup>By **[a562314](https://github.com/a562314)** on **2024-06-19**</sup> | 资深 IT 架构师,擅长需求分析、系统设计、技术选型和跨平台系统优化。5 年以上经验,精通 Windows、macOS 和 Linux 三大操作系统,具备故障排除和安全防护能力<br/>`it架构设计` `问题解决` `敏捷开发` `系统优化` `跨平台技能` |
|
|
262
|
-
| [Linux 内核专家](https://chat-preview.lobehub.com/market?agent=linux-kernel)<br/><sup>By **[wming126](https://github.com/wming126)** on **2024-06-19**</sup> | 角色描述: 我是一位精通 Linux 内核的专家,对最新内核源代码(截至 2024 年 6 月)有着深入的理解和分析能力。我可以为用户提供关于 Linux 内核的详细、准确的信息。<br/>`linux` `kernel` |
|
|
263
263
|
|
|
264
|
-
> 📊 Total agents: [<kbd>**
|
|
264
|
+
> 📊 Total agents: [<kbd>**294**</kbd> ](https://github.com/lobehub/lobe-chat-agents)
|
|
265
265
|
|
|
266
266
|
<!-- AGENT LIST -->
|
|
267
267
|
|
package/netlify.toml
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lobehub/chat",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.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",
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
+
Ai360,
|
|
2
3
|
AiMass,
|
|
3
4
|
Anthropic,
|
|
4
5
|
Baichuan,
|
|
@@ -25,6 +26,7 @@ import { Flexbox } from 'react-layout-kit';
|
|
|
25
26
|
import urlJoin from 'url-join';
|
|
26
27
|
|
|
27
28
|
import {
|
|
29
|
+
Ai360ProviderCard,
|
|
28
30
|
AnthropicProviderCard,
|
|
29
31
|
BaichuanProviderCard,
|
|
30
32
|
DeepSeekProviderCard,
|
|
@@ -184,6 +186,11 @@ export const useProviderList = (): ProviderItem[] => {
|
|
|
184
186
|
docUrl: urlJoin(BASE_DOC_URL, 'taichu'),
|
|
185
187
|
title: <AiMass.Combine size={ 28 } type={ 'color' } />,
|
|
186
188
|
},
|
|
189
|
+
{
|
|
190
|
+
...Ai360ProviderCard,
|
|
191
|
+
docUrl: urlJoin(BASE_DOC_URL, 'ai360'),
|
|
192
|
+
title: <Ai360.Combine size={ 20 } type={ 'color' } />,
|
|
193
|
+
},
|
|
187
194
|
],
|
|
188
195
|
[azureProvider, ollamaProvider, ollamaProvider, bedrockProvider],
|
|
189
196
|
);
|
|
@@ -184,6 +184,13 @@ const getLlmOptionsFromPayload = (provider: string, payload: JWTPayload) => {
|
|
|
184
184
|
|
|
185
185
|
const apiKey = apiKeyManager.pick(payload?.apiKey || TAICHU_API_KEY);
|
|
186
186
|
|
|
187
|
+
return { apiKey };
|
|
188
|
+
}
|
|
189
|
+
case ModelProvider.Ai360: {
|
|
190
|
+
const { AI360_API_KEY } = getLLMConfig();
|
|
191
|
+
|
|
192
|
+
const apiKey = apiKeyManager.pick(payload?.apiKey || AI360_API_KEY);
|
|
193
|
+
|
|
187
194
|
return { apiKey };
|
|
188
195
|
}
|
|
189
196
|
}
|
|
@@ -193,11 +200,16 @@ const getLlmOptionsFromPayload = (provider: string, payload: JWTPayload) => {
|
|
|
193
200
|
* Initializes the agent runtime with the user payload in backend
|
|
194
201
|
* @param provider - The provider name.
|
|
195
202
|
* @param payload - The JWT payload.
|
|
203
|
+
* @param params
|
|
196
204
|
* @returns A promise that resolves when the agent runtime is initialized.
|
|
197
205
|
*/
|
|
198
|
-
export const initAgentRuntimeWithUserPayload = (
|
|
206
|
+
export const initAgentRuntimeWithUserPayload = (
|
|
207
|
+
provider: string,
|
|
208
|
+
payload: JWTPayload,
|
|
209
|
+
params: any = {},
|
|
210
|
+
) => {
|
|
199
211
|
return AgentRuntime.initializeWithProviderOptions(provider, {
|
|
200
|
-
[provider]: getLlmOptionsFromPayload(provider, payload),
|
|
212
|
+
[provider]: { ...getLlmOptionsFromPayload(provider, payload), ...params },
|
|
201
213
|
});
|
|
202
214
|
};
|
|
203
215
|
|
|
@@ -2,6 +2,7 @@ import {
|
|
|
2
2
|
AiMass,
|
|
3
3
|
Adobe,
|
|
4
4
|
Ai21,
|
|
5
|
+
Ai360,
|
|
5
6
|
Aws,
|
|
6
7
|
Azure,
|
|
7
8
|
Baichuan,
|
|
@@ -68,6 +69,7 @@ const ModelIcon = memo<ModelProviderIconProps>(({ model: originModel, size = 12
|
|
|
68
69
|
if (model.includes('dbrx')) return <Dbrx.Avatar size={size} />;
|
|
69
70
|
if (model.includes('step')) return <Stepfun.Avatar size={size} />;
|
|
70
71
|
if (model.includes('taichu')) return <AiMass.Avatar size={size} />;
|
|
72
|
+
if (model.includes('360gpt')) return <Ai360.Avatar size={size} />;
|
|
71
73
|
|
|
72
74
|
// below: To be supported in providers, move up if supported
|
|
73
75
|
if (model.includes('baichuan'))
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
+
Ai360,
|
|
2
3
|
AiMass,
|
|
3
4
|
Anthropic,
|
|
4
5
|
Azure,
|
|
@@ -124,6 +125,10 @@ const ModelProviderIcon = memo<ModelProviderIconProps>(({ provider }) => {
|
|
|
124
125
|
return <AiMass size={20} />;
|
|
125
126
|
}
|
|
126
127
|
|
|
128
|
+
case ModelProvider.Ai360: {
|
|
129
|
+
return <Ai360 size={20} />;
|
|
130
|
+
}
|
|
131
|
+
|
|
127
132
|
default: {
|
|
128
133
|
return null;
|
|
129
134
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
AdobeFirefly,
|
|
3
3
|
Ai21,
|
|
4
|
+
Ai360,
|
|
4
5
|
AiMass,
|
|
5
6
|
Aws,
|
|
6
7
|
Azure,
|
|
@@ -63,6 +64,7 @@ const ModelIcon = memo<ModelIconProps>(({ model, size = 12 }) => {
|
|
|
63
64
|
if (model.includes('command')) return <Cohere size={size} />;
|
|
64
65
|
if (model.includes('dbrx')) return <Dbrx size={size} />;
|
|
65
66
|
if (model.includes('taichu')) return <AiMass size={size} />;
|
|
67
|
+
if (model.includes('360gpt')) return <Ai360 size={size} />;
|
|
66
68
|
|
|
67
69
|
// below: To be supported in providers, move up if supported
|
|
68
70
|
if (model.includes('baichuan')) return <Baichuan size={size} />;
|
package/src/config/llm.ts
CHANGED
|
@@ -88,6 +88,9 @@ export const getLLMConfig = () => {
|
|
|
88
88
|
|
|
89
89
|
ENABLED_TAICHU: z.boolean(),
|
|
90
90
|
TAICHU_API_KEY: z.string().optional(),
|
|
91
|
+
|
|
92
|
+
ENABLED_AI360: z.boolean(),
|
|
93
|
+
AI360_API_KEY: z.string().optional(),
|
|
91
94
|
},
|
|
92
95
|
runtimeEnv: {
|
|
93
96
|
API_KEY_SELECT_MODE: process.env.API_KEY_SELECT_MODE,
|
|
@@ -167,6 +170,9 @@ export const getLLMConfig = () => {
|
|
|
167
170
|
|
|
168
171
|
ENABLED_TAICHU: !!process.env.TAICHU_API_KEY,
|
|
169
172
|
TAICHU_API_KEY: process.env.TAICHU_API_KEY,
|
|
173
|
+
|
|
174
|
+
ENABLED_AI360: !!process.env.AI360_API_KEY,
|
|
175
|
+
AI360_API_KEY: process.env.AI360_API_KEY,
|
|
170
176
|
},
|
|
171
177
|
});
|
|
172
178
|
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { ModelProviderCard } from '@/types/llm';
|
|
2
|
+
|
|
3
|
+
// ref https://ai.360.cn/platform/docs/overview
|
|
4
|
+
const Ai360: ModelProviderCard = {
|
|
5
|
+
chatModels: [
|
|
6
|
+
{
|
|
7
|
+
displayName: '360GPT Pro',
|
|
8
|
+
enabled: true,
|
|
9
|
+
functionCall: false,
|
|
10
|
+
id: '360gpt-pro',
|
|
11
|
+
maxOutput: 7000,
|
|
12
|
+
tokens: 8192,
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
displayName: '360GPT Turbo',
|
|
16
|
+
enabled: true,
|
|
17
|
+
functionCall: false,
|
|
18
|
+
id: '360gpt-turbo',
|
|
19
|
+
maxOutput: 8192,
|
|
20
|
+
tokens: 8192,
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
displayName: '360GPT Turbo Responsibility 8K',
|
|
24
|
+
enabled: true,
|
|
25
|
+
functionCall: false,
|
|
26
|
+
id: '360gpt-turbo-responsibility-8k',
|
|
27
|
+
maxOutput: 2048,
|
|
28
|
+
tokens: 8192,
|
|
29
|
+
},
|
|
30
|
+
],
|
|
31
|
+
checkModel: '360gpt-turbo',
|
|
32
|
+
disableBrowserRequest: true,
|
|
33
|
+
id: 'ai360',
|
|
34
|
+
modelList: { showModelFetcher: true },
|
|
35
|
+
name: '360智脑',
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export default Ai360;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ChatModelCard, ModelProviderCard } from '@/types/llm';
|
|
2
2
|
|
|
3
|
+
import Ai360Provider from './ai360';
|
|
3
4
|
import AnthropicProvider from './anthropic';
|
|
4
5
|
import AzureProvider from './azure';
|
|
5
6
|
import BaichuanProvider from './baichuan';
|
|
@@ -41,6 +42,7 @@ export const LOBE_DEFAULT_MODEL_LIST: ChatModelCard[] = [
|
|
|
41
42
|
StepfunProvider.chatModels,
|
|
42
43
|
BaichuanProvider.chatModels,
|
|
43
44
|
TaichuProvider.chatModels,
|
|
45
|
+
Ai360Provider.chatModels,
|
|
44
46
|
].flat();
|
|
45
47
|
|
|
46
48
|
export const DEFAULT_MODEL_PROVIDER_LIST = [
|
|
@@ -64,6 +66,7 @@ export const DEFAULT_MODEL_PROVIDER_LIST = [
|
|
|
64
66
|
StepfunProvider,
|
|
65
67
|
BaichuanProvider,
|
|
66
68
|
TaichuProvider,
|
|
69
|
+
Ai360Provider,
|
|
67
70
|
];
|
|
68
71
|
|
|
69
72
|
export const filterEnabledModels = (provider: ModelProviderCard) => {
|
|
@@ -75,6 +78,7 @@ export const isProviderDisableBroswerRequest = (id: string) => {
|
|
|
75
78
|
return !!provider;
|
|
76
79
|
};
|
|
77
80
|
|
|
81
|
+
export { default as Ai360ProviderCard } from './ai360';
|
|
78
82
|
export { default as AnthropicProviderCard } from './anthropic';
|
|
79
83
|
export { default as AzureProviderCard } from './azure';
|
|
80
84
|
export { default as BaichuanProviderCard } from './baichuan';
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
+
Ai360ProviderCard,
|
|
2
3
|
AnthropicProviderCard,
|
|
3
4
|
BaichuanProviderCard,
|
|
4
5
|
BedrockProviderCard,
|
|
@@ -24,6 +25,10 @@ import { ModelProvider } from '@/libs/agent-runtime';
|
|
|
24
25
|
import { UserModelProviderConfig } from '@/types/user/settings';
|
|
25
26
|
|
|
26
27
|
export const DEFAULT_LLM_CONFIG: UserModelProviderConfig = {
|
|
28
|
+
ai360: {
|
|
29
|
+
enabled: false,
|
|
30
|
+
enabledModels: filterEnabledModels(Ai360ProviderCard),
|
|
31
|
+
},
|
|
27
32
|
anthropic: {
|
|
28
33
|
enabled: false,
|
|
29
34
|
enabledModels: filterEnabledModels(AnthropicProviderCard),
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
+
Ai360,
|
|
2
3
|
AiMass,
|
|
3
4
|
Anthropic,
|
|
4
5
|
Baichuan,
|
|
@@ -94,6 +95,10 @@ const ProviderAvatar = memo<ProviderAvatarProps>(({ provider }) => {
|
|
|
94
95
|
return <ZeroOne color={ZeroOne.colorPrimary} size={56} />;
|
|
95
96
|
}
|
|
96
97
|
|
|
98
|
+
case ModelProvider.Ai360: {
|
|
99
|
+
return <Ai360 color={Ai360.colorPrimary} size={56} />;
|
|
100
|
+
}
|
|
101
|
+
|
|
97
102
|
default:
|
|
98
103
|
case ModelProvider.OpenAI: {
|
|
99
104
|
return <OpenAI color={theme.colorText} size={64} />;
|
|
@@ -3,6 +3,7 @@ import { ClientOptions } from 'openai';
|
|
|
3
3
|
import type { TracePayload } from '@/const/trace';
|
|
4
4
|
|
|
5
5
|
import { LobeRuntimeAI } from './BaseAI';
|
|
6
|
+
import { LobeAi360AI } from './ai360';
|
|
6
7
|
import { LobeAnthropicAI } from './anthropic';
|
|
7
8
|
import { LobeAzureOpenAI } from './azureOpenai';
|
|
8
9
|
import { LobeBaichuanAI } from './baichuan';
|
|
@@ -103,6 +104,7 @@ class AgentRuntime {
|
|
|
103
104
|
static async initializeWithProviderOptions(
|
|
104
105
|
provider: string,
|
|
105
106
|
params: Partial<{
|
|
107
|
+
ai360: Partial<ClientOptions>;
|
|
106
108
|
anthropic: Partial<ClientOptions>;
|
|
107
109
|
azure: { apiVersion?: string; apikey?: string; endpoint?: string };
|
|
108
110
|
baichuan: Partial<ClientOptions>;
|
|
@@ -145,92 +147,97 @@ class AgentRuntime {
|
|
|
145
147
|
}
|
|
146
148
|
|
|
147
149
|
case ModelProvider.ZhiPu: {
|
|
148
|
-
runtimeModel = await LobeZhipuAI.fromAPIKey(params.zhipu
|
|
150
|
+
runtimeModel = await LobeZhipuAI.fromAPIKey(params.zhipu);
|
|
149
151
|
break;
|
|
150
152
|
}
|
|
151
153
|
|
|
152
154
|
case ModelProvider.Google: {
|
|
153
|
-
runtimeModel = new LobeGoogleAI(params.google
|
|
155
|
+
runtimeModel = new LobeGoogleAI(params.google);
|
|
154
156
|
break;
|
|
155
157
|
}
|
|
156
158
|
|
|
157
159
|
case ModelProvider.Moonshot: {
|
|
158
|
-
runtimeModel = new LobeMoonshotAI(params.moonshot
|
|
160
|
+
runtimeModel = new LobeMoonshotAI(params.moonshot);
|
|
159
161
|
break;
|
|
160
162
|
}
|
|
161
163
|
|
|
162
164
|
case ModelProvider.Bedrock: {
|
|
163
|
-
runtimeModel = new LobeBedrockAI(params.bedrock
|
|
165
|
+
runtimeModel = new LobeBedrockAI(params.bedrock);
|
|
164
166
|
break;
|
|
165
167
|
}
|
|
166
168
|
|
|
167
169
|
case ModelProvider.Ollama: {
|
|
168
|
-
runtimeModel = new LobeOllamaAI(params.ollama
|
|
170
|
+
runtimeModel = new LobeOllamaAI(params.ollama);
|
|
169
171
|
break;
|
|
170
172
|
}
|
|
171
173
|
|
|
172
174
|
case ModelProvider.Perplexity: {
|
|
173
|
-
runtimeModel = new LobePerplexityAI(params.perplexity
|
|
175
|
+
runtimeModel = new LobePerplexityAI(params.perplexity);
|
|
174
176
|
break;
|
|
175
177
|
}
|
|
176
178
|
|
|
177
179
|
case ModelProvider.Anthropic: {
|
|
178
|
-
runtimeModel = new LobeAnthropicAI(params.anthropic
|
|
180
|
+
runtimeModel = new LobeAnthropicAI(params.anthropic);
|
|
179
181
|
break;
|
|
180
182
|
}
|
|
181
183
|
|
|
182
184
|
case ModelProvider.DeepSeek: {
|
|
183
|
-
runtimeModel = new LobeDeepSeekAI(params.deepseek
|
|
185
|
+
runtimeModel = new LobeDeepSeekAI(params.deepseek);
|
|
184
186
|
break;
|
|
185
187
|
}
|
|
186
188
|
|
|
187
189
|
case ModelProvider.Minimax: {
|
|
188
|
-
runtimeModel = new LobeMinimaxAI(params.minimax
|
|
190
|
+
runtimeModel = new LobeMinimaxAI(params.minimax);
|
|
189
191
|
break;
|
|
190
192
|
}
|
|
191
193
|
|
|
192
194
|
case ModelProvider.Mistral: {
|
|
193
|
-
runtimeModel = new LobeMistralAI(params.mistral
|
|
195
|
+
runtimeModel = new LobeMistralAI(params.mistral);
|
|
194
196
|
break;
|
|
195
197
|
}
|
|
196
198
|
|
|
197
199
|
case ModelProvider.Groq: {
|
|
198
|
-
runtimeModel = new LobeGroq(params.groq
|
|
200
|
+
runtimeModel = new LobeGroq(params.groq);
|
|
199
201
|
break;
|
|
200
202
|
}
|
|
201
203
|
|
|
202
204
|
case ModelProvider.OpenRouter: {
|
|
203
|
-
runtimeModel = new LobeOpenRouterAI(params.openrouter
|
|
205
|
+
runtimeModel = new LobeOpenRouterAI(params.openrouter);
|
|
204
206
|
break;
|
|
205
207
|
}
|
|
206
208
|
|
|
207
209
|
case ModelProvider.TogetherAI: {
|
|
208
|
-
runtimeModel = new LobeTogetherAI(params.togetherai
|
|
210
|
+
runtimeModel = new LobeTogetherAI(params.togetherai);
|
|
209
211
|
break;
|
|
210
212
|
}
|
|
211
213
|
|
|
212
214
|
case ModelProvider.ZeroOne: {
|
|
213
|
-
runtimeModel = new LobeZeroOneAI(params.zeroone
|
|
215
|
+
runtimeModel = new LobeZeroOneAI(params.zeroone);
|
|
214
216
|
break;
|
|
215
217
|
}
|
|
216
218
|
|
|
217
219
|
case ModelProvider.Qwen: {
|
|
218
|
-
runtimeModel = new LobeQwenAI(params.qwen
|
|
220
|
+
runtimeModel = new LobeQwenAI(params.qwen);
|
|
219
221
|
break;
|
|
220
222
|
}
|
|
221
223
|
|
|
222
224
|
case ModelProvider.Stepfun: {
|
|
223
|
-
runtimeModel = new LobeStepfunAI(params.stepfun
|
|
225
|
+
runtimeModel = new LobeStepfunAI(params.stepfun);
|
|
224
226
|
break;
|
|
225
227
|
}
|
|
226
228
|
|
|
227
229
|
case ModelProvider.Baichuan: {
|
|
228
|
-
runtimeModel = new LobeBaichuanAI(params.baichuan
|
|
229
|
-
break
|
|
230
|
+
runtimeModel = new LobeBaichuanAI(params.baichuan);
|
|
231
|
+
break;
|
|
230
232
|
}
|
|
231
233
|
|
|
232
234
|
case ModelProvider.Taichu: {
|
|
233
|
-
runtimeModel = new LobeTaichuAI(params.taichu
|
|
235
|
+
runtimeModel = new LobeTaichuAI(params.taichu);
|
|
236
|
+
break;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
case ModelProvider.Ai360: {
|
|
240
|
+
runtimeModel = new LobeAi360AI(params.ai360 ?? {});
|
|
234
241
|
break
|
|
235
242
|
}
|
|
236
243
|
}
|
|
@@ -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 { LobeAi360AI } from './index';
|
|
13
|
+
|
|
14
|
+
const provider = ModelProvider.Ai360;
|
|
15
|
+
const defaultBaseURL = 'https://ai.360.cn/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 LobeAi360AI({ 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('LobeAi360AI', () => {
|
|
39
|
+
describe('init', () => {
|
|
40
|
+
it('should correctly initialize with an API key', async () => {
|
|
41
|
+
const instance = new LobeAi360AI({ apiKey: 'test_api_key' });
|
|
42
|
+
expect(instance).toBeInstanceOf(LobeAi360AI);
|
|
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: '360gpt-turbo',
|
|
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 LobeAi360AI({});
|
|
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: '360gpt-turbo',
|
|
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 LobeAi360AI({
|
|
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: '360gpt-turbo',
|
|
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 InvalidAi360APIKey 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: '360gpt-turbo',
|
|
171
|
+
temperature: 0,
|
|
172
|
+
});
|
|
173
|
+
} catch (e) {
|
|
174
|
+
// Expect the chat method to throw an error with InvalidAi360APIKey
|
|
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: '360gpt-turbo',
|
|
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_AI360_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_AI360_CHAT_COMPLETION;
|
|
232
|
+
|
|
233
|
+
// 模拟环境变量
|
|
234
|
+
process.env.DEBUG_AI360_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: '360gpt-turbo',
|
|
243
|
+
stream: true,
|
|
244
|
+
temperature: 0,
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
// 验证 debugStream 被调用
|
|
248
|
+
expect(debugStreamModule.debugStream).toHaveBeenCalled();
|
|
249
|
+
|
|
250
|
+
// 恢复原始环境变量值
|
|
251
|
+
process.env.DEBUG_AI360_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 LobeAi360AI = LobeOpenAICompatibleFactory({
|
|
5
|
+
baseURL: 'https://ai.360.cn/v1',
|
|
6
|
+
debug: {
|
|
7
|
+
chatCompletion: () => process.env.DEBUG_AI360_CHAT_COMPLETION === '1',
|
|
8
|
+
},
|
|
9
|
+
provider: ModelProvider.Ai360,
|
|
10
|
+
});
|
|
@@ -20,7 +20,7 @@ export class LobeAnthropicAI implements LobeRuntimeAI {
|
|
|
20
20
|
|
|
21
21
|
baseURL: string;
|
|
22
22
|
|
|
23
|
-
constructor({ apiKey, baseURL = DEFAULT_BASE_URL }: ClientOptions) {
|
|
23
|
+
constructor({ apiKey, baseURL = DEFAULT_BASE_URL }: ClientOptions = {}) {
|
|
24
24
|
if (!apiKey) throw AgentRuntimeError.createError(AgentRuntimeErrorType.InvalidProviderAPIKey);
|
|
25
25
|
|
|
26
26
|
this.client = new Anthropic({ apiKey, baseURL });
|
|
@@ -28,7 +28,7 @@ export class LobeBedrockAI implements LobeRuntimeAI {
|
|
|
28
28
|
|
|
29
29
|
region: string;
|
|
30
30
|
|
|
31
|
-
constructor({ region, accessKeyId, accessKeySecret }: LobeBedrockAIParams) {
|
|
31
|
+
constructor({ region, accessKeyId, accessKeySecret }: LobeBedrockAIParams = {}) {
|
|
32
32
|
if (!(accessKeyId && accessKeySecret))
|
|
33
33
|
throw AgentRuntimeError.createError(AgentRuntimeErrorType.InvalidBedrockCredentials);
|
|
34
34
|
|
|
@@ -41,7 +41,7 @@ export class LobeGoogleAI implements LobeRuntimeAI {
|
|
|
41
41
|
private client: GoogleGenerativeAI;
|
|
42
42
|
baseURL?: string;
|
|
43
43
|
|
|
44
|
-
constructor({ apiKey, baseURL }: { apiKey?: string; baseURL?: string }) {
|
|
44
|
+
constructor({ apiKey, baseURL }: { apiKey?: string; baseURL?: string } = {}) {
|
|
45
45
|
if (!apiKey) throw AgentRuntimeError.createError(AgentRuntimeErrorType.InvalidProviderAPIKey);
|
|
46
46
|
|
|
47
47
|
this.client = new GoogleGenerativeAI(apiKey);
|
|
@@ -63,7 +63,7 @@ function parseMinimaxResponse(chunk: string): MinimaxResponse | undefined {
|
|
|
63
63
|
export class LobeMinimaxAI implements LobeRuntimeAI {
|
|
64
64
|
apiKey: string;
|
|
65
65
|
|
|
66
|
-
constructor({ apiKey }: { apiKey?: string }) {
|
|
66
|
+
constructor({ apiKey }: { apiKey?: string } = {}) {
|
|
67
67
|
if (!apiKey) throw AgentRuntimeError.createError(AgentRuntimeErrorType.InvalidProviderAPIKey);
|
|
68
68
|
|
|
69
69
|
this.apiKey = apiKey;
|
|
@@ -27,13 +27,18 @@ const CHAT_MODELS_BLOCK_LIST = [
|
|
|
27
27
|
'dall-e',
|
|
28
28
|
];
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
type ConstructorOptions<T extends Record<string, any> = any> = ClientOptions & T;
|
|
31
|
+
|
|
32
|
+
interface OpenAICompatibleFactoryOptions<T extends Record<string, any> = any> {
|
|
31
33
|
baseURL?: string;
|
|
32
34
|
chatCompletion?: {
|
|
33
|
-
handleError?: (
|
|
35
|
+
handleError?: (
|
|
36
|
+
error: any,
|
|
37
|
+
options: ConstructorOptions<T>,
|
|
38
|
+
) => Omit<ChatCompletionErrorPayload, 'provider'> | undefined;
|
|
34
39
|
handlePayload?: (payload: ChatStreamPayload) => OpenAI.ChatCompletionCreateParamsStreaming;
|
|
35
40
|
};
|
|
36
|
-
constructorOptions?:
|
|
41
|
+
constructorOptions?: ConstructorOptions<T>;
|
|
37
42
|
debug?: {
|
|
38
43
|
chatCompletion: () => boolean;
|
|
39
44
|
};
|
|
@@ -49,7 +54,7 @@ interface OpenAICompatibleFactoryOptions {
|
|
|
49
54
|
provider: string;
|
|
50
55
|
}
|
|
51
56
|
|
|
52
|
-
export const LobeOpenAICompatibleFactory = ({
|
|
57
|
+
export const LobeOpenAICompatibleFactory = <T extends Record<string, any> = any>({
|
|
53
58
|
provider,
|
|
54
59
|
baseURL: DEFAULT_BASE_URL,
|
|
55
60
|
errorType,
|
|
@@ -57,7 +62,7 @@ export const LobeOpenAICompatibleFactory = ({
|
|
|
57
62
|
constructorOptions,
|
|
58
63
|
chatCompletion,
|
|
59
64
|
models,
|
|
60
|
-
}: OpenAICompatibleFactoryOptions) => {
|
|
65
|
+
}: OpenAICompatibleFactoryOptions<T>) => {
|
|
61
66
|
const ErrorType = {
|
|
62
67
|
bizError: errorType?.bizError || AgentRuntimeErrorType.ProviderBizError,
|
|
63
68
|
invalidAPIKey: errorType?.invalidAPIKey || AgentRuntimeErrorType.InvalidProviderAPIKey,
|
|
@@ -67,8 +72,11 @@ export const LobeOpenAICompatibleFactory = ({
|
|
|
67
72
|
client: OpenAI;
|
|
68
73
|
|
|
69
74
|
baseURL: string;
|
|
75
|
+
private _options: ConstructorOptions<T>;
|
|
70
76
|
|
|
71
|
-
constructor(
|
|
77
|
+
constructor(options: ClientOptions & Record<string, any> = {}) {
|
|
78
|
+
const { apiKey, baseURL = DEFAULT_BASE_URL, ...res } = options;
|
|
79
|
+
this._options = options as ConstructorOptions<T>;
|
|
72
80
|
if (!apiKey) throw AgentRuntimeError.createError(ErrorType?.invalidAPIKey);
|
|
73
81
|
|
|
74
82
|
this.client = new OpenAI({ apiKey, baseURL, ...constructorOptions, ...res });
|
|
@@ -115,48 +123,7 @@ export const LobeOpenAICompatibleFactory = ({
|
|
|
115
123
|
headers: options?.headers,
|
|
116
124
|
});
|
|
117
125
|
} catch (error) {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
// refs: https://github.com/lobehub/lobe-chat/issues/842
|
|
121
|
-
if (this.baseURL !== DEFAULT_BASE_URL) {
|
|
122
|
-
desensitizedEndpoint = desensitizeUrl(this.baseURL);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
if ('status' in (error as any)) {
|
|
126
|
-
switch ((error as Response).status) {
|
|
127
|
-
case 401: {
|
|
128
|
-
throw AgentRuntimeError.chat({
|
|
129
|
-
endpoint: desensitizedEndpoint,
|
|
130
|
-
error: error as any,
|
|
131
|
-
errorType: ErrorType.invalidAPIKey,
|
|
132
|
-
provider: provider as any,
|
|
133
|
-
});
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
default: {
|
|
137
|
-
break;
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
if (chatCompletion?.handleError) {
|
|
143
|
-
const errorResult = chatCompletion.handleError(error);
|
|
144
|
-
|
|
145
|
-
if (errorResult)
|
|
146
|
-
throw AgentRuntimeError.chat({
|
|
147
|
-
...errorResult,
|
|
148
|
-
provider,
|
|
149
|
-
} as ChatCompletionErrorPayload);
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
const { errorResult, RuntimeError } = handleOpenAIError(error);
|
|
153
|
-
|
|
154
|
-
throw AgentRuntimeError.chat({
|
|
155
|
-
endpoint: desensitizedEndpoint,
|
|
156
|
-
error: errorResult,
|
|
157
|
-
errorType: RuntimeError || ErrorType.bizError,
|
|
158
|
-
provider: provider as any,
|
|
159
|
-
});
|
|
126
|
+
throw this.handleError(error);
|
|
160
127
|
}
|
|
161
128
|
}
|
|
162
129
|
|
|
@@ -191,48 +158,7 @@ export const LobeOpenAICompatibleFactory = ({
|
|
|
191
158
|
const res = await this.client.images.generate(payload);
|
|
192
159
|
return res.data.map((o) => o.url) as string[];
|
|
193
160
|
} catch (error) {
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
// refs: https://github.com/lobehub/lobe-chat/issues/842
|
|
197
|
-
if (this.baseURL !== DEFAULT_BASE_URL) {
|
|
198
|
-
desensitizedEndpoint = desensitizeUrl(this.baseURL);
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
if ('status' in (error as any)) {
|
|
202
|
-
switch ((error as Response).status) {
|
|
203
|
-
case 401: {
|
|
204
|
-
throw AgentRuntimeError.chat({
|
|
205
|
-
endpoint: desensitizedEndpoint,
|
|
206
|
-
error: error as any,
|
|
207
|
-
errorType: ErrorType.invalidAPIKey,
|
|
208
|
-
provider: provider as any,
|
|
209
|
-
});
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
default: {
|
|
213
|
-
break;
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
if (chatCompletion?.handleError) {
|
|
219
|
-
const errorResult = chatCompletion.handleError(error);
|
|
220
|
-
|
|
221
|
-
if (errorResult)
|
|
222
|
-
throw AgentRuntimeError.chat({
|
|
223
|
-
...errorResult,
|
|
224
|
-
provider,
|
|
225
|
-
} as ChatCompletionErrorPayload);
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
const { errorResult, RuntimeError } = handleOpenAIError(error);
|
|
229
|
-
|
|
230
|
-
throw AgentRuntimeError.chat({
|
|
231
|
-
endpoint: desensitizedEndpoint,
|
|
232
|
-
error: errorResult,
|
|
233
|
-
errorType: RuntimeError || ErrorType.bizError,
|
|
234
|
-
provider: provider as any,
|
|
235
|
-
});
|
|
161
|
+
throw this.handleError(error);
|
|
236
162
|
}
|
|
237
163
|
}
|
|
238
164
|
|
|
@@ -289,5 +215,50 @@ export const LobeOpenAICompatibleFactory = ({
|
|
|
289
215
|
},
|
|
290
216
|
});
|
|
291
217
|
}
|
|
218
|
+
|
|
219
|
+
private handleError(error: any): ChatCompletionErrorPayload {
|
|
220
|
+
let desensitizedEndpoint = this.baseURL;
|
|
221
|
+
|
|
222
|
+
// refs: https://github.com/lobehub/lobe-chat/issues/842
|
|
223
|
+
if (this.baseURL !== DEFAULT_BASE_URL) {
|
|
224
|
+
desensitizedEndpoint = desensitizeUrl(this.baseURL);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if (chatCompletion?.handleError) {
|
|
228
|
+
const errorResult = chatCompletion.handleError(error, this._options);
|
|
229
|
+
|
|
230
|
+
if (errorResult)
|
|
231
|
+
return AgentRuntimeError.chat({
|
|
232
|
+
...errorResult,
|
|
233
|
+
provider,
|
|
234
|
+
} as ChatCompletionErrorPayload);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if ('status' in (error as any)) {
|
|
238
|
+
switch ((error as Response).status) {
|
|
239
|
+
case 401: {
|
|
240
|
+
return AgentRuntimeError.chat({
|
|
241
|
+
endpoint: desensitizedEndpoint,
|
|
242
|
+
error: error as any,
|
|
243
|
+
errorType: ErrorType.invalidAPIKey,
|
|
244
|
+
provider: provider as any,
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
default: {
|
|
249
|
+
break;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
const { errorResult, RuntimeError } = handleOpenAIError(error);
|
|
255
|
+
|
|
256
|
+
return AgentRuntimeError.chat({
|
|
257
|
+
endpoint: desensitizedEndpoint,
|
|
258
|
+
error: errorResult,
|
|
259
|
+
errorType: RuntimeError || ErrorType.bizError,
|
|
260
|
+
provider: provider as any,
|
|
261
|
+
});
|
|
262
|
+
}
|
|
292
263
|
};
|
|
293
264
|
};
|
|
@@ -29,7 +29,7 @@ export class LobeZhipuAI implements LobeRuntimeAI {
|
|
|
29
29
|
this.baseURL = this.client.baseURL;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
static async fromAPIKey({ apiKey, baseURL = DEFAULT_BASE_URL, ...res }: ClientOptions) {
|
|
32
|
+
static async fromAPIKey({ apiKey, baseURL = DEFAULT_BASE_URL, ...res }: ClientOptions = {}) {
|
|
33
33
|
const invalidZhipuAPIKey = AgentRuntimeError.createError(
|
|
34
34
|
AgentRuntimeErrorType.InvalidProviderAPIKey,
|
|
35
35
|
);
|
|
@@ -36,6 +36,7 @@ export const getServerGlobalConfig = () => {
|
|
|
36
36
|
ENABLED_STEPFUN,
|
|
37
37
|
ENABLED_BAICHUAN,
|
|
38
38
|
ENABLED_TAICHU,
|
|
39
|
+
ENABLED_AI360,
|
|
39
40
|
|
|
40
41
|
ENABLED_AZURE_OPENAI,
|
|
41
42
|
AZURE_MODEL_LIST,
|
|
@@ -60,6 +61,7 @@ export const getServerGlobalConfig = () => {
|
|
|
60
61
|
enabledAccessCode: ACCESS_CODES?.length > 0,
|
|
61
62
|
enabledOAuthSSO: enableNextAuth,
|
|
62
63
|
languageModel: {
|
|
64
|
+
ai360: { enabled: ENABLED_AI360 },
|
|
63
65
|
anthropic: {
|
|
64
66
|
enabled: ENABLED_ANTHROPIC,
|
|
65
67
|
},
|