@lobehub/lobehub 2.0.0-next.44 → 2.0.0-next.46
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/changelog/v1.json +18 -0
- package/package.json +1 -1
- package/packages/model-runtime/src/providers/azureai/index.ts +34 -2
- package/packages/utils/src/server/__tests__/response.test.ts +79 -0
- package/packages/utils/src/server/index.ts +1 -0
- package/packages/utils/src/server/response.ts +110 -0
- package/src/app/(backend)/webapi/stt/openai/route.ts +0 -2
- package/src/app/(backend)/webapi/tts/edge/route.ts +8 -2
- package/src/app/(backend)/webapi/tts/microsoft/route.ts +8 -2
- package/src/app/(backend)/webapi/tts/openai/route.ts +15 -3
- package/src/app/[variants]/(main)/chat/components/conversation/features/ChatList/ChatItem/Thread.tsx +1 -1
- package/src/app/[variants]/(main)/chat/components/conversation/features/ChatList/ChatItem/index.tsx +9 -16
- package/src/features/Conversation/Messages/Assistant/Actions/index.tsx +3 -5
- package/src/features/Conversation/Messages/Assistant/Extra/index.test.tsx +3 -3
- package/src/features/Conversation/Messages/Assistant/Extra/index.tsx +8 -5
- package/src/features/Conversation/Messages/Assistant/index.tsx +29 -15
- package/src/features/Conversation/Messages/Group/Actions/WithContentId.tsx +3 -5
- package/src/features/Conversation/Messages/Group/index.tsx +12 -20
- package/src/features/Conversation/Messages/Supervisor/index.tsx +14 -5
- package/src/features/Conversation/Messages/User/index.tsx +14 -8
- package/src/features/Conversation/Messages/index.tsx +16 -26
- package/src/features/Conversation/components/Extras/Usage/UsageDetail/index.tsx +7 -6
- package/src/features/Conversation/components/Extras/Usage/UsageDetail/tokens.ts +2 -5
- package/src/features/Conversation/components/Extras/Usage/index.tsx +13 -6
- package/src/server/modules/ContentChunk/index.test.ts +372 -0
- package/src/server/utils/createSpeechResponse.ts +55 -0
- package/src/app/(backend)/webapi/chat/azureai/route.test.ts +0 -25
- package/src/app/(backend)/webapi/chat/azureai/route.ts +0 -6
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import type { NewChunkItem, NewUnstructuredChunkItem } from '@/database/schemas';
|
|
4
|
+
import { knowledgeEnv } from '@/envs/knowledge';
|
|
5
|
+
import { ChunkingLoader } from '@/libs/langchain';
|
|
6
|
+
import { ChunkingStrategy, Unstructured } from '@/libs/unstructured';
|
|
7
|
+
|
|
8
|
+
import { ContentChunk } from './index';
|
|
9
|
+
|
|
10
|
+
// Mock the dependencies
|
|
11
|
+
vi.mock('@/libs/unstructured');
|
|
12
|
+
vi.mock('@/libs/langchain');
|
|
13
|
+
vi.mock('@/envs/knowledge', () => ({
|
|
14
|
+
knowledgeEnv: {
|
|
15
|
+
FILE_TYPE_CHUNKING_RULES: '',
|
|
16
|
+
UNSTRUCTURED_API_KEY: 'test-api-key',
|
|
17
|
+
UNSTRUCTURED_SERVER_URL: 'https://test.unstructured.io',
|
|
18
|
+
},
|
|
19
|
+
}));
|
|
20
|
+
|
|
21
|
+
describe('ContentChunk', () => {
|
|
22
|
+
let contentChunk: ContentChunk;
|
|
23
|
+
let mockUnstructuredPartition: ReturnType<typeof vi.fn>;
|
|
24
|
+
let mockLangChainPartition: ReturnType<typeof vi.fn>;
|
|
25
|
+
|
|
26
|
+
beforeEach(() => {
|
|
27
|
+
vi.clearAllMocks();
|
|
28
|
+
|
|
29
|
+
// Setup Unstructured mock
|
|
30
|
+
mockUnstructuredPartition = vi.fn();
|
|
31
|
+
(Unstructured as unknown as ReturnType<typeof vi.fn>).mockImplementation(() => ({
|
|
32
|
+
partition: mockUnstructuredPartition,
|
|
33
|
+
}));
|
|
34
|
+
|
|
35
|
+
// Setup LangChain mock
|
|
36
|
+
mockLangChainPartition = vi.fn();
|
|
37
|
+
(ChunkingLoader as unknown as ReturnType<typeof vi.fn>).mockImplementation(() => ({
|
|
38
|
+
partitionContent: mockLangChainPartition,
|
|
39
|
+
}));
|
|
40
|
+
|
|
41
|
+
contentChunk = new ContentChunk();
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
describe('constructor', () => {
|
|
45
|
+
it('should initialize with Unstructured and LangChain clients', () => {
|
|
46
|
+
expect(Unstructured).toHaveBeenCalledTimes(1);
|
|
47
|
+
expect(ChunkingLoader).toHaveBeenCalledTimes(1);
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
describe('chunkContent', () => {
|
|
52
|
+
const mockFileContent = new Uint8Array([1, 2, 3, 4, 5]);
|
|
53
|
+
const mockFilename = 'test-document.pdf';
|
|
54
|
+
|
|
55
|
+
it('should use default langchain service when no rules are configured', async () => {
|
|
56
|
+
const mockLangChainResult = [
|
|
57
|
+
{
|
|
58
|
+
id: 'chunk-1',
|
|
59
|
+
metadata: { source: 'test' },
|
|
60
|
+
pageContent: 'Test content chunk 1',
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
id: 'chunk-2',
|
|
64
|
+
metadata: { source: 'test' },
|
|
65
|
+
pageContent: 'Test content chunk 2',
|
|
66
|
+
},
|
|
67
|
+
];
|
|
68
|
+
|
|
69
|
+
mockLangChainPartition.mockResolvedValue(mockLangChainResult);
|
|
70
|
+
|
|
71
|
+
const result = await contentChunk.chunkContent({
|
|
72
|
+
content: mockFileContent,
|
|
73
|
+
fileType: 'application/pdf',
|
|
74
|
+
filename: mockFilename,
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
expect(mockLangChainPartition).toHaveBeenCalledWith(mockFilename, mockFileContent);
|
|
78
|
+
expect(result.chunks).toHaveLength(2);
|
|
79
|
+
expect(result.chunks[0]).toMatchObject({
|
|
80
|
+
id: 'chunk-1',
|
|
81
|
+
index: 0,
|
|
82
|
+
metadata: { source: 'test' },
|
|
83
|
+
text: 'Test content chunk 1',
|
|
84
|
+
type: 'LangChainElement',
|
|
85
|
+
});
|
|
86
|
+
expect(result.unstructuredChunks).toBeUndefined();
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('should use langchain when unstructured is not configured', async () => {
|
|
90
|
+
// Temporarily mock env to disable unstructured
|
|
91
|
+
vi.mocked(knowledgeEnv).UNSTRUCTURED_API_KEY = '';
|
|
92
|
+
|
|
93
|
+
const mockLangChainResult = [
|
|
94
|
+
{
|
|
95
|
+
id: 'chunk-1',
|
|
96
|
+
metadata: { source: 'test' },
|
|
97
|
+
pageContent: 'LangChain content',
|
|
98
|
+
},
|
|
99
|
+
];
|
|
100
|
+
|
|
101
|
+
mockLangChainPartition.mockResolvedValue(mockLangChainResult);
|
|
102
|
+
|
|
103
|
+
const result = await contentChunk.chunkContent({
|
|
104
|
+
content: mockFileContent,
|
|
105
|
+
fileType: 'application/pdf',
|
|
106
|
+
filename: mockFilename,
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
expect(mockLangChainPartition).toHaveBeenCalledWith(mockFilename, mockFileContent);
|
|
110
|
+
expect(result.chunks).toHaveLength(1);
|
|
111
|
+
expect(result.chunks[0].text).toBe('LangChain content');
|
|
112
|
+
expect(result.unstructuredChunks).toBeUndefined();
|
|
113
|
+
|
|
114
|
+
// Restore mock
|
|
115
|
+
vi.mocked(knowledgeEnv).UNSTRUCTURED_API_KEY = 'test-api-key';
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it('should handle langchain results with metadata', async () => {
|
|
119
|
+
const mockLangChainResult = [
|
|
120
|
+
{
|
|
121
|
+
id: 'chunk-1',
|
|
122
|
+
metadata: {
|
|
123
|
+
source: 'test-document.pdf',
|
|
124
|
+
page: 1,
|
|
125
|
+
loc: { lines: { from: 1, to: 10 } },
|
|
126
|
+
},
|
|
127
|
+
pageContent: 'First paragraph content',
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
id: 'chunk-2',
|
|
131
|
+
metadata: {
|
|
132
|
+
source: 'test-document.pdf',
|
|
133
|
+
page: 2,
|
|
134
|
+
},
|
|
135
|
+
pageContent: 'Second paragraph content',
|
|
136
|
+
},
|
|
137
|
+
];
|
|
138
|
+
|
|
139
|
+
mockLangChainPartition.mockResolvedValue(mockLangChainResult);
|
|
140
|
+
|
|
141
|
+
const result = await contentChunk.chunkContent({
|
|
142
|
+
content: mockFileContent,
|
|
143
|
+
fileType: 'application/pdf',
|
|
144
|
+
filename: mockFilename,
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
expect(result.chunks).toHaveLength(2);
|
|
148
|
+
expect(result.chunks[0]).toMatchObject({
|
|
149
|
+
id: 'chunk-1',
|
|
150
|
+
index: 0,
|
|
151
|
+
metadata: {
|
|
152
|
+
source: 'test-document.pdf',
|
|
153
|
+
page: 1,
|
|
154
|
+
loc: { lines: { from: 1, to: 10 } },
|
|
155
|
+
},
|
|
156
|
+
text: 'First paragraph content',
|
|
157
|
+
type: 'LangChainElement',
|
|
158
|
+
});
|
|
159
|
+
expect(result.chunks[1]).toMatchObject({
|
|
160
|
+
id: 'chunk-2',
|
|
161
|
+
index: 1,
|
|
162
|
+
text: 'Second paragraph content',
|
|
163
|
+
type: 'LangChainElement',
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
it('should handle different file types', async () => {
|
|
168
|
+
const mockLangChainResult = [
|
|
169
|
+
{
|
|
170
|
+
id: 'docx-chunk-1',
|
|
171
|
+
metadata: { source: 'test.docx' },
|
|
172
|
+
pageContent: 'Word document content',
|
|
173
|
+
},
|
|
174
|
+
];
|
|
175
|
+
|
|
176
|
+
mockLangChainPartition.mockResolvedValue(mockLangChainResult);
|
|
177
|
+
|
|
178
|
+
const result = await contentChunk.chunkContent({
|
|
179
|
+
content: mockFileContent,
|
|
180
|
+
fileType: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
|
181
|
+
filename: 'test.docx',
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
expect(mockLangChainPartition).toHaveBeenCalledWith('test.docx', mockFileContent);
|
|
185
|
+
expect(result.chunks).toHaveLength(1);
|
|
186
|
+
expect(result.chunks[0].text).toBe('Word document content');
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it('should throw error when all services fail and its the last service', async () => {
|
|
190
|
+
mockLangChainPartition.mockRejectedValue(new Error('LangChain error'));
|
|
191
|
+
|
|
192
|
+
await expect(
|
|
193
|
+
contentChunk.chunkContent({
|
|
194
|
+
content: mockFileContent,
|
|
195
|
+
fileType: 'application/pdf',
|
|
196
|
+
filename: mockFilename,
|
|
197
|
+
}),
|
|
198
|
+
).rejects.toThrow('LangChain error');
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
it('should handle empty langchain results', async () => {
|
|
202
|
+
mockLangChainPartition.mockResolvedValue([]);
|
|
203
|
+
|
|
204
|
+
const result = await contentChunk.chunkContent({
|
|
205
|
+
content: mockFileContent,
|
|
206
|
+
fileType: 'application/pdf',
|
|
207
|
+
filename: mockFilename,
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
expect(result.chunks).toHaveLength(0);
|
|
211
|
+
expect(result.unstructuredChunks).toBeUndefined();
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
it('should extract file extension correctly from MIME type', async () => {
|
|
215
|
+
const mockLangChainResult = [
|
|
216
|
+
{
|
|
217
|
+
id: 'chunk-1',
|
|
218
|
+
metadata: {},
|
|
219
|
+
pageContent: 'Content',
|
|
220
|
+
},
|
|
221
|
+
];
|
|
222
|
+
|
|
223
|
+
mockLangChainPartition.mockResolvedValue(mockLangChainResult);
|
|
224
|
+
|
|
225
|
+
await contentChunk.chunkContent({
|
|
226
|
+
content: mockFileContent,
|
|
227
|
+
fileType: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
|
228
|
+
filename: 'test.docx',
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
expect(mockLangChainPartition).toHaveBeenCalledWith('test.docx', mockFileContent);
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
it('should handle langchain results with minimal metadata', async () => {
|
|
235
|
+
const mockLangChainResult = [
|
|
236
|
+
{
|
|
237
|
+
id: 'chunk-minimal',
|
|
238
|
+
metadata: {},
|
|
239
|
+
pageContent: 'Content with no metadata',
|
|
240
|
+
},
|
|
241
|
+
];
|
|
242
|
+
|
|
243
|
+
mockLangChainPartition.mockResolvedValue(mockLangChainResult);
|
|
244
|
+
|
|
245
|
+
const result = await contentChunk.chunkContent({
|
|
246
|
+
content: mockFileContent,
|
|
247
|
+
fileType: 'text/plain',
|
|
248
|
+
filename: 'test.txt',
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
expect(result.chunks[0]).toMatchObject({
|
|
252
|
+
id: 'chunk-minimal',
|
|
253
|
+
index: 0,
|
|
254
|
+
metadata: {},
|
|
255
|
+
text: 'Content with no metadata',
|
|
256
|
+
type: 'LangChainElement',
|
|
257
|
+
});
|
|
258
|
+
});
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
describe('canUseUnstructured', () => {
|
|
262
|
+
it('should return true when API key and server URL are configured', () => {
|
|
263
|
+
const result = contentChunk['canUseUnstructured']();
|
|
264
|
+
expect(result).toBe(true);
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
it('should return false when API key is missing', () => {
|
|
268
|
+
const originalKey = knowledgeEnv.UNSTRUCTURED_API_KEY;
|
|
269
|
+
vi.mocked(knowledgeEnv).UNSTRUCTURED_API_KEY = '';
|
|
270
|
+
|
|
271
|
+
const result = contentChunk['canUseUnstructured']();
|
|
272
|
+
expect(result).toBe(false);
|
|
273
|
+
|
|
274
|
+
// Restore
|
|
275
|
+
vi.mocked(knowledgeEnv).UNSTRUCTURED_API_KEY = originalKey;
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
it('should return false when server URL is missing', () => {
|
|
279
|
+
const originalUrl = knowledgeEnv.UNSTRUCTURED_SERVER_URL;
|
|
280
|
+
vi.mocked(knowledgeEnv).UNSTRUCTURED_SERVER_URL = '';
|
|
281
|
+
|
|
282
|
+
const result = contentChunk['canUseUnstructured']();
|
|
283
|
+
expect(result).toBe(false);
|
|
284
|
+
|
|
285
|
+
// Restore
|
|
286
|
+
vi.mocked(knowledgeEnv).UNSTRUCTURED_SERVER_URL = originalUrl;
|
|
287
|
+
});
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
describe('getChunkingServices', () => {
|
|
291
|
+
it('should return default service for unknown file type', () => {
|
|
292
|
+
const services = contentChunk['getChunkingServices']('application/unknown');
|
|
293
|
+
expect(services).toEqual(['default']);
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
it('should extract extension from MIME type correctly', () => {
|
|
297
|
+
const services = contentChunk['getChunkingServices']('application/pdf');
|
|
298
|
+
expect(services).toEqual(['default']);
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
it('should handle MIME types with multiple slashes', () => {
|
|
302
|
+
const services = contentChunk['getChunkingServices'](
|
|
303
|
+
'application/vnd.openxmlformats-officedocument/wordprocessingml.document',
|
|
304
|
+
);
|
|
305
|
+
expect(services).toEqual(['default']);
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
it('should convert extension to lowercase', () => {
|
|
309
|
+
const services = contentChunk['getChunkingServices']('application/PDF');
|
|
310
|
+
expect(services).toEqual(['default']);
|
|
311
|
+
});
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
describe('integration scenarios', () => {
|
|
315
|
+
it('should handle multiple chunk items with correct indices', async () => {
|
|
316
|
+
const mockLangChainResult = Array.from({ length: 5 }, (_, i) => ({
|
|
317
|
+
id: `chunk-${i}`,
|
|
318
|
+
metadata: { index: i },
|
|
319
|
+
pageContent: `Content ${i}`,
|
|
320
|
+
}));
|
|
321
|
+
|
|
322
|
+
mockLangChainPartition.mockResolvedValue(mockLangChainResult);
|
|
323
|
+
|
|
324
|
+
const result = await contentChunk.chunkContent({
|
|
325
|
+
content: new Uint8Array([1, 2, 3]),
|
|
326
|
+
fileType: 'text/plain',
|
|
327
|
+
filename: 'test.txt',
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
expect(result.chunks).toHaveLength(5);
|
|
331
|
+
result.chunks.forEach((chunk, index) => {
|
|
332
|
+
expect(chunk.index).toBe(index);
|
|
333
|
+
expect(chunk.text).toBe(`Content ${index}`);
|
|
334
|
+
});
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
it('should preserve order of chunks from langchain response', async () => {
|
|
338
|
+
const mockLangChainResult = [
|
|
339
|
+
{
|
|
340
|
+
id: 'elem-3',
|
|
341
|
+
metadata: { source: 'test.txt' },
|
|
342
|
+
pageContent: 'Third',
|
|
343
|
+
},
|
|
344
|
+
{
|
|
345
|
+
id: 'elem-1',
|
|
346
|
+
metadata: { source: 'test.txt' },
|
|
347
|
+
pageContent: 'First',
|
|
348
|
+
},
|
|
349
|
+
{
|
|
350
|
+
id: 'elem-2',
|
|
351
|
+
metadata: { source: 'test.txt' },
|
|
352
|
+
pageContent: 'Second',
|
|
353
|
+
},
|
|
354
|
+
];
|
|
355
|
+
|
|
356
|
+
mockLangChainPartition.mockResolvedValue(mockLangChainResult);
|
|
357
|
+
|
|
358
|
+
const result = await contentChunk.chunkContent({
|
|
359
|
+
content: new Uint8Array([1, 2, 3]),
|
|
360
|
+
fileType: 'text/plain',
|
|
361
|
+
filename: 'test.txt',
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
expect(result.chunks[0].text).toBe('Third');
|
|
365
|
+
expect(result.chunks[1].text).toBe('First');
|
|
366
|
+
expect(result.chunks[2].text).toBe('Second');
|
|
367
|
+
expect(result.chunks[0].index).toBe(0);
|
|
368
|
+
expect(result.chunks[1].index).toBe(1);
|
|
369
|
+
expect(result.chunks[2].index).toBe(2);
|
|
370
|
+
});
|
|
371
|
+
});
|
|
372
|
+
});
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { ChatErrorType } from '@lobechat/types';
|
|
2
|
+
|
|
3
|
+
import { createErrorResponse } from '@/utils/errorResponse';
|
|
4
|
+
import { createNodeResponse } from '@/utils/server/response';
|
|
5
|
+
|
|
6
|
+
export interface CreateSpeechResponseOptions {
|
|
7
|
+
errorContentType?: string;
|
|
8
|
+
logTag: string;
|
|
9
|
+
messages?: {
|
|
10
|
+
failure?: string;
|
|
11
|
+
invalid?: string;
|
|
12
|
+
};
|
|
13
|
+
successContentType?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Wraps a third-party speech SDK response so the Node.js runtime always receives
|
|
18
|
+
* a valid platform Response, while keeping logging and error handling consistent.
|
|
19
|
+
*/
|
|
20
|
+
export const createSpeechResponse = async <T>(
|
|
21
|
+
responseCreator: () => Promise<T>,
|
|
22
|
+
{
|
|
23
|
+
logTag,
|
|
24
|
+
successContentType = 'audio/mpeg',
|
|
25
|
+
errorContentType = 'application/json',
|
|
26
|
+
messages,
|
|
27
|
+
}: CreateSpeechResponseOptions,
|
|
28
|
+
) => {
|
|
29
|
+
const prefix = `[${logTag}]`;
|
|
30
|
+
const invalidMessage = messages?.invalid ?? 'Unexpected payload from speech provider';
|
|
31
|
+
const failureMessage = messages?.failure ?? 'Failed to synthesize speech';
|
|
32
|
+
|
|
33
|
+
return createNodeResponse(responseCreator, {
|
|
34
|
+
error: {
|
|
35
|
+
cacheControl: 'no-store',
|
|
36
|
+
defaultContentType: errorContentType,
|
|
37
|
+
},
|
|
38
|
+
onInvalidResponse: (response) => {
|
|
39
|
+
console.error(`${prefix} ${invalidMessage}`, response);
|
|
40
|
+
|
|
41
|
+
return createErrorResponse(ChatErrorType.InternalServerError);
|
|
42
|
+
},
|
|
43
|
+
onNonResponseError: (error) => {
|
|
44
|
+
console.error(`${prefix} ${failureMessage}`, error);
|
|
45
|
+
|
|
46
|
+
return createErrorResponse(ChatErrorType.InternalServerError, {
|
|
47
|
+
message: error instanceof Error ? error.message : String(error),
|
|
48
|
+
});
|
|
49
|
+
},
|
|
50
|
+
success: {
|
|
51
|
+
cacheControl: 'no-store',
|
|
52
|
+
defaultContentType: successContentType,
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
};
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
// @vitest-environment edge-runtime
|
|
2
|
-
import { describe, expect, it, vi } from 'vitest';
|
|
3
|
-
|
|
4
|
-
import { POST as UniverseRoute } from '../[provider]/route';
|
|
5
|
-
import { POST, runtime } from './route';
|
|
6
|
-
|
|
7
|
-
vi.mock('../[provider]/route', () => ({
|
|
8
|
-
POST: vi.fn().mockResolvedValue('mocked response'),
|
|
9
|
-
}));
|
|
10
|
-
|
|
11
|
-
describe('Configuration tests', () => {
|
|
12
|
-
it('should have runtime set to "edge"', () => {
|
|
13
|
-
expect(runtime).toBe('edge');
|
|
14
|
-
});
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
describe('Groq POST function tests', () => {
|
|
18
|
-
it('should call UniverseRoute with correct parameters', async () => {
|
|
19
|
-
const mockRequest = new Request('https://example.com', { method: 'POST' });
|
|
20
|
-
await POST(mockRequest);
|
|
21
|
-
expect(UniverseRoute).toHaveBeenCalledWith(mockRequest, {
|
|
22
|
-
params: Promise.resolve({ provider: 'azureai' }),
|
|
23
|
-
});
|
|
24
|
-
});
|
|
25
|
-
});
|