@lobehub/chat 1.94.4 → 1.94.5
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 +33 -0
- package/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/changelog/v1.json +9 -0
- package/package.json +3 -2
- package/scripts/i18nWorkflow/genDefaultLocale.ts +2 -2
- package/scripts/i18nWorkflow/genDiff.ts +8 -9
- package/scripts/i18nWorkflow/utils.ts +14 -1
- package/src/config/aiModels/openai.ts +120 -0
- package/src/const/models.ts +26 -1
- package/src/libs/model-runtime/openai/index.ts +33 -11
- package/src/libs/model-runtime/types/chat.ts +2 -0
- package/src/libs/model-runtime/utils/openaiCompatibleFactory/index.ts +10 -8
- package/src/libs/model-runtime/utils/streams/openai/__snapshots__/responsesStream.test.ts.snap +6 -6
- package/src/libs/model-runtime/utils/streams/openai/responsesStream.ts +38 -2
- package/src/services/chat.ts +8 -4
package/CHANGELOG.md
CHANGED
@@ -2,6 +2,39 @@
|
|
2
2
|
|
3
3
|
# Changelog
|
4
4
|
|
5
|
+
### [Version 1.94.5](https://github.com/lobehub/lobe-chat/compare/v1.94.4...v1.94.5)
|
6
|
+
|
7
|
+
<sup>Released on **2025-06-12**</sup>
|
8
|
+
|
9
|
+
#### 🐛 Bug Fixes
|
10
|
+
|
11
|
+
- **chat**: Improve response animation merging logic.
|
12
|
+
|
13
|
+
#### 💄 Styles
|
14
|
+
|
15
|
+
- **misc**: Support `web_search_preview` & fix some bug form OpenAI Response API.
|
16
|
+
|
17
|
+
<br/>
|
18
|
+
|
19
|
+
<details>
|
20
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
21
|
+
|
22
|
+
#### What's fixed
|
23
|
+
|
24
|
+
- **chat**: Improve response animation merging logic, closes [#8160](https://github.com/lobehub/lobe-chat/issues/8160) ([9d81cdc](https://github.com/lobehub/lobe-chat/commit/9d81cdc))
|
25
|
+
|
26
|
+
#### Styles
|
27
|
+
|
28
|
+
- **misc**: Support `web_search_preview` & fix some bug form OpenAI Response API, closes [#8131](https://github.com/lobehub/lobe-chat/issues/8131) ([b2983f0](https://github.com/lobehub/lobe-chat/commit/b2983f0))
|
29
|
+
|
30
|
+
</details>
|
31
|
+
|
32
|
+
<div align="right">
|
33
|
+
|
34
|
+
[](#readme-top)
|
35
|
+
|
36
|
+
</div>
|
37
|
+
|
5
38
|
### [Version 1.94.4](https://github.com/lobehub/lobe-chat/compare/v1.94.3...v1.94.4)
|
6
39
|
|
7
40
|
<sup>Released on **2025-06-11**</sup>
|
package/README.md
CHANGED
@@ -335,7 +335,7 @@ In addition, these plugins are not limited to news aggregation, but can also ext
|
|
335
335
|
| [Bing_websearch](https://lobechat.com/discover/plugin/Bingsearch-identifier)<br/><sup>By **FineHow** on **2024-12-22**</sup> | Search for information from the internet base BingApi<br/>`bingsearch` |
|
336
336
|
| [Google CSE](https://lobechat.com/discover/plugin/google-cse)<br/><sup>By **vsnthdev** on **2024-12-02**</sup> | Searches Google through their official CSE API.<br/>`web` `search` |
|
337
337
|
|
338
|
-
> 📊 Total plugins: [<kbd>**
|
338
|
+
> 📊 Total plugins: [<kbd>**42**</kbd>](https://lobechat.com/discover/plugins)
|
339
339
|
|
340
340
|
<!-- PLUGIN LIST -->
|
341
341
|
|
package/README.zh-CN.md
CHANGED
@@ -328,7 +328,7 @@ LobeChat 的插件生态系统是其核心功能的重要扩展,它极大地
|
|
328
328
|
| [必应网页搜索](https://lobechat.com/discover/plugin/Bingsearch-identifier)<br/><sup>By **FineHow** on **2024-12-22**</sup> | 通过 BingApi 搜索互联网上的信息<br/>`bingsearch` |
|
329
329
|
| [谷歌自定义搜索引擎](https://lobechat.com/discover/plugin/google-cse)<br/><sup>By **vsnthdev** on **2024-12-02**</sup> | 通过他们的官方自定义搜索引擎 API 搜索谷歌。<br/>`网络` `搜索` |
|
330
330
|
|
331
|
-
> 📊 Total plugins: [<kbd>**
|
331
|
+
> 📊 Total plugins: [<kbd>**42**</kbd>](https://lobechat.com/discover/plugins)
|
332
332
|
|
333
333
|
<!-- PLUGIN LIST -->
|
334
334
|
|
package/changelog/v1.json
CHANGED
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@lobehub/chat",
|
3
|
-
"version": "1.94.
|
3
|
+
"version": "1.94.5",
|
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",
|
@@ -54,7 +54,7 @@
|
|
54
54
|
"dev:desktop": "next dev --turbopack -p 3015",
|
55
55
|
"docs:i18n": "lobe-i18n md && npm run lint:md && npm run lint:mdx && prettier -c --write locales/**/*",
|
56
56
|
"docs:seo": "lobe-seo && npm run lint:mdx",
|
57
|
-
"i18n": "npm run workflow:i18n && lobe-i18n",
|
57
|
+
"i18n": "npm run workflow:i18n && lobe-i18n && prettier -c --write \"locales/**\"",
|
58
58
|
"lint": "npm run lint:ts && npm run lint:style && npm run type-check && npm run lint:circular",
|
59
59
|
"lint:circular": "dpdm src/**/*.ts --no-warning --no-tree --exit-code circular:1 --no-progress -T true --skip-dynamic-imports circular",
|
60
60
|
"lint:md": "remark . --silent --output",
|
@@ -282,6 +282,7 @@
|
|
282
282
|
"@next/bundle-analyzer": "^15.3.3",
|
283
283
|
"@next/eslint-plugin-next": "^15.3.3",
|
284
284
|
"@peculiar/webcrypto": "^1.5.0",
|
285
|
+
"@prettier/sync": "^0.6.1",
|
285
286
|
"@semantic-release/exec": "^6.0.3",
|
286
287
|
"@testing-library/jest-dom": "^6.6.3",
|
287
288
|
"@testing-library/react": "^16.3.0",
|
@@ -2,7 +2,7 @@ import { consola } from 'consola';
|
|
2
2
|
import { colors } from 'consola/utils';
|
3
3
|
|
4
4
|
import { entryLocaleJsonFilepath, i18nConfig, srcDefaultLocales } from './const';
|
5
|
-
import { tagWhite,
|
5
|
+
import { tagWhite, writeJSONWithPrettier } from './utils';
|
6
6
|
|
7
7
|
export const genDefaultLocale = () => {
|
8
8
|
consola.info(`Default locale is ${i18nConfig.entryLocale}...`);
|
@@ -13,7 +13,7 @@ export const genDefaultLocale = () => {
|
|
13
13
|
|
14
14
|
for (const [ns, value] of data) {
|
15
15
|
const filepath = entryLocaleJsonFilepath(`${ns}.json`);
|
16
|
-
|
16
|
+
writeJSONWithPrettier(filepath, value);
|
17
17
|
consola.success(tagWhite(ns), colors.gray(filepath));
|
18
18
|
}
|
19
19
|
};
|
@@ -10,10 +10,10 @@ import {
|
|
10
10
|
outputLocaleJsonFilepath,
|
11
11
|
srcDefaultLocales,
|
12
12
|
} from './const';
|
13
|
-
import { readJSON, tagWhite,
|
13
|
+
import { readJSON, tagWhite, writeJSONWithPrettier } from './utils';
|
14
14
|
|
15
15
|
export const genDiff = () => {
|
16
|
-
consola.start(`
|
16
|
+
consola.start(`Remove diff analysis...`);
|
17
17
|
|
18
18
|
const resources = require(srcDefaultLocales);
|
19
19
|
const data = Object.entries(resources.default);
|
@@ -21,27 +21,26 @@ export const genDiff = () => {
|
|
21
21
|
for (const [ns, devJSON] of data) {
|
22
22
|
const filepath = entryLocaleJsonFilepath(`${ns}.json`);
|
23
23
|
if (!existsSync(filepath)) continue;
|
24
|
-
const
|
24
|
+
const previousProdJSON = readJSON(filepath);
|
25
25
|
|
26
|
-
const diffResult = diff(
|
27
|
-
|
28
|
-
if (remove.length === 0) {
|
26
|
+
const diffResult = diff(previousProdJSON, devJSON as any);
|
27
|
+
if (diffResult.length === 0) {
|
29
28
|
consola.success(tagWhite(ns), colors.gray(filepath));
|
30
29
|
continue;
|
31
30
|
}
|
32
31
|
|
33
32
|
const clearLocals = [];
|
34
33
|
|
35
|
-
for (const locale of
|
34
|
+
for (const locale of i18nConfig.outputLocales) {
|
36
35
|
const localeFilepath = outputLocaleJsonFilepath(locale, `${ns}.json`);
|
37
36
|
if (!existsSync(localeFilepath)) continue;
|
38
37
|
const localeJSON = readJSON(localeFilepath);
|
39
38
|
|
40
|
-
for (const item of
|
39
|
+
for (const item of diffResult) {
|
41
40
|
unset(localeJSON, item.path);
|
42
41
|
}
|
43
42
|
|
44
|
-
|
43
|
+
writeJSONWithPrettier(localeFilepath, localeJSON);
|
45
44
|
clearLocals.push(locale);
|
46
45
|
}
|
47
46
|
consola.info('clear', clearLocals);
|
@@ -2,9 +2,13 @@ import { consola } from 'consola';
|
|
2
2
|
import { colors } from 'consola/utils';
|
3
3
|
import { readFileSync, writeFileSync } from 'node:fs';
|
4
4
|
import { resolve } from 'node:path';
|
5
|
-
|
5
|
+
import prettier from "@prettier/sync";
|
6
6
|
import i18nConfig from '../../.i18nrc';
|
7
7
|
|
8
|
+
let prettierOptions = prettier.resolveConfig(
|
9
|
+
resolve(__dirname, '../../.prettierrc.js')
|
10
|
+
);
|
11
|
+
|
8
12
|
export const readJSON = (filePath: string) => {
|
9
13
|
const data = readFileSync(filePath, 'utf8');
|
10
14
|
return JSON.parse(data);
|
@@ -15,6 +19,15 @@ export const writeJSON = (filePath: string, data: any) => {
|
|
15
19
|
writeFileSync(filePath, jsonStr, 'utf8');
|
16
20
|
};
|
17
21
|
|
22
|
+
export const writeJSONWithPrettier = (filePath: string, data: any) => {
|
23
|
+
const jsonStr = JSON.stringify(data, null, 2);
|
24
|
+
const formatted = prettier.format(jsonStr, {
|
25
|
+
...prettierOptions,
|
26
|
+
parser: 'json',
|
27
|
+
});
|
28
|
+
writeFileSync(filePath, formatted, 'utf8');
|
29
|
+
};
|
30
|
+
|
18
31
|
export const genResourcesContent = (locales: string[]) => {
|
19
32
|
let index = '';
|
20
33
|
let indexObj = '';
|
@@ -59,6 +59,7 @@ export const openaiChatModels: AIChatModelCard[] = [
|
|
59
59
|
{
|
60
60
|
abilities: {
|
61
61
|
functionCall: true,
|
62
|
+
search: true,
|
62
63
|
vision: true,
|
63
64
|
},
|
64
65
|
contextWindowTokens: 1_047_576,
|
@@ -73,11 +74,15 @@ export const openaiChatModels: AIChatModelCard[] = [
|
|
73
74
|
output: 8,
|
74
75
|
},
|
75
76
|
releasedAt: '2025-04-14',
|
77
|
+
settings: {
|
78
|
+
searchImpl: 'params',
|
79
|
+
},
|
76
80
|
type: 'chat',
|
77
81
|
},
|
78
82
|
{
|
79
83
|
abilities: {
|
80
84
|
functionCall: true,
|
85
|
+
search: true,
|
81
86
|
vision: true,
|
82
87
|
},
|
83
88
|
contextWindowTokens: 1_047_576,
|
@@ -93,6 +98,9 @@ export const openaiChatModels: AIChatModelCard[] = [
|
|
93
98
|
output: 1.6,
|
94
99
|
},
|
95
100
|
releasedAt: '2025-04-14',
|
101
|
+
settings: {
|
102
|
+
searchImpl: 'params',
|
103
|
+
},
|
96
104
|
type: 'chat',
|
97
105
|
},
|
98
106
|
{
|
@@ -135,6 +143,28 @@ export const openaiChatModels: AIChatModelCard[] = [
|
|
135
143
|
},
|
136
144
|
type: 'chat',
|
137
145
|
},
|
146
|
+
{
|
147
|
+
abilities: {
|
148
|
+
functionCall: true,
|
149
|
+
reasoning: true,
|
150
|
+
vision: true,
|
151
|
+
},
|
152
|
+
contextWindowTokens: 200_000,
|
153
|
+
description:
|
154
|
+
'o1 系列模型经过强化学习训练,能够在回答前进行思考,并执行复杂的推理任务。o1-pro 模型使用了更多计算资源,以进行更深入的思考,从而持续提供更优质的回答。',
|
155
|
+
displayName: 'o1-pro',
|
156
|
+
id: 'o1-pro',
|
157
|
+
maxOutput: 100_000,
|
158
|
+
pricing: {
|
159
|
+
input: 150,
|
160
|
+
output: 600,
|
161
|
+
},
|
162
|
+
releasedAt: '2025-03-19',
|
163
|
+
settings: {
|
164
|
+
extendParams: ['reasoningEffort'],
|
165
|
+
},
|
166
|
+
type: 'chat',
|
167
|
+
},
|
138
168
|
{
|
139
169
|
abilities: {
|
140
170
|
reasoning: true,
|
@@ -158,6 +188,7 @@ export const openaiChatModels: AIChatModelCard[] = [
|
|
158
188
|
},
|
159
189
|
{
|
160
190
|
abilities: {
|
191
|
+
functionCall: true,
|
161
192
|
reasoning: true,
|
162
193
|
vision: true,
|
163
194
|
},
|
@@ -220,6 +251,7 @@ export const openaiChatModels: AIChatModelCard[] = [
|
|
220
251
|
{
|
221
252
|
abilities: {
|
222
253
|
functionCall: true,
|
254
|
+
search: true,
|
223
255
|
vision: true,
|
224
256
|
},
|
225
257
|
contextWindowTokens: 128_000,
|
@@ -234,6 +266,9 @@ export const openaiChatModels: AIChatModelCard[] = [
|
|
234
266
|
output: 0.6,
|
235
267
|
},
|
236
268
|
releasedAt: '2024-07-18',
|
269
|
+
settings: {
|
270
|
+
searchImpl: 'params',
|
271
|
+
},
|
237
272
|
type: 'chat',
|
238
273
|
},
|
239
274
|
{
|
@@ -259,6 +294,29 @@ export const openaiChatModels: AIChatModelCard[] = [
|
|
259
294
|
{
|
260
295
|
abilities: {
|
261
296
|
functionCall: true,
|
297
|
+
//search: true,
|
298
|
+
},
|
299
|
+
contextWindowTokens: 128_000,
|
300
|
+
description: 'GPT-4o mini Audio 模型,支持音频输入输出',
|
301
|
+
displayName: 'GPT-4o mini Audio',
|
302
|
+
id: 'gpt-4o-mini-audio-preview',
|
303
|
+
maxOutput: 16_384,
|
304
|
+
pricing: {
|
305
|
+
input: 0.15,
|
306
|
+
output: 0.6,
|
307
|
+
},
|
308
|
+
releasedAt: '2024-12-17',
|
309
|
+
/*
|
310
|
+
settings: {
|
311
|
+
searchImpl: 'params',
|
312
|
+
},
|
313
|
+
*/
|
314
|
+
type: 'chat',
|
315
|
+
},
|
316
|
+
{
|
317
|
+
abilities: {
|
318
|
+
functionCall: true,
|
319
|
+
search: true,
|
262
320
|
vision: true,
|
263
321
|
},
|
264
322
|
contextWindowTokens: 128_000,
|
@@ -272,6 +330,9 @@ export const openaiChatModels: AIChatModelCard[] = [
|
|
272
330
|
output: 10,
|
273
331
|
},
|
274
332
|
releasedAt: '2024-05-13',
|
333
|
+
settings: {
|
334
|
+
searchImpl: 'params',
|
335
|
+
},
|
275
336
|
type: 'chat',
|
276
337
|
},
|
277
338
|
{
|
@@ -297,6 +358,7 @@ export const openaiChatModels: AIChatModelCard[] = [
|
|
297
358
|
{
|
298
359
|
abilities: {
|
299
360
|
functionCall: true,
|
361
|
+
search: true,
|
300
362
|
vision: true,
|
301
363
|
},
|
302
364
|
contextWindowTokens: 128_000,
|
@@ -310,11 +372,15 @@ export const openaiChatModels: AIChatModelCard[] = [
|
|
310
372
|
output: 10,
|
311
373
|
},
|
312
374
|
releasedAt: '2024-11-20',
|
375
|
+
settings: {
|
376
|
+
searchImpl: 'params',
|
377
|
+
},
|
313
378
|
type: 'chat',
|
314
379
|
},
|
315
380
|
{
|
316
381
|
abilities: {
|
317
382
|
functionCall: true,
|
383
|
+
search: true,
|
318
384
|
vision: true,
|
319
385
|
},
|
320
386
|
contextWindowTokens: 128_000,
|
@@ -327,9 +393,16 @@ export const openaiChatModels: AIChatModelCard[] = [
|
|
327
393
|
output: 15,
|
328
394
|
},
|
329
395
|
releasedAt: '2024-05-13',
|
396
|
+
settings: {
|
397
|
+
searchImpl: 'params',
|
398
|
+
},
|
330
399
|
type: 'chat',
|
331
400
|
},
|
332
401
|
{
|
402
|
+
abilities: {
|
403
|
+
functionCall: true,
|
404
|
+
//search: true,
|
405
|
+
},
|
333
406
|
contextWindowTokens: 128_000,
|
334
407
|
description: 'GPT-4o Audio 模型,支持音频输入输出',
|
335
408
|
displayName: 'GPT-4o Audio',
|
@@ -340,6 +413,11 @@ export const openaiChatModels: AIChatModelCard[] = [
|
|
340
413
|
output: 10,
|
341
414
|
},
|
342
415
|
releasedAt: '2024-10-01',
|
416
|
+
/*
|
417
|
+
settings: {
|
418
|
+
searchImpl: 'params',
|
419
|
+
},
|
420
|
+
*/
|
343
421
|
type: 'chat',
|
344
422
|
},
|
345
423
|
{
|
@@ -545,6 +623,48 @@ export const openaiChatModels: AIChatModelCard[] = [
|
|
545
623
|
},
|
546
624
|
type: 'chat',
|
547
625
|
},
|
626
|
+
{
|
627
|
+
abilities: {
|
628
|
+
functionCall: true,
|
629
|
+
reasoning: true,
|
630
|
+
vision: true,
|
631
|
+
},
|
632
|
+
contextWindowTokens: 200_000,
|
633
|
+
description: 'codex-mini-latest 是 o4-mini 的微调版本,专门用于 Codex CLI。对于直接通过 API 使用,我们推荐从 gpt-4.1 开始。',
|
634
|
+
displayName: 'Codex mini',
|
635
|
+
id: 'codex-mini-latest',
|
636
|
+
maxOutput: 100_000,
|
637
|
+
pricing: {
|
638
|
+
input: 1.5,
|
639
|
+
output: 6,
|
640
|
+
},
|
641
|
+
releasedAt: '2025-06-01',
|
642
|
+
settings: {
|
643
|
+
extendParams: ['reasoningEffort'],
|
644
|
+
},
|
645
|
+
type: 'chat',
|
646
|
+
},
|
647
|
+
{
|
648
|
+
abilities: {
|
649
|
+
functionCall: true,
|
650
|
+
reasoning: true,
|
651
|
+
vision: true,
|
652
|
+
},
|
653
|
+
contextWindowTokens: 8192,
|
654
|
+
description: 'computer-use-preview 模型是专为“计算机使用工具”设计的专用模型,经过训练以理解并执行计算机相关任务。',
|
655
|
+
displayName: 'Computer Use Preview',
|
656
|
+
id: 'computer-use-preview',
|
657
|
+
maxOutput: 1024,
|
658
|
+
pricing: {
|
659
|
+
input: 3,
|
660
|
+
output: 12,
|
661
|
+
},
|
662
|
+
releasedAt: '2025-03-11',
|
663
|
+
settings: {
|
664
|
+
extendParams: ['reasoningEffort'],
|
665
|
+
},
|
666
|
+
type: 'chat',
|
667
|
+
},
|
548
668
|
];
|
549
669
|
|
550
670
|
export const openaiEmbeddingModels: AIEmbeddingModelCard[] = [
|
package/src/const/models.ts
CHANGED
@@ -6,7 +6,32 @@ export const systemToUserModels = new Set([
|
|
6
6
|
]);
|
7
7
|
|
8
8
|
// TODO: 临时写法,后续要重构成 model card 展示配置
|
9
|
-
export const disableStreamModels = new Set([
|
9
|
+
export const disableStreamModels = new Set([
|
10
|
+
'o1',
|
11
|
+
'o1-2024-12-17',
|
12
|
+
'o1-pro',
|
13
|
+
'o1-pro-2025-03-19',
|
14
|
+
/*
|
15
|
+
官网显示不支持,但是实际试下来支持 Streaming,暂时注释掉
|
16
|
+
'o3-pro',
|
17
|
+
'o3-pro-2025-06-10',
|
18
|
+
*/
|
19
|
+
'computer-use-preview',
|
20
|
+
'computer-use-preview-2025-03-11',
|
21
|
+
]);
|
22
|
+
|
23
|
+
/**
|
24
|
+
* models use Responses API only
|
25
|
+
*/
|
26
|
+
export const responsesAPIModels = new Set([
|
27
|
+
'o1-pro',
|
28
|
+
'o1-pro-2025-03-19',
|
29
|
+
'o3-pro',
|
30
|
+
'o3-pro-2025-06-10',
|
31
|
+
'codex-mini-latest',
|
32
|
+
'computer-use-preview',
|
33
|
+
'computer-use-preview-2025-03-11',
|
34
|
+
]);
|
10
35
|
|
11
36
|
/**
|
12
37
|
* models support context caching
|
@@ -2,21 +2,24 @@ import { ChatStreamPayload, ModelProvider } from '../types';
|
|
2
2
|
import { processMultiProviderModelList } from '../utils/modelParse';
|
3
3
|
import { createOpenAICompatibleRuntime } from '../utils/openaiCompatibleFactory';
|
4
4
|
import { pruneReasoningPayload } from '../utils/openaiHelpers';
|
5
|
+
import { responsesAPIModels } from '@/const/models';
|
5
6
|
|
6
7
|
export interface OpenAIModelCard {
|
7
8
|
id: string;
|
8
9
|
}
|
9
10
|
|
10
|
-
const prunePrefixes = ['o1', 'o3', 'o4'];
|
11
|
+
const prunePrefixes = ['o1', 'o3', 'o4', 'codex', 'computer-use'];
|
12
|
+
|
13
|
+
const oaiSearchContextSize = process.env.OPENAI_SEARCH_CONTEXT_SIZE; // low, medium, high
|
11
14
|
|
12
15
|
export const LobeOpenAI = createOpenAICompatibleRuntime({
|
13
16
|
baseURL: 'https://api.openai.com/v1',
|
14
17
|
chatCompletion: {
|
15
18
|
handlePayload: (payload) => {
|
16
|
-
const { model } = payload;
|
19
|
+
const { enabledSearch, model, ...rest } = payload;
|
17
20
|
|
18
|
-
if (model
|
19
|
-
return { ...
|
21
|
+
if (responsesAPIModels.has(model) || enabledSearch) {
|
22
|
+
return { ...rest, apiMode: 'responses', enabledSearch, model } as ChatStreamPayload;
|
20
23
|
}
|
21
24
|
|
22
25
|
if (prunePrefixes.some((prefix) => model.startsWith(prefix))) {
|
@@ -24,11 +27,10 @@ export const LobeOpenAI = createOpenAICompatibleRuntime({
|
|
24
27
|
}
|
25
28
|
|
26
29
|
if (model.includes('-search-')) {
|
27
|
-
const oaiSearchContextSize = process.env.OPENAI_SEARCH_CONTEXT_SIZE; // low, medium, high
|
28
|
-
|
29
30
|
return {
|
30
|
-
...
|
31
|
+
...rest,
|
31
32
|
frequency_penalty: undefined,
|
33
|
+
model,
|
32
34
|
presence_penalty: undefined,
|
33
35
|
stream: payload.stream ?? true,
|
34
36
|
temperature: undefined,
|
@@ -41,7 +43,7 @@ export const LobeOpenAI = createOpenAICompatibleRuntime({
|
|
41
43
|
} as any;
|
42
44
|
}
|
43
45
|
|
44
|
-
return { ...
|
46
|
+
return { ...rest, model, stream: payload.stream ?? true };
|
45
47
|
},
|
46
48
|
},
|
47
49
|
debug: {
|
@@ -57,17 +59,37 @@ export const LobeOpenAI = createOpenAICompatibleRuntime({
|
|
57
59
|
},
|
58
60
|
provider: ModelProvider.OpenAI,
|
59
61
|
responses: {
|
60
|
-
handlePayload: (payload
|
61
|
-
const { model } = payload;
|
62
|
+
handlePayload: (payload) => {
|
63
|
+
const { enabledSearch, model, tools, ...rest } = payload;
|
64
|
+
|
65
|
+
const openaiTools = enabledSearch
|
66
|
+
? [
|
67
|
+
...(tools || []),
|
68
|
+
{
|
69
|
+
type: 'web_search_preview',
|
70
|
+
...(oaiSearchContextSize && {
|
71
|
+
search_context_size: oaiSearchContextSize,
|
72
|
+
}),
|
73
|
+
},
|
74
|
+
]
|
75
|
+
: tools;
|
76
|
+
|
62
77
|
if (prunePrefixes.some((prefix) => model.startsWith(prefix))) {
|
63
78
|
if (!payload.reasoning) {
|
64
79
|
payload.reasoning = { summary: 'auto' };
|
65
80
|
} else {
|
66
81
|
payload.reasoning.summary = 'auto';
|
67
82
|
}
|
83
|
+
|
84
|
+
// computer-use series must set truncation as auto
|
85
|
+
if (model.startsWith('computer-use')) {
|
86
|
+
payload.truncation = 'auto';
|
87
|
+
}
|
88
|
+
|
89
|
+
return pruneReasoningPayload(payload) as any;
|
68
90
|
}
|
69
91
|
|
70
|
-
return { ...
|
92
|
+
return { ...rest, model, stream: payload.stream ?? true, tools: openaiTools } as any;
|
71
93
|
},
|
72
94
|
},
|
73
95
|
});
|
@@ -107,6 +107,7 @@ export interface ChatStreamPayload {
|
|
107
107
|
effort?: string;
|
108
108
|
summary?: string;
|
109
109
|
};
|
110
|
+
reasoning_effort?: 'low' | 'medium' | 'high';
|
110
111
|
responseMode?: 'stream' | 'json';
|
111
112
|
/**
|
112
113
|
* @title 是否开启流式请求
|
@@ -132,6 +133,7 @@ export interface ChatStreamPayload {
|
|
132
133
|
* @default 1
|
133
134
|
*/
|
134
135
|
top_p?: number;
|
136
|
+
truncation?: 'auto' | 'disabled';
|
135
137
|
}
|
136
138
|
|
137
139
|
export interface ChatMethodOptions {
|
@@ -209,14 +209,9 @@ export const createOpenAICompatibleRuntime = <T extends Record<string, any> = an
|
|
209
209
|
}
|
210
210
|
|
211
211
|
async chat(
|
212
|
-
{ responseMode,
|
212
|
+
{ responseMode, ...payload }: ChatStreamPayload,
|
213
213
|
options?: ChatMethodOptions,
|
214
214
|
) {
|
215
|
-
// new openai Response API
|
216
|
-
if (apiMode === 'responses') {
|
217
|
-
return this.handleResponseAPIMode(payload, options);
|
218
|
-
}
|
219
|
-
|
220
215
|
try {
|
221
216
|
const inputStartAt = Date.now();
|
222
217
|
const postPayload = chatCompletion?.handlePayload
|
@@ -226,6 +221,11 @@ export const createOpenAICompatibleRuntime = <T extends Record<string, any> = an
|
|
226
221
|
stream: payload.stream ?? true,
|
227
222
|
} as OpenAI.ChatCompletionCreateParamsStreaming);
|
228
223
|
|
224
|
+
// new openai Response API
|
225
|
+
if ((postPayload as any).apiMode === 'responses') {
|
226
|
+
return this.handleResponseAPIMode(payload, options);
|
227
|
+
}
|
228
|
+
|
229
229
|
const messages = await convertOpenAIMessages(postPayload.messages);
|
230
230
|
|
231
231
|
let response: Stream<OpenAI.Chat.Completions.ChatCompletionChunk>;
|
@@ -478,11 +478,12 @@ export const createOpenAICompatibleRuntime = <T extends Record<string, any> = an
|
|
478
478
|
): Promise<Response> {
|
479
479
|
const inputStartAt = Date.now();
|
480
480
|
|
481
|
-
const { messages, ...res } = responses?.handlePayload
|
481
|
+
const { messages, reasoning_effort, tools, ...res } = responses?.handlePayload
|
482
482
|
? (responses?.handlePayload(payload, this._options) as ChatStreamPayload)
|
483
483
|
: payload;
|
484
484
|
|
485
485
|
// remove penalty params
|
486
|
+
delete res.apiMode;
|
486
487
|
delete res.frequency_penalty;
|
487
488
|
delete res.presence_penalty;
|
488
489
|
|
@@ -490,9 +491,10 @@ export const createOpenAICompatibleRuntime = <T extends Record<string, any> = an
|
|
490
491
|
|
491
492
|
const postPayload = {
|
492
493
|
...res,
|
494
|
+
...(reasoning_effort ? { reasoning: { effort: reasoning_effort } } : {}),
|
493
495
|
input,
|
494
496
|
store: false,
|
495
|
-
tools:
|
497
|
+
tools: tools?.map((tool) => this.convertChatCompletionToolToResponseTool(tool)),
|
496
498
|
} as OpenAI.Responses.ResponseCreateParamsStreaming;
|
497
499
|
|
498
500
|
if (debug?.responses?.()) {
|
package/src/libs/model-runtime/utils/streams/openai/__snapshots__/responsesStream.test.ts.snap
CHANGED
@@ -86,11 +86,11 @@ exports[`OpenAIResponsesStream > Reasoning > summary 1`] = `
|
|
86
86
|
"data: " analyzing"
|
87
87
|
|
88
88
|
",
|
89
|
-
"id:
|
89
|
+
"id: rs_684313b9774481908ee856625f82fb8c0b502bf083132d0d
|
90
90
|
",
|
91
|
-
"event:
|
91
|
+
"event: text
|
92
92
|
",
|
93
|
-
"data:
|
93
|
+
"data: null
|
94
94
|
|
95
95
|
",
|
96
96
|
"id: resp_684313b89200819087f27686e0c822260b502bf083132d0d
|
@@ -128,11 +128,11 @@ exports[`OpenAIResponsesStream > Reasoning > summary 1`] = `
|
|
128
128
|
"data: {"type":"response.content_part.done","item_id":"msg_684313bee2c88190b0f4b09621ad7dc60b502bf083132d0d","output_index":1,"content_index":0,"part":{"type":"output_text","annotations":[],"text":"9.92 比 9.1 大。"}}
|
129
129
|
|
130
130
|
",
|
131
|
-
"id:
|
131
|
+
"id: msg_684313bee2c88190b0f4b09621ad7dc60b502bf083132d0d
|
132
132
|
",
|
133
|
-
"event:
|
133
|
+
"event: text
|
134
134
|
",
|
135
|
-
"data:
|
135
|
+
"data: null
|
136
136
|
|
137
137
|
",
|
138
138
|
"id: resp_684313b89200819087f27686e0c822260b502bf083132d0d
|
@@ -1,7 +1,7 @@
|
|
1
1
|
import OpenAI from 'openai';
|
2
2
|
import type { Stream } from 'openai/streaming';
|
3
3
|
|
4
|
-
import { ChatMessageError } from '@/types/message';
|
4
|
+
import { ChatMessageError, CitationItem } from '@/types/message';
|
5
5
|
|
6
6
|
import { AgentRuntimeErrorType } from '../../../error';
|
7
7
|
import { convertResponseUsage } from '../../usageConverter';
|
@@ -20,7 +20,17 @@ import {
|
|
20
20
|
import { OpenAIStreamOptions } from './openai';
|
21
21
|
|
22
22
|
const transformOpenAIStream = (
|
23
|
-
chunk: OpenAI.Responses.ResponseStreamEvent
|
23
|
+
chunk: OpenAI.Responses.ResponseStreamEvent | {
|
24
|
+
annotation: {
|
25
|
+
end_index: number;
|
26
|
+
start_index: number;
|
27
|
+
title: string;
|
28
|
+
type: 'url_citation';
|
29
|
+
url: string;
|
30
|
+
};
|
31
|
+
item_id: string;
|
32
|
+
type: 'response.output_text.annotation.added';
|
33
|
+
},
|
24
34
|
streamContext: StreamContext,
|
25
35
|
): StreamProtocolChunk | StreamProtocolChunk[] => {
|
26
36
|
// handle the first chunk error
|
@@ -42,6 +52,7 @@ const transformOpenAIStream = (
|
|
42
52
|
switch (chunk.type) {
|
43
53
|
case 'response.created': {
|
44
54
|
streamContext.id = chunk.response.id;
|
55
|
+
streamContext.returnedCitationArray = [];
|
45
56
|
|
46
57
|
return { data: chunk.response.status, id: streamContext.id, type: 'data' };
|
47
58
|
}
|
@@ -106,6 +117,31 @@ const transformOpenAIStream = (
|
|
106
117
|
return { data: chunk.delta, id: chunk.item_id, type: 'reasoning' };
|
107
118
|
}
|
108
119
|
|
120
|
+
case 'response.output_text.annotation.added': {
|
121
|
+
const citations = chunk.annotation;
|
122
|
+
|
123
|
+
if (streamContext.returnedCitationArray) {
|
124
|
+
streamContext.returnedCitationArray.push({
|
125
|
+
title: citations.title,
|
126
|
+
url: citations.url,
|
127
|
+
} as CitationItem);
|
128
|
+
}
|
129
|
+
|
130
|
+
return { data: null, id: chunk.item_id, type: 'text' };
|
131
|
+
}
|
132
|
+
|
133
|
+
case 'response.output_item.done': {
|
134
|
+
if (streamContext.returnedCitationArray?.length) {
|
135
|
+
return {
|
136
|
+
data: { citations: streamContext.returnedCitationArray },
|
137
|
+
id: chunk.item.id,
|
138
|
+
type: 'grounding',
|
139
|
+
}
|
140
|
+
}
|
141
|
+
|
142
|
+
return { data: null, id: chunk.item.id, type: 'text' };
|
143
|
+
}
|
144
|
+
|
109
145
|
case 'response.completed': {
|
110
146
|
if (chunk.response.usage) {
|
111
147
|
return {
|
package/src/services/chat.ts
CHANGED
@@ -388,6 +388,13 @@ class ChatService {
|
|
388
388
|
const userPreferTransitionMode =
|
389
389
|
userGeneralSettingsSelectors.transitionMode(getUserStoreState());
|
390
390
|
|
391
|
+
// The order of the array is very important.
|
392
|
+
const mergedResponseAnimation = [
|
393
|
+
providerConfig?.settings?.responseAnimation || {},
|
394
|
+
userPreferTransitionMode,
|
395
|
+
responseAnimation,
|
396
|
+
].reduce((acc, cur) => merge(acc, standardizeAnimationStyle(cur)), {});
|
397
|
+
|
391
398
|
return fetchSSE(API_ENDPOINTS.chat(sdkType), {
|
392
399
|
body: JSON.stringify(payload),
|
393
400
|
fetcher: fetcher,
|
@@ -397,10 +404,7 @@ class ChatService {
|
|
397
404
|
onErrorHandle: options?.onErrorHandle,
|
398
405
|
onFinish: options?.onFinish,
|
399
406
|
onMessageHandle: options?.onMessageHandle,
|
400
|
-
responseAnimation:
|
401
|
-
(acc, cur) => merge(acc, standardizeAnimationStyle(cur)),
|
402
|
-
providerConfig?.settings?.responseAnimation ?? {},
|
403
|
-
),
|
407
|
+
responseAnimation: mergedResponseAnimation,
|
404
408
|
signal,
|
405
409
|
});
|
406
410
|
};
|