@lobehub/chat 1.119.0 → 1.119.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.
- package/CHANGELOG.md +25 -0
- package/changelog/v1.json +9 -0
- package/package.json +1 -1
- package/packages/model-runtime/src/helpers/index.ts +1 -0
- package/packages/model-runtime/src/helpers/mergeChatMethodOptions.test.ts +368 -0
- package/packages/model-runtime/src/helpers/mergeChatMethodOptions.ts +123 -0
- package/src/features/AgentSetting/AgentModal/index.tsx +1 -1
package/CHANGELOG.md
CHANGED
@@ -2,6 +2,31 @@
|
|
2
2
|
|
3
3
|
# Changelog
|
4
4
|
|
5
|
+
### [Version 1.119.1](https://github.com/lobehub/lobe-chat/compare/v1.119.0...v1.119.1)
|
6
|
+
|
7
|
+
<sup>Released on **2025-08-30**</sup>
|
8
|
+
|
9
|
+
#### 🐛 Bug Fixes
|
10
|
+
|
11
|
+
- **misc**: Update enableStreaming name.
|
12
|
+
|
13
|
+
<br/>
|
14
|
+
|
15
|
+
<details>
|
16
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
17
|
+
|
18
|
+
#### What's fixed
|
19
|
+
|
20
|
+
- **misc**: Update enableStreaming name, closes [#8995](https://github.com/lobehub/lobe-chat/issues/8995) ([7c7de40](https://github.com/lobehub/lobe-chat/commit/7c7de40))
|
21
|
+
|
22
|
+
</details>
|
23
|
+
|
24
|
+
<div align="right">
|
25
|
+
|
26
|
+
[](#readme-top)
|
27
|
+
|
28
|
+
</div>
|
29
|
+
|
5
30
|
## [Version 1.119.0](https://github.com/lobehub/lobe-chat/compare/v1.118.8...v1.119.0)
|
6
31
|
|
7
32
|
<sup>Released on **2025-08-30**</sup>
|
package/changelog/v1.json
CHANGED
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@lobehub/chat",
|
3
|
-
"version": "1.119.
|
3
|
+
"version": "1.119.1",
|
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",
|
@@ -0,0 +1,368 @@
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest';
|
2
|
+
|
3
|
+
import { mergeMultipleChatMethodOptions } from './mergeChatMethodOptions';
|
4
|
+
|
5
|
+
// Mock debug to avoid console output
|
6
|
+
vi.mock('debug', () => ({
|
7
|
+
default: () => vi.fn(),
|
8
|
+
}));
|
9
|
+
|
10
|
+
describe('mergeMultipleChatMethodOptions', () => {
|
11
|
+
it('should return empty options when given empty array', () => {
|
12
|
+
const result = mergeMultipleChatMethodOptions([]);
|
13
|
+
|
14
|
+
expect(result).toEqual({
|
15
|
+
callback: expect.any(Object),
|
16
|
+
headers: {},
|
17
|
+
requestHeaders: {},
|
18
|
+
});
|
19
|
+
});
|
20
|
+
|
21
|
+
it('should merge headers from multiple options', () => {
|
22
|
+
const options = [
|
23
|
+
{ headers: { 'Content-Type': 'application/json' } },
|
24
|
+
{ headers: { Authorization: 'Bearer token' } },
|
25
|
+
{ headers: { 'X-Custom': 'value' } },
|
26
|
+
];
|
27
|
+
|
28
|
+
const result = mergeMultipleChatMethodOptions(options);
|
29
|
+
|
30
|
+
expect(result.headers).toEqual({
|
31
|
+
'Content-Type': 'application/json',
|
32
|
+
'Authorization': 'Bearer token',
|
33
|
+
'X-Custom': 'value',
|
34
|
+
});
|
35
|
+
});
|
36
|
+
|
37
|
+
it('should merge requestHeaders from multiple options', () => {
|
38
|
+
const options = [
|
39
|
+
{ requestHeaders: { 'User-Agent': 'test' } },
|
40
|
+
{ requestHeaders: { Accept: 'application/json' } },
|
41
|
+
];
|
42
|
+
|
43
|
+
const result = mergeMultipleChatMethodOptions(options);
|
44
|
+
|
45
|
+
expect(result.requestHeaders).toEqual({
|
46
|
+
'User-Agent': 'test',
|
47
|
+
'Accept': 'application/json',
|
48
|
+
});
|
49
|
+
});
|
50
|
+
|
51
|
+
it('should handle header conflicts by using last value', () => {
|
52
|
+
const options = [
|
53
|
+
{ headers: { 'Content-Type': 'application/json' } },
|
54
|
+
{ headers: { 'Content-Type': 'text/plain' } },
|
55
|
+
];
|
56
|
+
|
57
|
+
const result = mergeMultipleChatMethodOptions(options);
|
58
|
+
|
59
|
+
expect(result.headers).toEqual({
|
60
|
+
'Content-Type': 'text/plain',
|
61
|
+
});
|
62
|
+
});
|
63
|
+
|
64
|
+
it('should handle requestHeaders conflicts by using last value', () => {
|
65
|
+
const options = [
|
66
|
+
{ requestHeaders: { Accept: 'text/plain' } },
|
67
|
+
{ requestHeaders: { Accept: 'application/json' } },
|
68
|
+
];
|
69
|
+
|
70
|
+
const result = mergeMultipleChatMethodOptions(options);
|
71
|
+
|
72
|
+
expect(result.requestHeaders).toEqual({ Accept: 'application/json' });
|
73
|
+
});
|
74
|
+
|
75
|
+
it('should call all onStart callbacks', async () => {
|
76
|
+
const callback1 = vi.fn();
|
77
|
+
const callback2 = vi.fn();
|
78
|
+
const options = [{ callback: { onStart: callback1 } }, { callback: { onStart: callback2 } }];
|
79
|
+
|
80
|
+
const result = mergeMultipleChatMethodOptions(options);
|
81
|
+
await result.callback?.onStart?.();
|
82
|
+
|
83
|
+
expect(callback1).toHaveBeenCalledOnce();
|
84
|
+
expect(callback2).toHaveBeenCalledOnce();
|
85
|
+
});
|
86
|
+
|
87
|
+
it('should call all onText callbacks with data', async () => {
|
88
|
+
const callback1 = vi.fn();
|
89
|
+
const callback2 = vi.fn();
|
90
|
+
const testData = 'test text';
|
91
|
+
const options = [{ callback: { onText: callback1 } }, { callback: { onText: callback2 } }];
|
92
|
+
|
93
|
+
const result = mergeMultipleChatMethodOptions(options);
|
94
|
+
await result.callback?.onText?.(testData);
|
95
|
+
|
96
|
+
expect(callback1).toHaveBeenCalledWith(testData);
|
97
|
+
expect(callback2).toHaveBeenCalledWith(testData);
|
98
|
+
});
|
99
|
+
|
100
|
+
it('should call all onCompletion callbacks with data', async () => {
|
101
|
+
const callback1 = vi.fn();
|
102
|
+
const callback2 = vi.fn();
|
103
|
+
const completionUsageData = {
|
104
|
+
text: 'completion text',
|
105
|
+
usage: {
|
106
|
+
inputTextTokens: 5,
|
107
|
+
outputTextTokens: 10,
|
108
|
+
totalTokens: 15,
|
109
|
+
},
|
110
|
+
};
|
111
|
+
const options = [
|
112
|
+
{ callback: { onCompletion: callback1 } },
|
113
|
+
{ callback: { onCompletion: callback2 } },
|
114
|
+
];
|
115
|
+
|
116
|
+
const result = mergeMultipleChatMethodOptions(options);
|
117
|
+
await result.callback?.onCompletion?.(completionUsageData);
|
118
|
+
|
119
|
+
expect(callback1).toHaveBeenCalledWith(completionUsageData);
|
120
|
+
expect(callback2).toHaveBeenCalledWith(completionUsageData);
|
121
|
+
});
|
122
|
+
|
123
|
+
it('should call all onFinal callbacks with data', async () => {
|
124
|
+
const callback1 = vi.fn();
|
125
|
+
const callback2 = vi.fn();
|
126
|
+
const finalUsageData = {
|
127
|
+
text: 'final message',
|
128
|
+
usage: {
|
129
|
+
inputTextTokens: 3,
|
130
|
+
outputTextTokens: 7,
|
131
|
+
totalTokens: 10,
|
132
|
+
},
|
133
|
+
};
|
134
|
+
const options = [{ callback: { onFinal: callback1 } }, { callback: { onFinal: callback2 } }];
|
135
|
+
|
136
|
+
const result = mergeMultipleChatMethodOptions(options);
|
137
|
+
await result.callback?.onFinal?.(finalUsageData);
|
138
|
+
|
139
|
+
expect(callback1).toHaveBeenCalledWith(finalUsageData);
|
140
|
+
expect(callback2).toHaveBeenCalledWith(finalUsageData);
|
141
|
+
});
|
142
|
+
|
143
|
+
it('should call all onGrounding callbacks from multiple options', async () => {
|
144
|
+
const g1 = vi.fn();
|
145
|
+
const g2 = vi.fn();
|
146
|
+
const grounding = { citations: [{ title: 't', url: 'u' }] };
|
147
|
+
|
148
|
+
const result = mergeMultipleChatMethodOptions([
|
149
|
+
{ callback: { onGrounding: g1 } },
|
150
|
+
{ callback: { onGrounding: g2 } },
|
151
|
+
]);
|
152
|
+
|
153
|
+
await result.callback?.onGrounding?.(grounding);
|
154
|
+
expect(g1).toHaveBeenCalledWith(grounding);
|
155
|
+
expect(g2).toHaveBeenCalledWith(grounding);
|
156
|
+
});
|
157
|
+
|
158
|
+
it('should be no-op when invoking missing callback types', async () => {
|
159
|
+
const result = mergeMultipleChatMethodOptions([{ headers: { A: 'b' } }]);
|
160
|
+
|
161
|
+
await expect(result.callback?.onThinking?.('t')).resolves.toBeUndefined();
|
162
|
+
await expect(result.callback?.onGrounding?.({ citations: [] })).resolves.toBeUndefined();
|
163
|
+
await expect(
|
164
|
+
result.callback?.onToolsCalling?.({ chunk: [], toolsCalling: [] }),
|
165
|
+
).resolves.toBeUndefined();
|
166
|
+
await expect(
|
167
|
+
result.callback?.onUsage?.({ inputTextTokens: 1, outputTextTokens: 2, totalTokens: 3 }),
|
168
|
+
).resolves.toBeUndefined();
|
169
|
+
});
|
170
|
+
|
171
|
+
it('should pass-through already normalized usage (onUsage)', async () => {
|
172
|
+
const onUsage = vi.fn();
|
173
|
+
const onUsageData = { inputTextTokens: 2, outputTextTokens: 3, totalTokens: 5 };
|
174
|
+
|
175
|
+
const result = mergeMultipleChatMethodOptions([{ callback: { onUsage } }]);
|
176
|
+
|
177
|
+
await result.callback?.onUsage?.(onUsageData);
|
178
|
+
expect(onUsage).toHaveBeenCalledWith(onUsageData);
|
179
|
+
});
|
180
|
+
|
181
|
+
it('should isolate errors for non-text callbacks (onFinal)', async () => {
|
182
|
+
const err = vi.fn().mockRejectedValue(new Error('boom'));
|
183
|
+
const ok = vi.fn();
|
184
|
+
const errorTestUsageData = {
|
185
|
+
text: 'final',
|
186
|
+
usage: { inputTextTokens: 1, outputTextTokens: 1, totalTokens: 2 },
|
187
|
+
};
|
188
|
+
|
189
|
+
const result = mergeMultipleChatMethodOptions([
|
190
|
+
{ callback: { onFinal: err } },
|
191
|
+
{ callback: { onFinal: ok } },
|
192
|
+
]);
|
193
|
+
|
194
|
+
await expect(result.callback?.onFinal?.(errorTestUsageData)).resolves.toBeUndefined();
|
195
|
+
|
196
|
+
expect(err).toHaveBeenCalled();
|
197
|
+
expect(ok).toHaveBeenCalled();
|
198
|
+
});
|
199
|
+
|
200
|
+
it('should isolate errors between different callback types', async () => {
|
201
|
+
const errorCallback = vi.fn().mockRejectedValue(new Error('onStart error'));
|
202
|
+
const successCallback = vi.fn();
|
203
|
+
|
204
|
+
const result = mergeMultipleChatMethodOptions([
|
205
|
+
{ callback: { onStart: errorCallback } },
|
206
|
+
{ callback: { onText: successCallback } },
|
207
|
+
]);
|
208
|
+
|
209
|
+
// onStart throws error but should not affect onText
|
210
|
+
await expect(result.callback?.onStart?.()).resolves.toBeUndefined();
|
211
|
+
await expect(result.callback?.onText?.('test text')).resolves.toBeUndefined();
|
212
|
+
|
213
|
+
expect(errorCallback).toHaveBeenCalled();
|
214
|
+
expect(successCallback).toHaveBeenCalledWith('test text');
|
215
|
+
});
|
216
|
+
|
217
|
+
it('should isolate errors in onCompletion between multiple options', async () => {
|
218
|
+
const optionACallback = vi.fn().mockRejectedValue(new Error('Option A error'));
|
219
|
+
const optionBCallback = vi.fn();
|
220
|
+
const completionData = {
|
221
|
+
text: 'completion',
|
222
|
+
usage: { inputTextTokens: 2, outputTextTokens: 3, totalTokens: 5 },
|
223
|
+
};
|
224
|
+
|
225
|
+
const result = mergeMultipleChatMethodOptions([
|
226
|
+
{ callback: { onCompletion: optionACallback } }, // OptionA with error
|
227
|
+
{ callback: { onCompletion: optionBCallback } }, // OptionB should still work
|
228
|
+
]);
|
229
|
+
|
230
|
+
await expect(result.callback?.onCompletion?.(completionData)).resolves.toBeUndefined();
|
231
|
+
|
232
|
+
expect(optionACallback).toHaveBeenCalledWith(completionData);
|
233
|
+
expect(optionBCallback).toHaveBeenCalledWith(completionData);
|
234
|
+
});
|
235
|
+
|
236
|
+
it('should isolate errors in onUsage between multiple options', async () => {
|
237
|
+
const errorUsageCallback = vi.fn().mockRejectedValue(new Error('Usage error'));
|
238
|
+
const successUsageCallback = vi.fn();
|
239
|
+
const usageData = { inputTextTokens: 10, outputTextTokens: 20, totalTokens: 30 };
|
240
|
+
|
241
|
+
const result = mergeMultipleChatMethodOptions([
|
242
|
+
{ callback: { onUsage: errorUsageCallback } },
|
243
|
+
{ callback: { onUsage: successUsageCallback } },
|
244
|
+
]);
|
245
|
+
|
246
|
+
await expect(result.callback?.onUsage?.(usageData)).resolves.toBeUndefined();
|
247
|
+
|
248
|
+
expect(errorUsageCallback).toHaveBeenCalledWith(usageData);
|
249
|
+
expect(successUsageCallback).toHaveBeenCalledWith(usageData);
|
250
|
+
});
|
251
|
+
|
252
|
+
it('should isolate errors across mixed callback types from different options', async () => {
|
253
|
+
const errorOnStart = vi.fn().mockRejectedValue(new Error('Start error'));
|
254
|
+
const errorOnFinal = vi.fn().mockRejectedValue(new Error('Final error'));
|
255
|
+
const successOnText = vi.fn();
|
256
|
+
const successOnCompletion = vi.fn();
|
257
|
+
|
258
|
+
const finalData = {
|
259
|
+
text: 'final',
|
260
|
+
usage: { inputTextTokens: 1, outputTextTokens: 2, totalTokens: 3 },
|
261
|
+
};
|
262
|
+
const completionData = {
|
263
|
+
text: 'completion',
|
264
|
+
usage: { inputTextTokens: 3, outputTextTokens: 4, totalTokens: 7 },
|
265
|
+
};
|
266
|
+
|
267
|
+
const result = mergeMultipleChatMethodOptions([
|
268
|
+
{ callback: { onStart: errorOnStart, onText: successOnText } }, // OptionA: error + success
|
269
|
+
{ callback: { onFinal: errorOnFinal, onCompletion: successOnCompletion } }, // OptionB: error + success
|
270
|
+
]);
|
271
|
+
|
272
|
+
// All callbacks should be executed despite individual errors
|
273
|
+
await expect(result.callback?.onStart?.()).resolves.toBeUndefined();
|
274
|
+
await expect(result.callback?.onText?.('test')).resolves.toBeUndefined();
|
275
|
+
await expect(result.callback?.onFinal?.(finalData)).resolves.toBeUndefined();
|
276
|
+
await expect(result.callback?.onCompletion?.(completionData)).resolves.toBeUndefined();
|
277
|
+
|
278
|
+
expect(errorOnStart).toHaveBeenCalled();
|
279
|
+
expect(errorOnFinal).toHaveBeenCalledWith(finalData);
|
280
|
+
expect(successOnText).toHaveBeenCalledWith('test');
|
281
|
+
expect(successOnCompletion).toHaveBeenCalledWith(completionData);
|
282
|
+
});
|
283
|
+
|
284
|
+
it('should handle options with no callbacks', async () => {
|
285
|
+
const callback1 = vi.fn();
|
286
|
+
const options = [
|
287
|
+
{ headers: { 'Content-Type': 'application/json' } },
|
288
|
+
{ callback: { onText: callback1 } },
|
289
|
+
{ requestHeaders: { Accept: 'application/json' } },
|
290
|
+
];
|
291
|
+
|
292
|
+
const result = mergeMultipleChatMethodOptions(options);
|
293
|
+
await result.callback?.onText?.('test');
|
294
|
+
|
295
|
+
expect(callback1).toHaveBeenCalledWith('test');
|
296
|
+
});
|
297
|
+
|
298
|
+
it('should handle all callback types', async () => {
|
299
|
+
const callbacks = {
|
300
|
+
onStart: vi.fn(),
|
301
|
+
onText: vi.fn(),
|
302
|
+
onCompletion: vi.fn(),
|
303
|
+
onFinal: vi.fn(),
|
304
|
+
onGrounding: vi.fn(),
|
305
|
+
onThinking: vi.fn(),
|
306
|
+
onToolsCalling: vi.fn(),
|
307
|
+
onUsage: vi.fn(),
|
308
|
+
};
|
309
|
+
|
310
|
+
const options = [{ callback: callbacks }];
|
311
|
+
const result = mergeMultipleChatMethodOptions(options);
|
312
|
+
|
313
|
+
const allTypesUsageData = {
|
314
|
+
inputTextTokens: 5,
|
315
|
+
outputTextTokens: 5,
|
316
|
+
totalTokens: 10,
|
317
|
+
};
|
318
|
+
|
319
|
+
const completionData = { text: 'completion', usage: allTypesUsageData };
|
320
|
+
const finalData = { text: 'final message', usage: allTypesUsageData };
|
321
|
+
const groundingData = { citations: [{ title: 'grounding', url: 'http://example.com' }] };
|
322
|
+
const toolsCallingData = {
|
323
|
+
chunk: [{ index: 0, id: 'tool1', function: { name: 'test', arguments: '{}' } }],
|
324
|
+
toolsCalling: [
|
325
|
+
{ id: 'tool1', type: 'function', function: { name: 'test', arguments: '{}' } },
|
326
|
+
],
|
327
|
+
};
|
328
|
+
|
329
|
+
await result.callback?.onStart?.();
|
330
|
+
await result.callback?.onText?.('text');
|
331
|
+
await result.callback?.onCompletion?.(completionData);
|
332
|
+
await result.callback?.onFinal?.(finalData);
|
333
|
+
await result.callback?.onGrounding?.(groundingData);
|
334
|
+
await result.callback?.onThinking?.('thinking content');
|
335
|
+
await result.callback?.onToolsCalling?.(toolsCallingData);
|
336
|
+
await result.callback?.onUsage?.(allTypesUsageData);
|
337
|
+
|
338
|
+
expect(callbacks.onStart).toHaveBeenCalled();
|
339
|
+
expect(callbacks.onText).toHaveBeenCalledWith('text');
|
340
|
+
expect(callbacks.onCompletion).toHaveBeenCalledWith(completionData);
|
341
|
+
expect(callbacks.onFinal).toHaveBeenCalledWith(finalData);
|
342
|
+
expect(callbacks.onGrounding).toHaveBeenCalledWith(groundingData);
|
343
|
+
expect(callbacks.onThinking).toHaveBeenCalledWith('thinking content');
|
344
|
+
expect(callbacks.onToolsCalling).toHaveBeenCalledWith(toolsCallingData);
|
345
|
+
expect(callbacks.onUsage).toHaveBeenCalledWith(allTypesUsageData);
|
346
|
+
});
|
347
|
+
|
348
|
+
it('should handle mixed options with headers and callbacks', () => {
|
349
|
+
const callback1 = vi.fn();
|
350
|
+
const callback2 = vi.fn();
|
351
|
+
const options = [
|
352
|
+
{
|
353
|
+
headers: { 'Content-Type': 'application/json' },
|
354
|
+
callback: { onStart: callback1 },
|
355
|
+
},
|
356
|
+
{
|
357
|
+
requestHeaders: { Authorization: 'Bearer token' },
|
358
|
+
callback: { onText: callback2 },
|
359
|
+
},
|
360
|
+
];
|
361
|
+
|
362
|
+
const result = mergeMultipleChatMethodOptions(options);
|
363
|
+
|
364
|
+
expect(result.headers).toEqual({ 'Content-Type': 'application/json' });
|
365
|
+
expect(result.requestHeaders).toEqual({ Authorization: 'Bearer token' });
|
366
|
+
expect(result.callback).toBeDefined();
|
367
|
+
});
|
368
|
+
});
|
@@ -0,0 +1,123 @@
|
|
1
|
+
import { ChatMethodOptions } from '@lobechat/model-runtime';
|
2
|
+
import debug from 'debug';
|
3
|
+
|
4
|
+
const log = debug('model-runtime:helpers:mergeChatMethodOptions');
|
5
|
+
|
6
|
+
export const mergeMultipleChatMethodOptions = (options: ChatMethodOptions[]): ChatMethodOptions => {
|
7
|
+
let completionOptions: ChatMethodOptions = {};
|
8
|
+
completionOptions.callback = {
|
9
|
+
onCompletion: async (data) => {
|
10
|
+
for (const option of options) {
|
11
|
+
if (option.callback?.onCompletion) {
|
12
|
+
try {
|
13
|
+
await option.callback.onCompletion(data);
|
14
|
+
} catch (error) {
|
15
|
+
log('onCompletion callback error:');
|
16
|
+
log(JSON.stringify(error));
|
17
|
+
}
|
18
|
+
}
|
19
|
+
}
|
20
|
+
},
|
21
|
+
onFinal: async (data) => {
|
22
|
+
for (const option of options) {
|
23
|
+
if (option.callback?.onFinal) {
|
24
|
+
try {
|
25
|
+
await option.callback.onFinal(data);
|
26
|
+
} catch (error) {
|
27
|
+
log('onFinal callback error:');
|
28
|
+
log(JSON.stringify(error));
|
29
|
+
}
|
30
|
+
}
|
31
|
+
}
|
32
|
+
},
|
33
|
+
onGrounding: async (data) => {
|
34
|
+
for (const option of options) {
|
35
|
+
if (option.callback?.onGrounding) {
|
36
|
+
try {
|
37
|
+
await option.callback.onGrounding(data);
|
38
|
+
} catch (error) {
|
39
|
+
log('onGrounding callback error:');
|
40
|
+
log(JSON.stringify(error));
|
41
|
+
}
|
42
|
+
}
|
43
|
+
}
|
44
|
+
},
|
45
|
+
onStart: async () => {
|
46
|
+
for (const option of options) {
|
47
|
+
if (option.callback?.onStart) {
|
48
|
+
try {
|
49
|
+
await option.callback.onStart();
|
50
|
+
} catch (error) {
|
51
|
+
log('onStart callback error:');
|
52
|
+
log(JSON.stringify(error));
|
53
|
+
}
|
54
|
+
}
|
55
|
+
}
|
56
|
+
},
|
57
|
+
onText: async (data) => {
|
58
|
+
for (const option of options) {
|
59
|
+
if (option.callback?.onText) {
|
60
|
+
try {
|
61
|
+
await option.callback.onText(data);
|
62
|
+
} catch (error) {
|
63
|
+
log('onText callback error:');
|
64
|
+
log(JSON.stringify(error));
|
65
|
+
}
|
66
|
+
}
|
67
|
+
}
|
68
|
+
},
|
69
|
+
onThinking: async (data) => {
|
70
|
+
for (const option of options) {
|
71
|
+
if (option.callback?.onThinking) {
|
72
|
+
try {
|
73
|
+
await option.callback.onThinking(data);
|
74
|
+
} catch (error) {
|
75
|
+
log('onThinking callback error:');
|
76
|
+
log(JSON.stringify(error));
|
77
|
+
}
|
78
|
+
}
|
79
|
+
}
|
80
|
+
},
|
81
|
+
onToolsCalling: async (data) => {
|
82
|
+
for (const option of options) {
|
83
|
+
if (option.callback?.onToolsCalling) {
|
84
|
+
try {
|
85
|
+
await option.callback.onToolsCalling(data);
|
86
|
+
} catch (error) {
|
87
|
+
log('onToolsCalling callback error:');
|
88
|
+
log(JSON.stringify(error));
|
89
|
+
}
|
90
|
+
}
|
91
|
+
}
|
92
|
+
},
|
93
|
+
onUsage: async (data) => {
|
94
|
+
for (const option of options) {
|
95
|
+
if (option.callback?.onUsage) {
|
96
|
+
try {
|
97
|
+
await option.callback.onUsage(data);
|
98
|
+
} catch (error) {
|
99
|
+
log('onUsage callback error:');
|
100
|
+
log(JSON.stringify(error));
|
101
|
+
}
|
102
|
+
}
|
103
|
+
}
|
104
|
+
},
|
105
|
+
};
|
106
|
+
completionOptions.headers = options.reduce((acc, option) => {
|
107
|
+
if (option)
|
108
|
+
return {
|
109
|
+
...acc,
|
110
|
+
...option.headers,
|
111
|
+
};
|
112
|
+
return acc;
|
113
|
+
}, {});
|
114
|
+
completionOptions.requestHeaders = options.reduce((acc, option) => {
|
115
|
+
if (option)
|
116
|
+
return {
|
117
|
+
...acc,
|
118
|
+
...option.requestHeaders,
|
119
|
+
};
|
120
|
+
return acc;
|
121
|
+
}, {});
|
122
|
+
return completionOptions;
|
123
|
+
};
|