@lobehub/chat 0.156.2 → 0.157.1

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.
Files changed (155) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/package.json +32 -31
  3. package/src/app/api/chat/[provider]/route.test.ts +2 -2
  4. package/src/app/api/chat/[provider]/route.ts +1 -1
  5. package/src/app/api/chat/models/[provider]/route.ts +1 -1
  6. package/src/app/api/config.test.ts +1 -51
  7. package/src/app/api/openai/createBizOpenAI/auth.test.ts +52 -0
  8. package/src/app/api/openai/createBizOpenAI/index.ts +1 -1
  9. package/src/app/api/plugin/gateway/route.ts +1 -1
  10. package/src/app/api/text-to-image/[provider]/route.ts +61 -0
  11. package/src/components/GalleyGrid/index.tsx +2 -2
  12. package/src/config/modelProviders/anthropic.ts +3 -0
  13. package/src/config/modelProviders/google.ts +3 -0
  14. package/src/config/modelProviders/groq.ts +5 -1
  15. package/src/config/modelProviders/minimax.ts +10 -7
  16. package/src/config/modelProviders/mistral.ts +1 -0
  17. package/src/config/modelProviders/moonshot.ts +3 -0
  18. package/src/config/modelProviders/zhipu.ts +2 -6
  19. package/src/config/server/provider.ts +1 -1
  20. package/src/database/client/core/db.ts +32 -0
  21. package/src/database/client/core/schemas.ts +9 -0
  22. package/src/database/client/models/__tests__/message.test.ts +2 -2
  23. package/src/database/client/schemas/message.ts +10 -1
  24. package/src/features/AgentSetting/store/action.ts +15 -6
  25. package/src/features/Conversation/Actions/Assistant.tsx +3 -2
  26. package/src/features/Conversation/Actions/Tool.tsx +28 -0
  27. package/src/features/Conversation/Actions/index.ts +2 -2
  28. package/src/features/Conversation/Messages/Assistant/ToolCalls/index.tsx +73 -0
  29. package/src/features/Conversation/Messages/Assistant/ToolCalls/style.ts +25 -0
  30. package/src/features/Conversation/Messages/Assistant/index.tsx +51 -0
  31. package/src/features/Conversation/Messages/Default.tsx +4 -1
  32. package/src/features/Conversation/{Plugins → Messages/Tool}/Inspector/index.tsx +35 -36
  33. package/src/features/Conversation/Messages/Tool/index.tsx +44 -0
  34. package/src/features/Conversation/Messages/index.ts +3 -2
  35. package/src/features/Conversation/Plugins/Render/StandaloneType/Iframe.tsx +1 -1
  36. package/src/features/Conversation/Plugins/Render/index.tsx +11 -2
  37. package/src/features/Conversation/components/SkeletonList.tsx +2 -2
  38. package/src/features/Conversation/index.tsx +2 -3
  39. package/src/hooks/useTokenCount.test.ts +38 -0
  40. package/src/hooks/useTokenCount.ts +1 -2
  41. package/src/libs/agent-runtime/AgentRuntime.ts +9 -1
  42. package/src/libs/agent-runtime/BaseAI.ts +5 -9
  43. package/src/libs/agent-runtime/anthropic/index.test.ts +195 -0
  44. package/src/libs/agent-runtime/anthropic/index.ts +71 -15
  45. package/src/libs/agent-runtime/azureOpenai/index.ts +6 -5
  46. package/src/libs/agent-runtime/bedrock/index.ts +24 -18
  47. package/src/libs/agent-runtime/google/index.test.ts +154 -0
  48. package/src/libs/agent-runtime/google/index.ts +91 -10
  49. package/src/libs/agent-runtime/groq/index.test.ts +41 -72
  50. package/src/libs/agent-runtime/groq/index.ts +7 -0
  51. package/src/libs/agent-runtime/minimax/index.test.ts +2 -2
  52. package/src/libs/agent-runtime/minimax/index.ts +14 -37
  53. package/src/libs/agent-runtime/mistral/index.test.ts +0 -53
  54. package/src/libs/agent-runtime/mistral/index.ts +1 -0
  55. package/src/libs/agent-runtime/moonshot/index.test.ts +1 -71
  56. package/src/libs/agent-runtime/ollama/index.test.ts +197 -0
  57. package/src/libs/agent-runtime/ollama/index.ts +3 -3
  58. package/src/libs/agent-runtime/openai/index.test.ts +0 -53
  59. package/src/libs/agent-runtime/openrouter/index.test.ts +1 -53
  60. package/src/libs/agent-runtime/perplexity/index.test.ts +0 -71
  61. package/src/libs/agent-runtime/perplexity/index.ts +2 -3
  62. package/src/libs/agent-runtime/togetherai/__snapshots__/index.test.ts.snap +886 -0
  63. package/src/libs/agent-runtime/togetherai/fixtures/models.json +8111 -0
  64. package/src/libs/agent-runtime/togetherai/index.test.ts +16 -54
  65. package/src/libs/agent-runtime/types/chat.ts +19 -3
  66. package/src/libs/agent-runtime/types/index.ts +1 -0
  67. package/src/libs/agent-runtime/types/textToImage.ts +34 -0
  68. package/src/libs/agent-runtime/utils/anthropicHelpers.test.ts +120 -1
  69. package/src/libs/agent-runtime/utils/anthropicHelpers.ts +67 -4
  70. package/src/libs/agent-runtime/utils/createError.ts +1 -0
  71. package/src/libs/agent-runtime/utils/debugStream.test.ts +70 -0
  72. package/src/libs/agent-runtime/utils/debugStream.ts +39 -9
  73. package/src/libs/agent-runtime/utils/openaiCompatibleFactory/index.test.ts +521 -0
  74. package/src/libs/agent-runtime/utils/openaiCompatibleFactory/index.ts +127 -5
  75. package/src/libs/agent-runtime/utils/response.ts +12 -0
  76. package/src/libs/agent-runtime/utils/streams/anthropic.test.ts +197 -0
  77. package/src/libs/agent-runtime/utils/streams/anthropic.ts +91 -0
  78. package/src/libs/agent-runtime/utils/streams/bedrock/claude.ts +21 -0
  79. package/src/libs/agent-runtime/utils/streams/bedrock/common.ts +32 -0
  80. package/src/libs/agent-runtime/utils/streams/bedrock/index.ts +3 -0
  81. package/src/libs/agent-runtime/utils/streams/bedrock/llama.test.ts +196 -0
  82. package/src/libs/agent-runtime/utils/streams/bedrock/llama.ts +51 -0
  83. package/src/libs/agent-runtime/utils/streams/google-ai.test.ts +97 -0
  84. package/src/libs/agent-runtime/utils/streams/google-ai.ts +68 -0
  85. package/src/libs/agent-runtime/utils/streams/index.ts +7 -0
  86. package/src/libs/agent-runtime/utils/streams/minimax.ts +39 -0
  87. package/src/libs/agent-runtime/utils/streams/ollama.test.ts +77 -0
  88. package/src/libs/agent-runtime/utils/streams/ollama.ts +38 -0
  89. package/src/libs/agent-runtime/utils/streams/openai.test.ts +263 -0
  90. package/src/libs/agent-runtime/utils/streams/openai.ts +79 -0
  91. package/src/libs/agent-runtime/utils/streams/protocol.ts +100 -0
  92. package/src/libs/agent-runtime/zeroone/index.test.ts +1 -53
  93. package/src/libs/agent-runtime/zhipu/index.test.ts +1 -1
  94. package/src/libs/agent-runtime/zhipu/index.ts +3 -2
  95. package/src/locales/default/plugin.ts +3 -4
  96. package/src/locales/default/tool.ts +1 -0
  97. package/src/migrations/FromV4ToV5/fixtures/from-v1-to-v5-output.json +245 -0
  98. package/src/migrations/FromV4ToV5/fixtures/function-input-v4.json +96 -0
  99. package/src/migrations/FromV4ToV5/fixtures/function-output-v5.json +120 -0
  100. package/src/migrations/FromV4ToV5/index.ts +58 -0
  101. package/src/migrations/FromV4ToV5/migrations.test.ts +49 -0
  102. package/src/migrations/FromV4ToV5/types/v4.ts +21 -0
  103. package/src/migrations/FromV4ToV5/types/v5.ts +27 -0
  104. package/src/migrations/index.ts +8 -1
  105. package/src/services/__tests__/chat.test.ts +10 -20
  106. package/src/services/_url.ts +1 -1
  107. package/src/services/chat.ts +78 -65
  108. package/src/services/{imageGeneration.ts → textToImage.ts} +11 -2
  109. package/src/store/chat/initialState.ts +1 -1
  110. package/src/store/chat/selectors.ts +1 -1
  111. package/src/store/chat/slices/{tool → builtinTool}/action.test.ts +1 -1
  112. package/src/store/chat/slices/{tool → builtinTool}/action.ts +16 -4
  113. package/src/store/chat/slices/enchance/action.ts +25 -21
  114. package/src/store/chat/slices/message/action.test.ts +36 -86
  115. package/src/store/chat/slices/message/action.ts +98 -169
  116. package/src/store/chat/slices/message/initialState.ts +5 -0
  117. package/src/store/chat/slices/message/reducer.ts +18 -1
  118. package/src/store/chat/slices/message/selectors.test.ts +38 -68
  119. package/src/store/chat/slices/message/selectors.ts +9 -22
  120. package/src/store/chat/slices/plugin/action.test.ts +148 -204
  121. package/src/store/chat/slices/plugin/action.ts +163 -134
  122. package/src/store/chat/slices/share/action.test.ts +3 -3
  123. package/src/store/chat/slices/share/action.ts +1 -1
  124. package/src/store/chat/slices/topic/action.ts +7 -2
  125. package/src/store/chat/store.ts +2 -2
  126. package/src/store/tool/selectors/tool.ts +6 -24
  127. package/src/store/tool/slices/builtin/action.test.ts +90 -0
  128. package/src/store/tool/slices/store/action.test.ts +6 -2
  129. package/src/store/tool/slices/store/action.ts +3 -1
  130. package/src/tools/dalle/Render/Item/Error.tsx +50 -0
  131. package/src/tools/dalle/Render/Item/Image.tsx +44 -0
  132. package/src/tools/dalle/Render/{Item.tsx → Item/index.tsx} +20 -29
  133. package/src/types/llm.ts +1 -1
  134. package/src/types/message/index.ts +9 -4
  135. package/src/types/message/tools.ts +57 -0
  136. package/src/types/openai/chat.ts +6 -0
  137. package/src/utils/fetch.test.ts +450 -1
  138. package/src/utils/fetch.ts +338 -39
  139. package/src/utils/toolCall.ts +21 -0
  140. package/src/app/api/openai/images/createImageGeneration.ts +0 -26
  141. package/src/app/api/openai/images/route.ts +0 -16
  142. package/src/features/Conversation/Actions/Function.tsx +0 -17
  143. package/src/features/Conversation/Messages/Assistant.tsx +0 -26
  144. package/src/features/Conversation/Messages/Function.tsx +0 -35
  145. package/src/libs/agent-runtime/ollama/stream.ts +0 -31
  146. /package/src/app/api/{chat → middleware}/auth/index.test.ts +0 -0
  147. /package/src/app/api/{chat → middleware}/auth/index.ts +0 -0
  148. /package/src/app/api/{chat → middleware}/auth/utils.ts +0 -0
  149. /package/src/app/api/{auth.ts → openai/createBizOpenAI/auth.ts} +0 -0
  150. /package/src/features/Conversation/{Plugins → Messages/Tool}/Inspector/PluginResultJSON.tsx +0 -0
  151. /package/src/features/Conversation/{Plugins → Messages/Tool}/Inspector/Settings.tsx +0 -0
  152. /package/src/features/Conversation/{Plugins → Messages/Tool}/Inspector/style.ts +0 -0
  153. /package/src/store/chat/slices/{tool → builtinTool}/initialState.ts +0 -0
  154. /package/src/store/chat/slices/{tool → builtinTool}/selectors.ts +0 -0
  155. /package/src/tools/dalle/Render/{EditMode.tsx → Item/EditMode.tsx} +0 -0
@@ -40,24 +40,6 @@ describe('LobeMoonshotAI', () => {
40
40
  });
41
41
 
42
42
  describe('chat', () => {
43
- it('should return a StreamingTextResponse on successful API call', async () => {
44
- // Arrange
45
- const mockStream = new ReadableStream();
46
- const mockResponse = Promise.resolve(mockStream);
47
-
48
- (instance['client'].chat.completions.create as Mock).mockResolvedValue(mockResponse);
49
-
50
- // Act
51
- const result = await instance.chat({
52
- messages: [{ content: 'Hello', role: 'user' }],
53
- model: 'text-davinci-003',
54
- temperature: 0,
55
- });
56
-
57
- // Assert
58
- expect(result).toBeInstanceOf(Response);
59
- });
60
-
61
43
  describe('Error', () => {
62
44
  it('should return OpenAIBizError with an openai error response when OpenAI.APIError is thrown', async () => {
63
45
  // Arrange
@@ -223,59 +205,6 @@ describe('LobeMoonshotAI', () => {
223
205
  });
224
206
  });
225
207
 
226
- describe('LobeMoonshotAI chat with callback and headers', () => {
227
- it('should handle callback and headers correctly', async () => {
228
- // 模拟 chat.completions.create 方法返回一个可读流
229
- const mockCreateMethod = vi
230
- .spyOn(instance['client'].chat.completions, 'create')
231
- .mockResolvedValue(
232
- new ReadableStream({
233
- start(controller) {
234
- controller.enqueue({
235
- id: 'chatcmpl-8xDx5AETP8mESQN7UB30GxTN2H1SO',
236
- object: 'chat.completion.chunk',
237
- created: 1709125675,
238
- model: 'gpt-3.5-turbo-0125',
239
- system_fingerprint: 'fp_86156a94a0',
240
- choices: [
241
- { index: 0, delta: { content: 'hello' }, logprobs: null, finish_reason: null },
242
- ],
243
- });
244
- controller.close();
245
- },
246
- }) as any,
247
- );
248
-
249
- // 准备 callback 和 headers
250
- const mockCallback: ChatStreamCallbacks = {
251
- onStart: vi.fn(),
252
- onToken: vi.fn(),
253
- };
254
- const mockHeaders = { 'Custom-Header': 'TestValue' };
255
-
256
- // 执行测试
257
- const result = await instance.chat(
258
- {
259
- messages: [{ content: 'Hello', role: 'user' }],
260
- model: 'text-davinci-003',
261
- temperature: 0,
262
- },
263
- { callback: mockCallback, headers: mockHeaders },
264
- );
265
-
266
- // 验证 callback 被调用
267
- await result.text(); // 确保流被消费
268
- expect(mockCallback.onStart).toHaveBeenCalled();
269
- expect(mockCallback.onToken).toHaveBeenCalledWith('hello');
270
-
271
- // 验证 headers 被正确传递
272
- expect(result.headers.get('Custom-Header')).toEqual('TestValue');
273
-
274
- // 清理
275
- mockCreateMethod.mockRestore();
276
- });
277
- });
278
-
279
208
  describe('DEBUG', () => {
280
209
  it('should call debugStream and return StreamingTextResponse when DEBUG_MOONSHOT_CHAT_COMPLETION is 1', async () => {
281
210
  // Arrange
@@ -306,6 +235,7 @@ describe('LobeMoonshotAI', () => {
306
235
  await instance.chat({
307
236
  messages: [{ content: 'Hello', role: 'user' }],
308
237
  model: 'text-davinci-003',
238
+ stream: true,
309
239
  temperature: 0,
310
240
  });
311
241
 
@@ -0,0 +1,197 @@
1
+ import { Ollama } from 'ollama/browser';
2
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
3
+
4
+ import { AgentRuntimeErrorType } from '../error';
5
+ import { ModelProvider } from '../types';
6
+ import { AgentRuntimeError } from '../utils/createError';
7
+ import { LobeOllamaAI } from './index';
8
+
9
+ vi.mock('ollama/browser');
10
+
11
+ describe('LobeOllamaAI', () => {
12
+ let ollamaAI: LobeOllamaAI;
13
+
14
+ beforeEach(() => {
15
+ ollamaAI = new LobeOllamaAI({ baseURL: 'https://example.com' });
16
+ });
17
+
18
+ afterEach(() => {
19
+ vi.resetAllMocks();
20
+ });
21
+
22
+ describe('constructor', () => {
23
+ it('should initialize Ollama client and baseURL with valid baseURL', () => {
24
+ expect(ollamaAI['client']).toBeInstanceOf(Ollama);
25
+ expect(ollamaAI.baseURL).toBe('https://example.com');
26
+ });
27
+
28
+ it('should throw AgentRuntimeError with invalid baseURL', () => {
29
+ try {
30
+ new LobeOllamaAI({ baseURL: 'invalid-url' });
31
+ } catch (e) {
32
+ expect(e).toEqual(AgentRuntimeError.createError(AgentRuntimeErrorType.InvalidOllamaArgs));
33
+ }
34
+ });
35
+ });
36
+
37
+ describe('chat', () => {
38
+ it('should call Ollama client chat method and return StreamingResponse', async () => {
39
+ const chatMock = vi.fn().mockResolvedValue({});
40
+ vi.mocked(Ollama.prototype.chat).mockImplementation(chatMock);
41
+
42
+ const payload = {
43
+ messages: [{ content: 'Hello', role: 'user' }],
44
+ model: 'model-id',
45
+ };
46
+ const options = { signal: new AbortController().signal };
47
+
48
+ const response = await ollamaAI.chat(payload as any, options);
49
+
50
+ expect(chatMock).toHaveBeenCalledWith({
51
+ messages: [{ content: 'Hello', role: 'user' }],
52
+ model: 'model-id',
53
+ options: {
54
+ frequency_penalty: undefined,
55
+ presence_penalty: undefined,
56
+ temperature: undefined,
57
+ top_p: undefined,
58
+ },
59
+ stream: true,
60
+ });
61
+ expect(response).toBeInstanceOf(Response);
62
+ });
63
+
64
+ it('should throw AgentRuntimeError when Ollama client chat method throws an error', async () => {
65
+ const errorMock = {
66
+ message: 'Chat error',
67
+ name: 'ChatError',
68
+ status_code: 500,
69
+ };
70
+ vi.mocked(Ollama.prototype.chat).mockRejectedValue(errorMock);
71
+
72
+ const payload = {
73
+ messages: [{ content: 'Hello', role: 'user' }],
74
+ model: 'model-id',
75
+ };
76
+
77
+ try {
78
+ await ollamaAI.chat(payload as any);
79
+ } catch (e) {
80
+ expect(e).toEqual(
81
+ AgentRuntimeError.chat({
82
+ error: errorMock,
83
+ errorType: AgentRuntimeErrorType.OllamaBizError,
84
+ provider: ModelProvider.Ollama,
85
+ }),
86
+ );
87
+ }
88
+ });
89
+
90
+ it('should abort the request when signal aborts', async () => {
91
+ const abortMock = vi.fn();
92
+ vi.mocked(Ollama.prototype.abort).mockImplementation(abortMock);
93
+
94
+ const payload = {
95
+ messages: [{ content: 'Hello', role: 'user' }],
96
+ model: 'model-id',
97
+ };
98
+ const options = { signal: new AbortController().signal };
99
+
100
+ ollamaAI.chat(payload as any, options);
101
+
102
+ options.signal.dispatchEvent(new Event('abort'));
103
+
104
+ expect(abortMock).toHaveBeenCalled();
105
+ });
106
+ });
107
+
108
+ describe('models', () => {
109
+ it('should call Ollama client list method and return ChatModelCard array', async () => {
110
+ const listMock = vi.fn().mockResolvedValue({
111
+ models: [{ name: 'model-1' }, { name: 'model-2' }],
112
+ });
113
+ vi.mocked(Ollama.prototype.list).mockImplementation(listMock);
114
+
115
+ const models = await ollamaAI.models();
116
+
117
+ expect(listMock).toHaveBeenCalled();
118
+ expect(models).toEqual([{ id: 'model-1' }, { id: 'model-2' }]);
119
+ });
120
+ });
121
+
122
+ describe('buildOllamaMessages', () => {
123
+ it('should convert OpenAIChatMessage array to OllamaMessage array', () => {
124
+ const messages = [
125
+ { content: 'Hello', role: 'user' },
126
+ { content: 'Hi there!', role: 'assistant' },
127
+ ];
128
+
129
+ const ollamaMessages = ollamaAI['buildOllamaMessages'](messages as any);
130
+
131
+ expect(ollamaMessages).toEqual([
132
+ { content: 'Hello', role: 'user' },
133
+ { content: 'Hi there!', role: 'assistant' },
134
+ ]);
135
+ });
136
+ });
137
+
138
+ describe('convertContentToOllamaMessage', () => {
139
+ it('should convert string content to OllamaMessage', () => {
140
+ const message = { content: 'Hello', role: 'user' };
141
+
142
+ const ollamaMessage = ollamaAI['convertContentToOllamaMessage'](message as any);
143
+
144
+ expect(ollamaMessage).toEqual({ content: 'Hello', role: 'user' });
145
+ });
146
+
147
+ it('should convert text content to OllamaMessage', () => {
148
+ const message = {
149
+ content: [{ type: 'text', text: 'Hello' }],
150
+ role: 'user',
151
+ };
152
+
153
+ const ollamaMessage = ollamaAI['convertContentToOllamaMessage'](message as any);
154
+
155
+ expect(ollamaMessage).toEqual({ content: 'Hello', role: 'user' });
156
+ });
157
+
158
+ it('should convert image_url content to OllamaMessage with images', () => {
159
+ const message = {
160
+ content: [
161
+ {
162
+ type: 'image_url',
163
+ image_url: { url: 'data:image/png;base64,abc123' },
164
+ },
165
+ ],
166
+ role: 'user',
167
+ };
168
+
169
+ const ollamaMessage = ollamaAI['convertContentToOllamaMessage'](message as any);
170
+
171
+ expect(ollamaMessage).toEqual({
172
+ content: '',
173
+ role: 'user',
174
+ images: ['abc123'],
175
+ });
176
+ });
177
+
178
+ it('should ignore invalid image_url content', () => {
179
+ const message = {
180
+ content: [
181
+ {
182
+ type: 'image_url',
183
+ image_url: { url: 'invalid-url' },
184
+ },
185
+ ],
186
+ role: 'user',
187
+ };
188
+
189
+ const ollamaMessage = ollamaAI['convertContentToOllamaMessage'](message as any);
190
+
191
+ expect(ollamaMessage).toEqual({
192
+ content: '',
193
+ role: 'user',
194
+ });
195
+ });
196
+ });
197
+ });
@@ -1,15 +1,15 @@
1
- import { StreamingTextResponse } from 'ai';
2
1
  import { Ollama } from 'ollama/browser';
3
2
  import { ClientOptions } from 'openai';
4
3
 
5
4
  import { OpenAIChatMessage } from '@/libs/agent-runtime';
6
- import { OllamaStream } from '@/libs/agent-runtime/ollama/stream';
7
5
  import { ChatModelCard } from '@/types/llm';
8
6
 
9
7
  import { LobeRuntimeAI } from '../BaseAI';
10
8
  import { AgentRuntimeErrorType } from '../error';
11
9
  import { ChatCompetitionOptions, ChatStreamPayload, ModelProvider } from '../types';
12
10
  import { AgentRuntimeError } from '../utils/createError';
11
+ import { StreamingResponse } from '../utils/response';
12
+ import { OllamaStream } from '../utils/streams';
13
13
  import { parseDataUri } from '../utils/uriParser';
14
14
  import { OllamaMessage } from './type';
15
15
 
@@ -51,7 +51,7 @@ export class LobeOllamaAI implements LobeRuntimeAI {
51
51
  stream: true,
52
52
  });
53
53
 
54
- return new StreamingTextResponse(OllamaStream(response, options?.callback), {
54
+ return StreamingResponse(OllamaStream(response, options?.callback), {
55
55
  headers: options?.headers,
56
56
  });
57
57
  } catch (error) {
@@ -190,59 +190,6 @@ describe('LobeOpenAI', () => {
190
190
  });
191
191
  });
192
192
 
193
- describe('LobeOpenAI chat with callback and headers', () => {
194
- it('should handle callback and headers correctly', async () => {
195
- // 模拟 chat.completions.create 方法返回一个可读流
196
- const mockCreateMethod = vi
197
- .spyOn(instance['client'].chat.completions, 'create')
198
- .mockResolvedValue(
199
- new ReadableStream({
200
- start(controller) {
201
- controller.enqueue({
202
- id: 'chatcmpl-8xDx5AETP8mESQN7UB30GxTN2H1SO',
203
- object: 'chat.completion.chunk',
204
- created: 1709125675,
205
- model: 'gpt-3.5-turbo-0125',
206
- system_fingerprint: 'fp_86156a94a0',
207
- choices: [
208
- { index: 0, delta: { content: 'hello' }, logprobs: null, finish_reason: null },
209
- ],
210
- });
211
- controller.close();
212
- },
213
- }) as any,
214
- );
215
-
216
- // 准备 callback 和 headers
217
- const mockCallback: ChatStreamCallbacks = {
218
- onStart: vi.fn(),
219
- onToken: vi.fn(),
220
- };
221
- const mockHeaders = { 'Custom-Header': 'TestValue' };
222
-
223
- // 执行测试
224
- const result = await instance.chat(
225
- {
226
- messages: [{ content: 'Hello', role: 'user' }],
227
- model: 'text-davinci-003',
228
- temperature: 0,
229
- },
230
- { callback: mockCallback, headers: mockHeaders },
231
- );
232
-
233
- // 验证 callback 被调用
234
- await result.text(); // 确保流被消费
235
- expect(mockCallback.onStart).toHaveBeenCalled();
236
- expect(mockCallback.onToken).toHaveBeenCalledWith('hello');
237
-
238
- // 验证 headers 被正确传递
239
- expect(result.headers.get('Custom-Header')).toEqual('TestValue');
240
-
241
- // 清理
242
- mockCreateMethod.mockRestore();
243
- });
244
- });
245
-
246
193
  describe('DEBUG', () => {
247
194
  it('should call debugStream and return StreamingTextResponse when DEBUG_OPENAI_CHAT_COMPLETION is 1', async () => {
248
195
  // Arrange
@@ -81,6 +81,7 @@ describe('LobeOpenRouterAI', () => {
81
81
  {
82
82
  max_tokens: 1024,
83
83
  messages: [{ content: 'Hello', role: 'user' }],
84
+ stream: true,
84
85
  model: 'mistralai/mistral-7b-instruct:free',
85
86
  temperature: 0.7,
86
87
  top_p: 1,
@@ -255,59 +256,6 @@ describe('LobeOpenRouterAI', () => {
255
256
  });
256
257
  });
257
258
 
258
- describe('LobeOpenRouterAI chat with callback and headers', () => {
259
- it('should handle callback and headers correctly', async () => {
260
- // 模拟 chat.completions.create 方法返回一个可读流
261
- const mockCreateMethod = vi
262
- .spyOn(instance['client'].chat.completions, 'create')
263
- .mockResolvedValue(
264
- new ReadableStream({
265
- start(controller) {
266
- controller.enqueue({
267
- id: 'chatcmpl-8xDx5AETP8mESQN7UB30GxTN2H1SO',
268
- object: 'chat.completion.chunk',
269
- created: 1709125675,
270
- model: 'mistralai/mistral-7b-instruct:free',
271
- system_fingerprint: 'fp_86156a94a0',
272
- choices: [
273
- { index: 0, delta: { content: 'hello' }, logprobs: null, finish_reason: null },
274
- ],
275
- });
276
- controller.close();
277
- },
278
- }) as any,
279
- );
280
-
281
- // 准备 callback 和 headers
282
- const mockCallback: ChatStreamCallbacks = {
283
- onStart: vi.fn(),
284
- onToken: vi.fn(),
285
- };
286
- const mockHeaders = { 'Custom-Header': 'TestValue' };
287
-
288
- // 执行测试
289
- const result = await instance.chat(
290
- {
291
- messages: [{ content: 'Hello', role: 'user' }],
292
- model: 'mistralai/mistral-7b-instruct:free',
293
- temperature: 0,
294
- },
295
- { callback: mockCallback, headers: mockHeaders },
296
- );
297
-
298
- // 验证 callback 被调用
299
- await result.text(); // 确保流被消费
300
- expect(mockCallback.onStart).toHaveBeenCalled();
301
- expect(mockCallback.onToken).toHaveBeenCalledWith('hello');
302
-
303
- // 验证 headers 被正确传递
304
- expect(result.headers.get('Custom-Header')).toEqual('TestValue');
305
-
306
- // 清理
307
- mockCreateMethod.mockRestore();
308
- });
309
- });
310
-
311
259
  describe('DEBUG', () => {
312
260
  it('should call debugStream and return StreamingTextResponse when DEBUG_OPENROUTER_CHAT_COMPLETION is 1', async () => {
313
261
  // Arrange
@@ -40,24 +40,6 @@ describe('LobePerplexityAI', () => {
40
40
  });
41
41
 
42
42
  describe('chat', () => {
43
- it('should return a StreamingTextResponse on successful API call', async () => {
44
- // Arrange
45
- const mockStream = new ReadableStream();
46
- const mockResponse = Promise.resolve(mockStream);
47
-
48
- (instance['client'].chat.completions.create as Mock).mockResolvedValue(mockResponse);
49
-
50
- // Act
51
- const result = await instance.chat({
52
- messages: [{ content: 'Hello', role: 'user' }],
53
- model: 'text-davinci-003',
54
- temperature: 0,
55
- });
56
-
57
- // Assert
58
- expect(result).toBeInstanceOf(Response);
59
- });
60
-
61
43
  describe('Error', () => {
62
44
  it('should return OpenAIBizError with an openai error response when OpenAI.APIError is thrown', async () => {
63
45
  // Arrange
@@ -223,59 +205,6 @@ describe('LobePerplexityAI', () => {
223
205
  });
224
206
  });
225
207
 
226
- describe('LobePerplexityAI chat with callback and headers', () => {
227
- it('should handle callback and headers correctly', async () => {
228
- // 模拟 chat.completions.create 方法返回一个可读流
229
- const mockCreateMethod = vi
230
- .spyOn(instance['client'].chat.completions, 'create')
231
- .mockResolvedValue(
232
- new ReadableStream({
233
- start(controller) {
234
- controller.enqueue({
235
- id: 'chatcmpl-8xDx5AETP8mESQN7UB30GxTN2H1SO',
236
- object: 'chat.completion.chunk',
237
- created: 1709125675,
238
- model: 'gpt-3.5-turbo-0125',
239
- system_fingerprint: 'fp_86156a94a0',
240
- choices: [
241
- { index: 0, delta: { content: 'hello' }, logprobs: null, finish_reason: null },
242
- ],
243
- });
244
- controller.close();
245
- },
246
- }) as any,
247
- );
248
-
249
- // 准备 callback 和 headers
250
- const mockCallback: ChatStreamCallbacks = {
251
- onStart: vi.fn(),
252
- onToken: vi.fn(),
253
- };
254
- const mockHeaders = { 'Custom-Header': 'TestValue' };
255
-
256
- // 执行测试
257
- const result = await instance.chat(
258
- {
259
- messages: [{ content: 'Hello', role: 'user' }],
260
- model: 'text-davinci-003',
261
- temperature: 0,
262
- },
263
- { callback: mockCallback, headers: mockHeaders },
264
- );
265
-
266
- // 验证 callback 被调用
267
- await result.text(); // 确保流被消费
268
- expect(mockCallback.onStart).toHaveBeenCalled();
269
- expect(mockCallback.onToken).toHaveBeenCalledWith('hello');
270
-
271
- // 验证 headers 被正确传递
272
- expect(result.headers.get('Custom-Header')).toEqual('TestValue');
273
-
274
- // 清理
275
- mockCreateMethod.mockRestore();
276
- });
277
- });
278
-
279
208
  describe('DEBUG', () => {
280
209
  it('should call debugStream and return StreamingTextResponse when DEBUG_PERPLEXITY_CHAT_COMPLETION is 1', async () => {
281
210
  // Arrange
@@ -9,7 +9,7 @@ export const LobePerplexityAI = LobeOpenAICompatibleFactory({
9
9
  chatCompletion: {
10
10
  handlePayload: (payload: ChatStreamPayload) => {
11
11
  // Set a default frequency penalty value greater than 0
12
- const { presence_penalty, frequency_penalty, ...res } = payload;
12
+ const { presence_penalty, frequency_penalty, stream = true, ...res } = payload;
13
13
 
14
14
  let param;
15
15
 
@@ -22,8 +22,7 @@ export const LobePerplexityAI = LobeOpenAICompatibleFactory({
22
22
  param = { frequency_penalty: frequency_penalty || defaultFrequencyPenalty };
23
23
  }
24
24
 
25
- console.log(param);
26
- return { ...res, ...param } as OpenAI.ChatCompletionCreateParamsStreaming;
25
+ return { ...res, ...param, stream } as OpenAI.ChatCompletionCreateParamsStreaming;
27
26
  },
28
27
  },
29
28
  debug: {