@lobehub/chat 1.94.4 → 1.94.6
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 +58 -0
- package/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/changelog/v1.json +18 -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/groq.ts +14 -0
- package/src/config/aiModels/openai.ts +146 -4
- package/src/config/modelProviders/openai.ts +1 -1
- package/src/const/models.ts +26 -1
- package/src/libs/model-runtime/google/index.ts +108 -21
- 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,64 @@
|
|
2
2
|
|
3
3
|
# Changelog
|
4
4
|
|
5
|
+
### [Version 1.94.6](https://github.com/lobehub/lobe-chat/compare/v1.94.5...v1.94.6)
|
6
|
+
|
7
|
+
<sup>Released on **2025-06-12**</sup>
|
8
|
+
|
9
|
+
#### 🐛 Bug Fixes
|
10
|
+
|
11
|
+
- **misc**: Abort the Gemini request correctly & Add openai o3-pro.
|
12
|
+
|
13
|
+
<br/>
|
14
|
+
|
15
|
+
<details>
|
16
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
17
|
+
|
18
|
+
#### What's fixed
|
19
|
+
|
20
|
+
- **misc**: Abort the Gemini request correctly & Add openai o3-pro, closes [#8135](https://github.com/lobehub/lobe-chat/issues/8135) ([c79f1b9](https://github.com/lobehub/lobe-chat/commit/c79f1b9))
|
21
|
+
|
22
|
+
</details>
|
23
|
+
|
24
|
+
<div align="right">
|
25
|
+
|
26
|
+
[](#readme-top)
|
27
|
+
|
28
|
+
</div>
|
29
|
+
|
30
|
+
### [Version 1.94.5](https://github.com/lobehub/lobe-chat/compare/v1.94.4...v1.94.5)
|
31
|
+
|
32
|
+
<sup>Released on **2025-06-12**</sup>
|
33
|
+
|
34
|
+
#### 🐛 Bug Fixes
|
35
|
+
|
36
|
+
- **chat**: Improve response animation merging logic.
|
37
|
+
|
38
|
+
#### 💄 Styles
|
39
|
+
|
40
|
+
- **misc**: Support `web_search_preview` & fix some bug form OpenAI Response API.
|
41
|
+
|
42
|
+
<br/>
|
43
|
+
|
44
|
+
<details>
|
45
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
46
|
+
|
47
|
+
#### What's fixed
|
48
|
+
|
49
|
+
- **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))
|
50
|
+
|
51
|
+
#### Styles
|
52
|
+
|
53
|
+
- **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))
|
54
|
+
|
55
|
+
</details>
|
56
|
+
|
57
|
+
<div align="right">
|
58
|
+
|
59
|
+
[](#readme-top)
|
60
|
+
|
61
|
+
</div>
|
62
|
+
|
5
63
|
### [Version 1.94.4](https://github.com/lobehub/lobe-chat/compare/v1.94.3...v1.94.4)
|
6
64
|
|
7
65
|
<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
@@ -1,4 +1,22 @@
|
|
1
1
|
[
|
2
|
+
{
|
3
|
+
"children": {
|
4
|
+
"fixes": [
|
5
|
+
"Abort the Gemini request correctly & Add openai o3-pro."
|
6
|
+
]
|
7
|
+
},
|
8
|
+
"date": "2025-06-12",
|
9
|
+
"version": "1.94.6"
|
10
|
+
},
|
11
|
+
{
|
12
|
+
"children": {
|
13
|
+
"improvements": [
|
14
|
+
"Support web_search_preview & fix some bug form OpenAI Response API."
|
15
|
+
]
|
16
|
+
},
|
17
|
+
"date": "2025-06-12",
|
18
|
+
"version": "1.94.5"
|
19
|
+
},
|
2
20
|
{
|
3
21
|
"children": {
|
4
22
|
"improvements": [
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@lobehub/chat",
|
3
|
-
"version": "1.94.
|
3
|
+
"version": "1.94.6",
|
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 = '';
|
@@ -62,6 +62,20 @@ const groqChatModels: AIChatModelCard[] = [
|
|
62
62
|
},
|
63
63
|
type: 'chat',
|
64
64
|
},
|
65
|
+
{
|
66
|
+
abilities: {
|
67
|
+
reasoning: true,
|
68
|
+
},
|
69
|
+
contextWindowTokens: 131_072,
|
70
|
+
displayName: 'Qwen3 32B',
|
71
|
+
id: 'qwen/qwen3-32b',
|
72
|
+
maxOutput: 16_384,
|
73
|
+
pricing: {
|
74
|
+
input: 0.29,
|
75
|
+
output: 0.59,
|
76
|
+
},
|
77
|
+
type: 'chat',
|
78
|
+
},
|
65
79
|
{
|
66
80
|
abilities: {
|
67
81
|
functionCall: true,
|
@@ -8,6 +8,28 @@ import {
|
|
8
8
|
} from '@/types/aiModel';
|
9
9
|
|
10
10
|
export const openaiChatModels: AIChatModelCard[] = [
|
11
|
+
{
|
12
|
+
abilities: {
|
13
|
+
functionCall: true,
|
14
|
+
reasoning: true,
|
15
|
+
vision: true,
|
16
|
+
},
|
17
|
+
contextWindowTokens: 200_000,
|
18
|
+
description:
|
19
|
+
'o3-pro 模型使用更多的计算来更深入地思考并始终提供更好的答案,仅支持 Responses API 下使用。',
|
20
|
+
displayName: 'o3-pro',
|
21
|
+
id: 'o3-pro',
|
22
|
+
maxOutput: 100_000,
|
23
|
+
pricing: {
|
24
|
+
input: 20,
|
25
|
+
output: 80,
|
26
|
+
},
|
27
|
+
releasedAt: '2025-06-10',
|
28
|
+
settings: {
|
29
|
+
extendParams: ['reasoningEffort'],
|
30
|
+
},
|
31
|
+
type: 'chat',
|
32
|
+
},
|
11
33
|
{
|
12
34
|
abilities: {
|
13
35
|
functionCall: true,
|
@@ -22,11 +44,11 @@ export const openaiChatModels: AIChatModelCard[] = [
|
|
22
44
|
id: 'o3',
|
23
45
|
maxOutput: 100_000,
|
24
46
|
pricing: {
|
25
|
-
cachedInput:
|
26
|
-
input:
|
27
|
-
output:
|
47
|
+
cachedInput: 0.5,
|
48
|
+
input: 2,
|
49
|
+
output: 8,
|
28
50
|
},
|
29
|
-
releasedAt: '2025-04-
|
51
|
+
releasedAt: '2025-04-16',
|
30
52
|
settings: {
|
31
53
|
extendParams: ['reasoningEffort'],
|
32
54
|
},
|
@@ -59,6 +81,7 @@ export const openaiChatModels: AIChatModelCard[] = [
|
|
59
81
|
{
|
60
82
|
abilities: {
|
61
83
|
functionCall: true,
|
84
|
+
search: true,
|
62
85
|
vision: true,
|
63
86
|
},
|
64
87
|
contextWindowTokens: 1_047_576,
|
@@ -73,11 +96,15 @@ export const openaiChatModels: AIChatModelCard[] = [
|
|
73
96
|
output: 8,
|
74
97
|
},
|
75
98
|
releasedAt: '2025-04-14',
|
99
|
+
settings: {
|
100
|
+
searchImpl: 'params',
|
101
|
+
},
|
76
102
|
type: 'chat',
|
77
103
|
},
|
78
104
|
{
|
79
105
|
abilities: {
|
80
106
|
functionCall: true,
|
107
|
+
search: true,
|
81
108
|
vision: true,
|
82
109
|
},
|
83
110
|
contextWindowTokens: 1_047_576,
|
@@ -93,6 +120,9 @@ export const openaiChatModels: AIChatModelCard[] = [
|
|
93
120
|
output: 1.6,
|
94
121
|
},
|
95
122
|
releasedAt: '2025-04-14',
|
123
|
+
settings: {
|
124
|
+
searchImpl: 'params',
|
125
|
+
},
|
96
126
|
type: 'chat',
|
97
127
|
},
|
98
128
|
{
|
@@ -135,6 +165,28 @@ export const openaiChatModels: AIChatModelCard[] = [
|
|
135
165
|
},
|
136
166
|
type: 'chat',
|
137
167
|
},
|
168
|
+
{
|
169
|
+
abilities: {
|
170
|
+
functionCall: true,
|
171
|
+
reasoning: true,
|
172
|
+
vision: true,
|
173
|
+
},
|
174
|
+
contextWindowTokens: 200_000,
|
175
|
+
description:
|
176
|
+
'o1 系列模型经过强化学习训练,能够在回答前进行思考,并执行复杂的推理任务。o1-pro 模型使用了更多计算资源,以进行更深入的思考,从而持续提供更优质的回答。',
|
177
|
+
displayName: 'o1-pro',
|
178
|
+
id: 'o1-pro',
|
179
|
+
maxOutput: 100_000,
|
180
|
+
pricing: {
|
181
|
+
input: 150,
|
182
|
+
output: 600,
|
183
|
+
},
|
184
|
+
releasedAt: '2025-03-19',
|
185
|
+
settings: {
|
186
|
+
extendParams: ['reasoningEffort'],
|
187
|
+
},
|
188
|
+
type: 'chat',
|
189
|
+
},
|
138
190
|
{
|
139
191
|
abilities: {
|
140
192
|
reasoning: true,
|
@@ -158,6 +210,7 @@ export const openaiChatModels: AIChatModelCard[] = [
|
|
158
210
|
},
|
159
211
|
{
|
160
212
|
abilities: {
|
213
|
+
functionCall: true,
|
161
214
|
reasoning: true,
|
162
215
|
vision: true,
|
163
216
|
},
|
@@ -220,6 +273,7 @@ export const openaiChatModels: AIChatModelCard[] = [
|
|
220
273
|
{
|
221
274
|
abilities: {
|
222
275
|
functionCall: true,
|
276
|
+
search: true,
|
223
277
|
vision: true,
|
224
278
|
},
|
225
279
|
contextWindowTokens: 128_000,
|
@@ -234,6 +288,9 @@ export const openaiChatModels: AIChatModelCard[] = [
|
|
234
288
|
output: 0.6,
|
235
289
|
},
|
236
290
|
releasedAt: '2024-07-18',
|
291
|
+
settings: {
|
292
|
+
searchImpl: 'params',
|
293
|
+
},
|
237
294
|
type: 'chat',
|
238
295
|
},
|
239
296
|
{
|
@@ -259,6 +316,29 @@ export const openaiChatModels: AIChatModelCard[] = [
|
|
259
316
|
{
|
260
317
|
abilities: {
|
261
318
|
functionCall: true,
|
319
|
+
//search: true,
|
320
|
+
},
|
321
|
+
contextWindowTokens: 128_000,
|
322
|
+
description: 'GPT-4o mini Audio 模型,支持音频输入输出',
|
323
|
+
displayName: 'GPT-4o mini Audio',
|
324
|
+
id: 'gpt-4o-mini-audio-preview',
|
325
|
+
maxOutput: 16_384,
|
326
|
+
pricing: {
|
327
|
+
input: 0.15,
|
328
|
+
output: 0.6,
|
329
|
+
},
|
330
|
+
releasedAt: '2024-12-17',
|
331
|
+
/*
|
332
|
+
settings: {
|
333
|
+
searchImpl: 'params',
|
334
|
+
},
|
335
|
+
*/
|
336
|
+
type: 'chat',
|
337
|
+
},
|
338
|
+
{
|
339
|
+
abilities: {
|
340
|
+
functionCall: true,
|
341
|
+
search: true,
|
262
342
|
vision: true,
|
263
343
|
},
|
264
344
|
contextWindowTokens: 128_000,
|
@@ -272,6 +352,9 @@ export const openaiChatModels: AIChatModelCard[] = [
|
|
272
352
|
output: 10,
|
273
353
|
},
|
274
354
|
releasedAt: '2024-05-13',
|
355
|
+
settings: {
|
356
|
+
searchImpl: 'params',
|
357
|
+
},
|
275
358
|
type: 'chat',
|
276
359
|
},
|
277
360
|
{
|
@@ -297,6 +380,7 @@ export const openaiChatModels: AIChatModelCard[] = [
|
|
297
380
|
{
|
298
381
|
abilities: {
|
299
382
|
functionCall: true,
|
383
|
+
search: true,
|
300
384
|
vision: true,
|
301
385
|
},
|
302
386
|
contextWindowTokens: 128_000,
|
@@ -310,11 +394,15 @@ export const openaiChatModels: AIChatModelCard[] = [
|
|
310
394
|
output: 10,
|
311
395
|
},
|
312
396
|
releasedAt: '2024-11-20',
|
397
|
+
settings: {
|
398
|
+
searchImpl: 'params',
|
399
|
+
},
|
313
400
|
type: 'chat',
|
314
401
|
},
|
315
402
|
{
|
316
403
|
abilities: {
|
317
404
|
functionCall: true,
|
405
|
+
search: true,
|
318
406
|
vision: true,
|
319
407
|
},
|
320
408
|
contextWindowTokens: 128_000,
|
@@ -327,9 +415,16 @@ export const openaiChatModels: AIChatModelCard[] = [
|
|
327
415
|
output: 15,
|
328
416
|
},
|
329
417
|
releasedAt: '2024-05-13',
|
418
|
+
settings: {
|
419
|
+
searchImpl: 'params',
|
420
|
+
},
|
330
421
|
type: 'chat',
|
331
422
|
},
|
332
423
|
{
|
424
|
+
abilities: {
|
425
|
+
functionCall: true,
|
426
|
+
//search: true,
|
427
|
+
},
|
333
428
|
contextWindowTokens: 128_000,
|
334
429
|
description: 'GPT-4o Audio 模型,支持音频输入输出',
|
335
430
|
displayName: 'GPT-4o Audio',
|
@@ -340,6 +435,11 @@ export const openaiChatModels: AIChatModelCard[] = [
|
|
340
435
|
output: 10,
|
341
436
|
},
|
342
437
|
releasedAt: '2024-10-01',
|
438
|
+
/*
|
439
|
+
settings: {
|
440
|
+
searchImpl: 'params',
|
441
|
+
},
|
442
|
+
*/
|
343
443
|
type: 'chat',
|
344
444
|
},
|
345
445
|
{
|
@@ -545,6 +645,48 @@ export const openaiChatModels: AIChatModelCard[] = [
|
|
545
645
|
},
|
546
646
|
type: 'chat',
|
547
647
|
},
|
648
|
+
{
|
649
|
+
abilities: {
|
650
|
+
functionCall: true,
|
651
|
+
reasoning: true,
|
652
|
+
vision: true,
|
653
|
+
},
|
654
|
+
contextWindowTokens: 200_000,
|
655
|
+
description: 'codex-mini-latest 是 o4-mini 的微调版本,专门用于 Codex CLI。对于直接通过 API 使用,我们推荐从 gpt-4.1 开始。',
|
656
|
+
displayName: 'Codex mini',
|
657
|
+
id: 'codex-mini-latest',
|
658
|
+
maxOutput: 100_000,
|
659
|
+
pricing: {
|
660
|
+
input: 1.5,
|
661
|
+
output: 6,
|
662
|
+
},
|
663
|
+
releasedAt: '2025-06-01',
|
664
|
+
settings: {
|
665
|
+
extendParams: ['reasoningEffort'],
|
666
|
+
},
|
667
|
+
type: 'chat',
|
668
|
+
},
|
669
|
+
{
|
670
|
+
abilities: {
|
671
|
+
functionCall: true,
|
672
|
+
reasoning: true,
|
673
|
+
vision: true,
|
674
|
+
},
|
675
|
+
contextWindowTokens: 8192,
|
676
|
+
description: 'computer-use-preview 模型是专为“计算机使用工具”设计的专用模型,经过训练以理解并执行计算机相关任务。',
|
677
|
+
displayName: 'Computer Use Preview',
|
678
|
+
id: 'computer-use-preview',
|
679
|
+
maxOutput: 1024,
|
680
|
+
pricing: {
|
681
|
+
input: 3,
|
682
|
+
output: 12,
|
683
|
+
},
|
684
|
+
releasedAt: '2025-03-11',
|
685
|
+
settings: {
|
686
|
+
extendParams: ['reasoningEffort'],
|
687
|
+
},
|
688
|
+
type: 'chat',
|
689
|
+
},
|
548
690
|
];
|
549
691
|
|
550
692
|
export const openaiEmbeddingModels: AIEmbeddingModelCard[] = [
|
@@ -323,7 +323,7 @@ const OpenAI: ModelProviderCard = {
|
|
323
323
|
},
|
324
324
|
},
|
325
325
|
],
|
326
|
-
checkModel: 'gpt-
|
326
|
+
checkModel: 'gpt-4.1-nano',
|
327
327
|
description:
|
328
328
|
'OpenAI 是全球领先的人工智能研究机构,其开发的模型如GPT系列推动了自然语言处理的前沿。OpenAI 致力于通过创新和高效的AI解决方案改变多个行业。他们的产品具有显著的性能和经济性,广泛用于研究、商业和创新应用。',
|
329
329
|
enabled: true,
|
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
|
@@ -25,11 +25,7 @@ import {
|
|
25
25
|
import { AgentRuntimeError } from '../utils/createError';
|
26
26
|
import { debugStream } from '../utils/debugStream';
|
27
27
|
import { StreamingResponse } from '../utils/response';
|
28
|
-
import {
|
29
|
-
GoogleGenerativeAIStream,
|
30
|
-
VertexAIStream,
|
31
|
-
convertIterableToStream,
|
32
|
-
} from '../utils/streams';
|
28
|
+
import { GoogleGenerativeAIStream, VertexAIStream } from '../utils/streams';
|
33
29
|
import { parseDataUri } from '../utils/uriParser';
|
34
30
|
|
35
31
|
const modelsOffSafetySettings = new Set(['gemini-2.0-flash-exp']);
|
@@ -91,6 +87,17 @@ interface GoogleAIThinkingConfig {
|
|
91
87
|
thinkingBudget?: number;
|
92
88
|
}
|
93
89
|
|
90
|
+
const isAbortError = (error: Error): boolean => {
|
91
|
+
const message = error.message.toLowerCase();
|
92
|
+
return (
|
93
|
+
message.includes('aborted') ||
|
94
|
+
message.includes('cancelled') ||
|
95
|
+
message.includes('error reading from the stream') ||
|
96
|
+
message.includes('abort') ||
|
97
|
+
error.name === 'AbortError'
|
98
|
+
);
|
99
|
+
};
|
100
|
+
|
94
101
|
export class LobeGoogleAI implements LobeRuntimeAI {
|
95
102
|
private client: GoogleGenerativeAI;
|
96
103
|
private isVertexAi: boolean;
|
@@ -140,6 +147,20 @@ export class LobeGoogleAI implements LobeRuntimeAI {
|
|
140
147
|
const contents = await this.buildGoogleMessages(payload.messages);
|
141
148
|
|
142
149
|
const inputStartAt = Date.now();
|
150
|
+
|
151
|
+
const controller = new AbortController();
|
152
|
+
const originalSignal = options?.signal;
|
153
|
+
|
154
|
+
if (originalSignal) {
|
155
|
+
if (originalSignal.aborted) {
|
156
|
+
controller.abort();
|
157
|
+
} else {
|
158
|
+
originalSignal.addEventListener('abort', () => {
|
159
|
+
controller.abort();
|
160
|
+
});
|
161
|
+
}
|
162
|
+
}
|
163
|
+
|
143
164
|
const geminiStreamResult = await this.client
|
144
165
|
.getGenerativeModel(
|
145
166
|
{
|
@@ -177,15 +198,20 @@ export class LobeGoogleAI implements LobeRuntimeAI {
|
|
177
198
|
},
|
178
199
|
{ apiVersion: 'v1beta', baseUrl: this.baseURL },
|
179
200
|
)
|
180
|
-
.generateContentStream(
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
201
|
+
.generateContentStream(
|
202
|
+
{
|
203
|
+
contents,
|
204
|
+
systemInstruction: modelsDisableInstuction.has(model)
|
205
|
+
? undefined
|
206
|
+
: (payload.system as string),
|
207
|
+
tools: this.buildGoogleTools(payload.tools, payload),
|
208
|
+
},
|
209
|
+
{
|
210
|
+
signal: controller.signal,
|
211
|
+
},
|
212
|
+
);
|
187
213
|
|
188
|
-
const googleStream =
|
214
|
+
const googleStream = this.createEnhancedStream(geminiStreamResult.stream, controller.signal);
|
189
215
|
const [prod, useForDebug] = googleStream.tee();
|
190
216
|
|
191
217
|
const key = this.isVertexAi
|
@@ -205,6 +231,16 @@ export class LobeGoogleAI implements LobeRuntimeAI {
|
|
205
231
|
} catch (e) {
|
206
232
|
const err = e as Error;
|
207
233
|
|
234
|
+
// 移除之前的静默处理,统一抛出错误
|
235
|
+
if (isAbortError(err)) {
|
236
|
+
console.log('Request was cancelled');
|
237
|
+
throw AgentRuntimeError.chat({
|
238
|
+
error: { message: 'Request was cancelled' },
|
239
|
+
errorType: AgentRuntimeErrorType.ProviderBizError,
|
240
|
+
provider: this.provider,
|
241
|
+
});
|
242
|
+
}
|
243
|
+
|
208
244
|
console.log(err);
|
209
245
|
const { errorType, error } = this.parseErrorMessage(err.message);
|
210
246
|
|
@@ -212,24 +248,75 @@ export class LobeGoogleAI implements LobeRuntimeAI {
|
|
212
248
|
}
|
213
249
|
}
|
214
250
|
|
215
|
-
|
251
|
+
private createEnhancedStream(originalStream: any, signal: AbortSignal): ReadableStream {
|
252
|
+
return new ReadableStream({
|
253
|
+
async start(controller) {
|
254
|
+
let hasData = false;
|
255
|
+
|
256
|
+
try {
|
257
|
+
for await (const chunk of originalStream) {
|
258
|
+
if (signal.aborted) {
|
259
|
+
// 如果有数据已经输出,优雅地关闭流而不是抛出错误
|
260
|
+
if (hasData) {
|
261
|
+
console.log('Stream cancelled gracefully, preserving existing output');
|
262
|
+
controller.close();
|
263
|
+
return;
|
264
|
+
} else {
|
265
|
+
// 如果还没有数据输出,则抛出取消错误
|
266
|
+
throw new Error('Stream cancelled');
|
267
|
+
}
|
268
|
+
}
|
269
|
+
|
270
|
+
hasData = true;
|
271
|
+
controller.enqueue(chunk);
|
272
|
+
}
|
273
|
+
} catch (error) {
|
274
|
+
const err = error as Error;
|
275
|
+
|
276
|
+
// 统一处理所有错误,包括 abort 错误
|
277
|
+
if (isAbortError(err) || signal.aborted) {
|
278
|
+
// 如果有数据已经输出,优雅地关闭流
|
279
|
+
if (hasData) {
|
280
|
+
console.log('Stream reading cancelled gracefully, preserving existing output');
|
281
|
+
controller.close();
|
282
|
+
return;
|
283
|
+
} else {
|
284
|
+
console.log('Stream reading cancelled before any output');
|
285
|
+
controller.error(new Error('Stream cancelled'));
|
286
|
+
return;
|
287
|
+
}
|
288
|
+
} else {
|
289
|
+
// 处理其他流解析错误
|
290
|
+
console.error('Stream parsing error:', err);
|
291
|
+
controller.error(err);
|
292
|
+
return;
|
293
|
+
}
|
294
|
+
}
|
295
|
+
|
296
|
+
controller.close();
|
297
|
+
},
|
298
|
+
});
|
299
|
+
}
|
300
|
+
|
301
|
+
async models(options?: { signal?: AbortSignal }) {
|
216
302
|
try {
|
217
303
|
const url = `${this.baseURL}/v1beta/models?key=${this.apiKey}`;
|
218
304
|
const response = await fetch(url, {
|
219
305
|
method: 'GET',
|
306
|
+
signal: options?.signal,
|
220
307
|
});
|
221
|
-
|
308
|
+
|
222
309
|
if (!response.ok) {
|
223
310
|
throw new Error(`HTTP error! status: ${response.status}`);
|
224
311
|
}
|
225
|
-
|
312
|
+
|
226
313
|
const json = await response.json();
|
227
|
-
|
314
|
+
|
228
315
|
const modelList: GoogleModelCard[] = json.models;
|
229
|
-
|
316
|
+
|
230
317
|
const processedModels = modelList.map((model) => {
|
231
318
|
const id = model.name.replace(/^models\//, '');
|
232
|
-
|
319
|
+
|
233
320
|
return {
|
234
321
|
contextWindowTokens: (model.inputTokenLimit || 0) + (model.outputTokenLimit || 0),
|
235
322
|
displayName: model.displayName || id,
|
@@ -237,9 +324,9 @@ export class LobeGoogleAI implements LobeRuntimeAI {
|
|
237
324
|
maxOutput: model.outputTokenLimit || undefined,
|
238
325
|
};
|
239
326
|
});
|
240
|
-
|
327
|
+
|
241
328
|
const { MODEL_LIST_CONFIGS, processModelList } = await import('../utils/modelParse');
|
242
|
-
|
329
|
+
|
243
330
|
return processModelList(processedModels, MODEL_LIST_CONFIGS.google);
|
244
331
|
} catch (error) {
|
245
332
|
console.error('Failed to fetch Google models:', error);
|
@@ -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
|
};
|