@lobehub/chat 1.55.0 → 1.55.2

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 (32) hide show
  1. package/CHANGELOG.md +58 -0
  2. package/changelog/v1.json +21 -0
  3. package/docs/changelog/2025-02-02-deepseek-r1.mdx +4 -4
  4. package/docs/self-hosting/advanced/auth/next-auth/casdoor.mdx +4 -3
  5. package/docs/self-hosting/advanced/auth/next-auth/casdoor.zh-CN.mdx +4 -3
  6. package/docs/self-hosting/advanced/auth/next-auth/logto.mdx +9 -9
  7. package/docs/self-hosting/advanced/auth/next-auth/logto.zh-CN.mdx +9 -9
  8. package/docs/self-hosting/advanced/model-list.zh-CN.mdx +6 -6
  9. package/docs/self-hosting/advanced/observability/langfuse.zh-CN.mdx +70 -0
  10. package/docs/self-hosting/environment-variables/auth.mdx +3 -3
  11. package/docs/self-hosting/environment-variables/auth.zh-CN.mdx +3 -2
  12. package/docs/self-hosting/platform/btpanel.mdx +1 -1
  13. package/docs/self-hosting/platform/btpanel.zh-CN.mdx +1 -1
  14. package/docs/self-hosting/platform/tencentcloud-lighthouse.mdx +33 -0
  15. package/docs/self-hosting/platform/tencentcloud-lighthouse.zh-CN.mdx +31 -0
  16. package/docs/self-hosting/server-database.mdx +1 -1
  17. package/docs/self-hosting/start.zh-CN.mdx +3 -1
  18. package/docs/usage/providers/wenxin.mdx +1 -0
  19. package/docs/usage/providers/wenxin.zh-CN.mdx +1 -0
  20. package/package.json +1 -3
  21. package/src/config/aiModels/openrouter.ts +30 -0
  22. package/src/config/modelProviders/openrouter.ts +9 -0
  23. package/src/features/Conversation/Messages/Assistant/index.tsx +5 -1
  24. package/src/libs/agent-runtime/AgentRuntime.test.ts +1 -0
  25. package/src/libs/agent-runtime/azureOpenai/index.test.ts +47 -9
  26. package/src/libs/agent-runtime/azureOpenai/index.ts +35 -28
  27. package/src/libs/agent-runtime/utils/streams/index.ts +0 -1
  28. package/src/server/modules/AgentRuntime/index.test.ts +3 -1
  29. package/src/server/routers/lambda/aiModel.test.ts +240 -0
  30. package/src/store/aiInfra/slices/aiModel/selectors.test.ts +228 -0
  31. package/src/libs/agent-runtime/utils/streams/azureOpenai.test.ts +0 -536
  32. package/src/libs/agent-runtime/utils/streams/azureOpenai.ts +0 -83
@@ -1,536 +0,0 @@
1
- import { describe, expect, it, vi } from 'vitest';
2
-
3
- import { AzureOpenAIStream } from './azureOpenai';
4
-
5
- describe('AzureOpenAIStream', () => {
6
- it('should transform AzureOpenAI stream to protocol stream', async () => {
7
- const mockOpenAIStream = new ReadableStream({
8
- start(controller) {
9
- controller.enqueue({
10
- choices: [
11
- {
12
- delta: { content: 'Hello' },
13
- index: 0,
14
- },
15
- ],
16
- id: '1',
17
- });
18
- controller.enqueue({
19
- choices: [
20
- {
21
- delta: { content: ' world!' },
22
- index: 1,
23
- },
24
- ],
25
- id: '1',
26
- });
27
- controller.enqueue({
28
- choices: [
29
- {
30
- delta: null,
31
- finishReason: 'stop',
32
- index: 2,
33
- },
34
- ],
35
- id: '1',
36
- });
37
-
38
- controller.close();
39
- },
40
- });
41
-
42
- const onStartMock = vi.fn();
43
- const onTextMock = vi.fn();
44
- const onTokenMock = vi.fn();
45
- const onCompletionMock = vi.fn();
46
-
47
- const protocolStream = AzureOpenAIStream(mockOpenAIStream, {
48
- onStart: onStartMock,
49
- onText: onTextMock,
50
- onToken: onTokenMock,
51
- onCompletion: onCompletionMock,
52
- });
53
-
54
- const decoder = new TextDecoder();
55
- const chunks = [];
56
-
57
- // @ts-ignore
58
- for await (const chunk of protocolStream) {
59
- chunks.push(decoder.decode(chunk, { stream: true }));
60
- }
61
-
62
- expect(chunks).toEqual([
63
- 'id: 1\n',
64
- 'event: text\n',
65
- `data: "Hello"\n\n`,
66
- 'id: 1\n',
67
- 'event: text\n',
68
- `data: " world!"\n\n`,
69
- 'id: 1\n',
70
- 'event: stop\n',
71
- `data: "stop"\n\n`,
72
- ]);
73
-
74
- expect(onStartMock).toHaveBeenCalledTimes(1);
75
- expect(onTextMock).toHaveBeenNthCalledWith(1, '"Hello"');
76
- expect(onTextMock).toHaveBeenNthCalledWith(2, '" world!"');
77
- expect(onTokenMock).toHaveBeenCalledTimes(2);
78
- expect(onCompletionMock).toHaveBeenCalledTimes(1);
79
- });
80
-
81
- it('should handle empty stream', async () => {
82
- const mockStream = new ReadableStream({
83
- start(controller) {
84
- controller.close();
85
- },
86
- });
87
-
88
- const protocolStream = AzureOpenAIStream(mockStream);
89
-
90
- const decoder = new TextDecoder();
91
- const chunks = [];
92
-
93
- // @ts-ignore
94
- for await (const chunk of protocolStream) {
95
- chunks.push(decoder.decode(chunk, { stream: true }));
96
- }
97
-
98
- expect(chunks).toEqual([]);
99
- });
100
-
101
- it('should handle delta content null', async () => {
102
- const mockOpenAIStream = new ReadableStream({
103
- start(controller) {
104
- controller.enqueue({
105
- choices: [
106
- {
107
- delta: { content: null },
108
- index: 0,
109
- },
110
- ],
111
- id: '3',
112
- });
113
-
114
- controller.close();
115
- },
116
- });
117
-
118
- const protocolStream = AzureOpenAIStream(mockOpenAIStream);
119
-
120
- const decoder = new TextDecoder();
121
- const chunks = [];
122
-
123
- // @ts-ignore
124
- for await (const chunk of protocolStream) {
125
- chunks.push(decoder.decode(chunk, { stream: true }));
126
- }
127
-
128
- expect(chunks).toEqual(['id: 3\n', 'event: data\n', `data: {"content":null}\n\n`]);
129
- });
130
-
131
- it('should handle other delta data', async () => {
132
- const mockOpenAIStream = new ReadableStream({
133
- start(controller) {
134
- controller.enqueue({
135
- choices: [
136
- {
137
- delta: { custom_field: 'custom_value' },
138
- index: 0,
139
- },
140
- ],
141
- id: '4',
142
- });
143
-
144
- controller.close();
145
- },
146
- });
147
-
148
- const protocolStream = AzureOpenAIStream(mockOpenAIStream);
149
-
150
- const decoder = new TextDecoder();
151
- const chunks = [];
152
-
153
- // @ts-ignore
154
- for await (const chunk of protocolStream) {
155
- chunks.push(decoder.decode(chunk, { stream: true }));
156
- }
157
-
158
- expect(chunks).toEqual([
159
- 'id: 4\n',
160
- 'event: data\n',
161
- `data: {"delta":{"custom_field":"custom_value"},"id":"4","index":0}\n\n`,
162
- ]);
163
- });
164
-
165
- describe('tool Calling', () => {
166
- it('should handle tool calls', async () => {
167
- const streams = [
168
- {
169
- id: 'chatcmpl-9eEBuv3ra8l4KKQhGj6ldhqfwV4Iy',
170
- model: 'gpt-4o-2024-05-13',
171
- object: 'chat.completion.chunk',
172
- systemFingerprint: 'fp_abc28019ad',
173
- created: '1970-01-20T21:36:14.698Z',
174
- choices: [
175
- {
176
- delta: {
177
- content: null,
178
- role: 'assistant',
179
- toolCalls: [
180
- {
181
- function: { arguments: '', name: 'realtime-weather____fetchCurrentWeather' },
182
- id: 'call_1GT6no85IuAal06XHH2CZe8Q',
183
- index: 0,
184
- type: 'function',
185
- },
186
- ],
187
- },
188
- index: 0,
189
- logprobs: null,
190
- finishReason: null,
191
- contentFilterResults: {},
192
- },
193
- ],
194
- },
195
- {
196
- id: 'chatcmpl-9eEBuv3ra8l4KKQhGj6ldhqfwV4Iy',
197
- model: 'gpt-4o-2024-05-13',
198
- object: 'chat.completion.chunk',
199
- systemFingerprint: 'fp_abc28019ad',
200
- created: '1970-01-20T21:36:14.698Z',
201
- choices: [
202
- {
203
- delta: { toolCalls: [{ function: { arguments: '{"' }, index: 0 }] },
204
- index: 0,
205
- logprobs: null,
206
- finishReason: null,
207
- contentFilterResults: {},
208
- },
209
- ],
210
- },
211
- {
212
- id: 'chatcmpl-9eEBuv3ra8l4KKQhGj6ldhqfwV4Iy',
213
- model: 'gpt-4o-2024-05-13',
214
- object: 'chat.completion.chunk',
215
- systemFingerprint: 'fp_abc28019ad',
216
- created: '1970-01-20T21:36:14.698Z',
217
- choices: [
218
- {
219
- delta: { toolCalls: [{ function: { arguments: 'city' }, index: 0 }] },
220
- index: 0,
221
- logprobs: null,
222
- finishReason: null,
223
- contentFilterResults: {},
224
- },
225
- ],
226
- },
227
- {
228
- id: 'chatcmpl-9eEBuv3ra8l4KKQhGj6ldhqfwV4Iy',
229
- model: 'gpt-4o-2024-05-13',
230
- object: 'chat.completion.chunk',
231
- systemFingerprint: 'fp_abc28019ad',
232
- created: '1970-01-20T21:36:14.698Z',
233
- choices: [
234
- {
235
- delta: { toolCalls: [{ function: { arguments: '":"' }, index: 0 }] },
236
- index: 0,
237
- logprobs: null,
238
- finishReason: null,
239
- contentFilterResults: {},
240
- },
241
- ],
242
- },
243
- {
244
- id: 'chatcmpl-9eEBuv3ra8l4KKQhGj6ldhqfwV4Iy',
245
- model: 'gpt-4o-2024-05-13',
246
- object: 'chat.completion.chunk',
247
- systemFingerprint: 'fp_abc28019ad',
248
- created: '1970-01-20T21:36:14.698Z',
249
- choices: [
250
- {
251
- delta: { toolCalls: [{ function: { arguments: '杭州' }, index: 0 }] },
252
- index: 0,
253
- logprobs: null,
254
- finishReason: null,
255
- contentFilteesults: {},
256
- },
257
- ],
258
- },
259
- {
260
- id: 'chatcmpl-9eEBuv3ra8l4KKQhGj6ldhqfwV4Iy',
261
- model: 'gpt-4o-2024-05-13',
262
- object: 'chat.completion.chunk',
263
- systemFingerprint: 'fp_abc28019ad',
264
- created: '1970-01-20T21:36:14.698Z',
265
- choices: [
266
- {
267
- delta: { toolCalls: [{ function: { arguments: '"}' }, index: 0 }] },
268
- index: 0,
269
- logprobs: null,
270
- finishReason: null,
271
- contentFilterResults: {},
272
- },
273
- ],
274
- },
275
- {
276
- id: 'chatcmpl-9eEBuv3ra8l4KKQhGj6ldhqfwV4Iy',
277
- model: 'gpt-4o-2024-05-13',
278
- object: 'chat.completion.chunk',
279
- systemFingerprint: 'fp_abc28019ad',
280
- created: '1970-01-20T21:36:14.698Z',
281
- choices: [
282
- {
283
- delta: {},
284
- index: 0,
285
- logprobs: null,
286
- finishReason: 'tool_calls',
287
- contentFilterResults: {},
288
- },
289
- ],
290
- },
291
- ];
292
-
293
- const mockReadableStream = new ReadableStream({
294
- start(controller) {
295
- streams.forEach((chunk) => {
296
- controller.enqueue(chunk);
297
- });
298
- controller.close();
299
- },
300
- });
301
-
302
- const onToolCallMock = vi.fn();
303
-
304
- const protocolStream = AzureOpenAIStream(mockReadableStream, {
305
- onToolCall: onToolCallMock,
306
- });
307
-
308
- const decoder = new TextDecoder();
309
- const chunks = [];
310
-
311
- // @ts-ignore
312
- for await (const chunk of protocolStream) {
313
- chunks.push(decoder.decode(chunk, { stream: true }));
314
- }
315
-
316
- expect(chunks).toEqual(
317
- [
318
- 'id: chatcmpl-9eEBuv3ra8l4KKQhGj6ldhqfwV4Iy',
319
- 'event: tool_calls',
320
- `data: [{"function":{"arguments":"","name":"realtime-weather____fetchCurrentWeather"},"id":"call_1GT6no85IuAal06XHH2CZe8Q","index":0,"type":"function"}]\n`,
321
- 'id: chatcmpl-9eEBuv3ra8l4KKQhGj6ldhqfwV4Iy',
322
- 'event: tool_calls',
323
- `data: [{"function":{"arguments":"{\\""},"id":"call_1GT6no85IuAal06XHH2CZe8Q","index":0,"type":"function"}]\n`,
324
- 'id: chatcmpl-9eEBuv3ra8l4KKQhGj6ldhqfwV4Iy',
325
- 'event: tool_calls',
326
- `data: [{"function":{"arguments":"city"},"id":"call_1GT6no85IuAal06XHH2CZe8Q","index":0,"type":"function"}]\n`,
327
- 'id: chatcmpl-9eEBuv3ra8l4KKQhGj6ldhqfwV4Iy',
328
- 'event: tool_calls',
329
- `data: [{"function":{"arguments":"\\":\\""},"id":"call_1GT6no85IuAal06XHH2CZe8Q","index":0,"type":"function"}]\n`,
330
- 'id: chatcmpl-9eEBuv3ra8l4KKQhGj6ldhqfwV4Iy',
331
- 'event: tool_calls',
332
- `data: [{"function":{"arguments":"杭州"},"id":"call_1GT6no85IuAal06XHH2CZe8Q","index":0,"type":"function"}]\n`,
333
- 'id: chatcmpl-9eEBuv3ra8l4KKQhGj6ldhqfwV4Iy',
334
- 'event: tool_calls',
335
- `data: [{"function":{"arguments":"\\"}"},"id":"call_1GT6no85IuAal06XHH2CZe8Q","index":0,"type":"function"}]\n`,
336
- 'id: chatcmpl-9eEBuv3ra8l4KKQhGj6ldhqfwV4Iy',
337
- 'event: stop',
338
- `data: "tool_calls"\n`,
339
- ].map((item) => `${item}\n`),
340
- );
341
-
342
- expect(onToolCallMock).toHaveBeenCalledTimes(6);
343
- });
344
- it('should handle parallel tools calling', async () => {
345
- const streams = [
346
- {
347
- id: 'chatcmpl-9eEh9DtpidX5CyE4GcyIeyhU3pLir',
348
- model: 'gpt-4o-2024-05-13',
349
- object: 'chat.completion.chunk',
350
- systemFingerprint: 'fp_abc28019ad',
351
- created: '1970-01-20T21:36:16.635Z',
352
- choices: [
353
- {
354
- delta: {
355
- toolCalls: [
356
- {
357
- function: { arguments: '', name: 'realtime-weather____fetchCurrentWeather' },
358
- id: 'call_cnQ80VjcWCS69wWKp4jz0nJd',
359
- index: 0,
360
- type: 'function',
361
- },
362
- ],
363
- },
364
- index: 0,
365
- logprobs: null,
366
- finishReason: null,
367
- contentFilterResults: {},
368
- },
369
- ],
370
- },
371
- {
372
- id: 'chatcmpl-9eEh9DtpidX5CyE4GcyIeyhU3pLir',
373
- model: 'gpt-4o-2024-05-13',
374
- object: 'chat.completion.chunk',
375
- systemFingerprint: 'fp_abc28019ad',
376
- created: '1970-01-20T21:36:16.635Z',
377
- choices: [
378
- {
379
- delta: { toolCalls: [{ function: { arguments: '{"city": "杭州"}' }, index: 0 }] },
380
- index: 0,
381
- logprobs: null,
382
- finishReason: null,
383
- contentFilterResults: {},
384
- },
385
- ],
386
- },
387
- {
388
- id: 'chatcmpl-9eEh9DtpidX5CyE4GcyIeyhU3pLir',
389
- model: 'gpt-4o-2024-05-13',
390
- object: 'chat.completion.chunk',
391
- systemFingerprint: 'fp_abc28019ad',
392
- created: '1970-01-20T21:36:16.635Z',
393
- choices: [
394
- {
395
- delta: {
396
- toolCalls: [
397
- {
398
- function: { arguments: '', name: 'realtime-weather____fetchCurrentWeather' },
399
- id: 'call_LHrpPTrT563QkP9chVddzXQk',
400
- index: 1,
401
- type: 'function',
402
- },
403
- ],
404
- },
405
- index: 0,
406
- logprobs: null,
407
- finishReason: null,
408
- contentFilterResults: {},
409
- },
410
- ],
411
- },
412
- {
413
- id: 'chatcmpl-9eEh9DtpidX5CyE4GcyIeyhU3pLir',
414
- model: 'gpt-4o-2024-05-13',
415
- object: 'chat.completion.chunk',
416
- systemFingerprint: 'fp_abc28019ad',
417
- created: '1970-01-20T21:36:16.635Z',
418
- choices: [
419
- {
420
- delta: { toolCalls: [{ function: { arguments: '{"city": "北京"}' }, index: 1 }] },
421
- index: 0,
422
- logprobs: null,
423
- finishReason: null,
424
- contentFilterResults: {},
425
- },
426
- ],
427
- },
428
- {
429
- id: 'chatcmpl-9eEh9DtpidX5CyE4GcyIeyhU3pLir',
430
- model: 'gpt-4o-2024-05-13',
431
- object: 'chat.completion.chunk',
432
- systemFingerprint: 'fp_abc28019ad',
433
- created: '1970-01-20T21:36:16.635Z',
434
- choices: [
435
- {
436
- delta: {},
437
- index: 0,
438
- logprobs: null,
439
- finishReason: 'tool_calls',
440
- contentFilterResults: {},
441
- },
442
- ],
443
- },
444
- ];
445
-
446
- const mockReadableStream = new ReadableStream({
447
- start(controller) {
448
- streams.forEach((chunk) => {
449
- controller.enqueue(chunk);
450
- });
451
- controller.close();
452
- },
453
- });
454
-
455
- const onToolCallMock = vi.fn();
456
-
457
- const protocolStream = AzureOpenAIStream(mockReadableStream, {
458
- onToolCall: onToolCallMock,
459
- });
460
-
461
- const decoder = new TextDecoder();
462
- const chunks = [];
463
-
464
- // @ts-ignore
465
- for await (const chunk of protocolStream) {
466
- chunks.push(decoder.decode(chunk, { stream: true }));
467
- }
468
-
469
- expect(chunks).toEqual(
470
- [
471
- 'id: chatcmpl-9eEh9DtpidX5CyE4GcyIeyhU3pLir',
472
- 'event: tool_calls',
473
- `data: [{"function":{"arguments":"","name":"realtime-weather____fetchCurrentWeather"},"id":"call_cnQ80VjcWCS69wWKp4jz0nJd","index":0,"type":"function"}]\n`,
474
- 'id: chatcmpl-9eEh9DtpidX5CyE4GcyIeyhU3pLir',
475
- 'event: tool_calls',
476
- `data: [{"function":{"arguments":"{\\"city\\": \\"杭州\\"}"},"id":"call_cnQ80VjcWCS69wWKp4jz0nJd","index":0,"type":"function"}]\n`,
477
- 'id: chatcmpl-9eEh9DtpidX5CyE4GcyIeyhU3pLir',
478
- 'event: tool_calls',
479
- `data: [{"function":{"arguments":"","name":"realtime-weather____fetchCurrentWeather"},"id":"call_LHrpPTrT563QkP9chVddzXQk","index":1,"type":"function"}]\n`,
480
- 'id: chatcmpl-9eEh9DtpidX5CyE4GcyIeyhU3pLir',
481
- 'event: tool_calls',
482
- `data: [{"function":{"arguments":"{\\"city\\": \\"北京\\"}"},"id":"call_LHrpPTrT563QkP9chVddzXQk","index":1,"type":"function"}]\n`,
483
- 'id: chatcmpl-9eEh9DtpidX5CyE4GcyIeyhU3pLir',
484
- 'event: stop',
485
- `data: "tool_calls"\n`,
486
- ].map((item) => `${item}\n`),
487
- );
488
-
489
- expect(onToolCallMock).toHaveBeenCalledTimes(4);
490
- });
491
- it('should handle tool calls without index and type', async () => {
492
- const mockOpenAIStream = new ReadableStream({
493
- start(controller) {
494
- controller.enqueue({
495
- choices: [
496
- {
497
- delta: {
498
- toolCalls: [
499
- {
500
- function: { name: 'tool1', arguments: '{}' },
501
- id: 'call_1',
502
- },
503
- {
504
- function: { name: 'tool2', arguments: '{}' },
505
- id: 'call_2',
506
- },
507
- ],
508
- },
509
- index: 0,
510
- },
511
- ],
512
- id: '5',
513
- });
514
-
515
- controller.close();
516
- },
517
- });
518
-
519
- const protocolStream = AzureOpenAIStream(mockOpenAIStream);
520
-
521
- const decoder = new TextDecoder();
522
- const chunks = [];
523
-
524
- // @ts-ignore
525
- for await (const chunk of protocolStream) {
526
- chunks.push(decoder.decode(chunk, { stream: true }));
527
- }
528
-
529
- expect(chunks).toEqual([
530
- 'id: 5\n',
531
- 'event: tool_calls\n',
532
- `data: [{"function":{"name":"tool1","arguments":"{}"},"id":"call_1","index":0,"type":"function"},{"function":{"name":"tool2","arguments":"{}"},"id":"call_2","index":1,"type":"function"}]\n\n`,
533
- ]);
534
- });
535
- });
536
- });
@@ -1,83 +0,0 @@
1
- import { ChatCompletions, ChatCompletionsFunctionToolCall } from '@azure/openai';
2
- import OpenAI from 'openai';
3
- import type { Stream } from 'openai/streaming';
4
-
5
- import { ChatStreamCallbacks } from '../../types';
6
- import {
7
- StreamProtocolChunk,
8
- StreamProtocolToolCallChunk,
9
- StreamStack,
10
- StreamToolCallChunkData,
11
- convertIterableToStream,
12
- createCallbacksTransformer,
13
- createSSEProtocolTransformer,
14
- } from './protocol';
15
-
16
- const transformOpenAIStream = (chunk: ChatCompletions, stack: StreamStack): StreamProtocolChunk => {
17
- // maybe need another structure to add support for multiple choices
18
-
19
- const item = chunk.choices[0];
20
- if (!item) {
21
- return { data: chunk, id: chunk.id, type: 'data' };
22
- }
23
-
24
- if (typeof item.delta?.content === 'string') {
25
- return { data: item.delta.content, id: chunk.id, type: 'text' };
26
- }
27
-
28
- if (item.delta?.toolCalls) {
29
- return {
30
- data: item.delta.toolCalls.map((value, index): StreamToolCallChunkData => {
31
- const func = (value as ChatCompletionsFunctionToolCall).function;
32
-
33
- // at first time, set tool id
34
- if (!stack.tool) {
35
- stack.tool = { id: value.id, index, name: func.name };
36
- } else {
37
- // in the parallel tool calling, set the new tool id
38
- if (value.id && stack.tool.id !== value.id) {
39
- stack.tool = { id: value.id, index, name: func.name };
40
- }
41
- }
42
-
43
- return {
44
- function: func,
45
- id: value.id || stack.tool?.id,
46
- index: value.index || index,
47
- type: value.type || 'function',
48
- };
49
- }),
50
- id: chunk.id,
51
- type: 'tool_calls',
52
- } as StreamProtocolToolCallChunk;
53
- }
54
-
55
- // 给定结束原因
56
- if (item.finishReason) {
57
- return { data: item.finishReason, id: chunk.id, type: 'stop' };
58
- }
59
-
60
- if (item.delta?.content === null) {
61
- return { data: item.delta, id: chunk.id, type: 'data' };
62
- }
63
-
64
- // 其余情况下,返回 delta 和 index
65
- return {
66
- data: { delta: item.delta, id: chunk.id, index: item.index },
67
- id: chunk.id,
68
- type: 'data',
69
- };
70
- };
71
-
72
- export const AzureOpenAIStream = (
73
- stream: Stream<OpenAI.ChatCompletionChunk> | ReadableStream,
74
- callbacks?: ChatStreamCallbacks,
75
- ) => {
76
- const stack: StreamStack = { id: '' };
77
- const readableStream =
78
- stream instanceof ReadableStream ? stream : convertIterableToStream(stream);
79
-
80
- return readableStream
81
- .pipeThrough(createSSEProtocolTransformer(transformOpenAIStream, stack))
82
- .pipeThrough(createCallbacksTransformer(callbacks));
83
- };