@lobehub/chat 1.3.6 → 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 CHANGED
@@ -2,6 +2,31 @@
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
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
27
+
28
+ </div>
29
+
5
30
  ### [Version 1.3.6](https://github.com/lobehub/lobe-chat/compare/v1.3.5...v1.3.6)
6
31
 
7
32
  <sup>Released on **2024-07-11**</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>**293**</kbd> ](https://github.com/lobehub/lobe-chat-agents)
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>**293**</kbd> ](https://github.com/lobehub/lobe-chat-agents)
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
@@ -1,5 +1,5 @@
1
1
  [build]
2
- command = "npm run build"
2
+ command = "pnpm run build"
3
3
  publish = ".next"
4
4
 
5
5
  [build.environment]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/chat",
3
- "version": "1.3.6",
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
  }
@@ -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>;
@@ -233,6 +235,11 @@ class AgentRuntime {
233
235
  runtimeModel = new LobeTaichuAI(params.taichu);
234
236
  break;
235
237
  }
238
+
239
+ case ModelProvider.Ai360: {
240
+ runtimeModel = new LobeAi360AI(params.ai360 ?? {});
241
+ break
242
+ }
236
243
  }
237
244
 
238
245
  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 { 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
+ });
@@ -22,6 +22,7 @@ export interface CreateChatCompletionOptions {
22
22
  }
23
23
 
24
24
  export enum ModelProvider {
25
+ Ai360 = 'ai360',
25
26
  Anthropic = 'anthropic',
26
27
  Azure = 'azure',
27
28
  Baichuan = 'baichuan',
@@ -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
  },
@@ -16,6 +16,7 @@ export interface AWSBedrockKeyVault {
16
16
  }
17
17
 
18
18
  export interface UserKeyVaults {
19
+ ai360?: OpenAICompatibleKeyVault;
19
20
  anthropic?: OpenAICompatibleKeyVault;
20
21
  azure?: AzureOpenAIKeyVault;
21
22
  baichuan?: OpenAICompatibleKeyVault;