@lobehub/chat 1.93.0 → 1.93.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.
- package/CHANGELOG.md +50 -0
- package/README.md +8 -8
- package/README.zh-CN.md +3 -3
- package/changelog/v1.json +18 -0
- package/package.json +1 -1
- package/src/libs/model-runtime/perplexity/index.test.ts +4 -4
- package/src/libs/model-runtime/utils/streams/ollama.test.ts +59 -0
- package/src/libs/model-runtime/utils/streams/ollama.ts +14 -1
- package/src/libs/model-runtime/utils/streams/openai/openai.test.ts +163 -0
- package/src/libs/model-runtime/utils/streams/openai/openai.ts +18 -2
- package/src/libs/model-runtime/utils/streams/protocol.ts +12 -0
- package/src/services/__tests__/chat.test.ts +0 -2
- package/src/store/aiInfra/slices/aiProvider/selectors.ts +1 -1
package/CHANGELOG.md
CHANGED
@@ -2,6 +2,56 @@
|
|
2
2
|
|
3
3
|
# Changelog
|
4
4
|
|
5
|
+
### [Version 1.93.2](https://github.com/lobehub/lobe-chat/compare/v1.93.1...v1.93.2)
|
6
|
+
|
7
|
+
<sup>Released on **2025-06-09**</sup>
|
8
|
+
|
9
|
+
#### ♻ Code Refactoring
|
10
|
+
|
11
|
+
- **misc**: Refactor `<think>` & `</think>` handling.
|
12
|
+
|
13
|
+
<br/>
|
14
|
+
|
15
|
+
<details>
|
16
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
17
|
+
|
18
|
+
#### Code refactoring
|
19
|
+
|
20
|
+
- **misc**: Refactor `<think>` & `</think>` handling, closes [#8121](https://github.com/lobehub/lobe-chat/issues/8121) ([04ac353](https://github.com/lobehub/lobe-chat/commit/04ac353))
|
21
|
+
|
22
|
+
</details>
|
23
|
+
|
24
|
+
<div align="right">
|
25
|
+
|
26
|
+
[](#readme-top)
|
27
|
+
|
28
|
+
</div>
|
29
|
+
|
30
|
+
### [Version 1.93.1](https://github.com/lobehub/lobe-chat/compare/v1.93.0...v1.93.1)
|
31
|
+
|
32
|
+
<sup>Released on **2025-06-08**</sup>
|
33
|
+
|
34
|
+
#### 🐛 Bug Fixes
|
35
|
+
|
36
|
+
- **misc**: Fix openai default Responses API issue.
|
37
|
+
|
38
|
+
<br/>
|
39
|
+
|
40
|
+
<details>
|
41
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
42
|
+
|
43
|
+
#### What's fixed
|
44
|
+
|
45
|
+
- **misc**: Fix openai default Responses API issue, closes [#8124](https://github.com/lobehub/lobe-chat/issues/8124) ([7f6ccf2](https://github.com/lobehub/lobe-chat/commit/7f6ccf2))
|
46
|
+
|
47
|
+
</details>
|
48
|
+
|
49
|
+
<div align="right">
|
50
|
+
|
51
|
+
[](#readme-top)
|
52
|
+
|
53
|
+
</div>
|
54
|
+
|
5
55
|
## [Version 1.93.0](https://github.com/lobehub/lobe-chat/compare/v1.92.3...v1.93.0)
|
6
56
|
|
7
57
|
<sup>Released on **2025-06-08**</sup>
|
package/README.md
CHANGED
@@ -206,7 +206,7 @@ We have implemented support for the following model service providers:
|
|
206
206
|
- **[Groq](https://lobechat.com/discover/provider/groq)**: Groq's LPU inference engine has excelled in the latest independent large language model (LLM) benchmarks, redefining the standards for AI solutions with its remarkable speed and efficiency. Groq represents instant inference speed, demonstrating strong performance in cloud-based deployments.
|
207
207
|
- **[Perplexity](https://lobechat.com/discover/provider/perplexity)**: Perplexity is a leading provider of conversational generation models, offering various advanced Llama 3.1 models that support both online and offline applications, particularly suited for complex natural language processing tasks.
|
208
208
|
- **[Mistral](https://lobechat.com/discover/provider/mistral)**: Mistral provides advanced general, specialized, and research models widely used in complex reasoning, multilingual tasks, and code generation. Through functional calling interfaces, users can integrate custom functionalities for specific applications.
|
209
|
-
- **[ModelScope](https://lobechat.com/discover/provider/modelscope)**:
|
209
|
+
- **[ModelScope](https://lobechat.com/discover/provider/modelscope)**: ModelScope is a model-as-a-service platform launched by Alibaba Cloud, offering a wide range of AI models and inference services.
|
210
210
|
- **[Ai21Labs](https://lobechat.com/discover/provider/ai21)**: AI21 Labs builds foundational models and AI systems for enterprises, accelerating the application of generative AI in production.
|
211
211
|
- **[Upstage](https://lobechat.com/discover/provider/upstage)**: Upstage focuses on developing AI models for various business needs, including Solar LLM and document AI, aiming to achieve artificial general intelligence (AGI) for work. It allows for the creation of simple conversational agents through Chat API and supports functional calling, translation, embedding, and domain-specific applications.
|
212
212
|
- **[xAI (Grok)](https://lobechat.com/discover/provider/xai)**: xAI is a company dedicated to building artificial intelligence to accelerate human scientific discovery. Our mission is to advance our collective understanding of the universe.
|
@@ -367,14 +367,14 @@ Our marketplace is not just a showcase platform but also a collaborative space.
|
|
367
367
|
|
368
368
|
<!-- AGENT LIST -->
|
369
369
|
|
370
|
-
| Recent Submits
|
371
|
-
|
|
372
|
-
| [Academic Paper Reading Mentor](https://lobechat.com/discover/assistant/paper-understanding)<br/><sup>By **[AdijeShen](https://github.com/AdijeShen)** on **2025-05-09**</sup>
|
373
|
-
| [Nutritional Advisor](https://lobechat.com/discover/assistant/nutritionist)<br/><sup>By **[egornomic](https://github.com/egornomic)** on **2025-04-15**</sup>
|
374
|
-
| [
|
375
|
-
| [Academic Paper Review Expert](https://lobechat.com/discover/assistant/academic-paper-overview)<br/><sup>By **[arvinxx](https://github.com/arvinxx)** on **2025-03-11**</sup>
|
370
|
+
| Recent Submits | Description |
|
371
|
+
| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
372
|
+
| [Academic Paper Reading Mentor](https://lobechat.com/discover/assistant/paper-understanding)<br/><sup>By **[AdijeShen](https://github.com/AdijeShen)** on **2025-05-09**</sup> | Expert in explaining complex academic papers in simple and understandable language<br/>`academic-knowledge` `paper-analysis` |
|
373
|
+
| [Nutritional Advisor](https://lobechat.com/discover/assistant/nutritionist)<br/><sup>By **[egornomic](https://github.com/egornomic)** on **2025-04-15**</sup> | Specializes in providing detailed nutritional information for food items.<br/>`nutrition` `food` `health` `information` |
|
374
|
+
| [Rewritten in Translation Style](https://lobechat.com/discover/assistant/rewrite-in-a-translation-tone)<br/><sup>By **[q2019715](https://github.com/q2019715)** on **2025-03-13**</sup> | Rewrites a paragraph in a translation style<br/>`translation-style` `creative-writing` `language-style` `text-rewriting` `culture` |
|
375
|
+
| [Academic Paper Review Expert](https://lobechat.com/discover/assistant/academic-paper-overview)<br/><sup>By **[arvinxx](https://github.com/arvinxx)** on **2025-03-11**</sup> | An academic research assistant skilled in high-quality literature retrieval and analysis<br/>`academic-research` `literature-search` `data-analysis` `information-extraction` `consulting` |
|
376
376
|
|
377
|
-
> 📊 Total agents: [<kbd>**
|
377
|
+
> 📊 Total agents: [<kbd>**499**</kbd> ](https://lobechat.com/discover/assistants)
|
378
378
|
|
379
379
|
<!-- AGENT LIST -->
|
380
380
|
|
package/README.zh-CN.md
CHANGED
@@ -206,7 +206,7 @@ LobeChat 支持文件上传与知识库功能,你可以上传文件、图片
|
|
206
206
|
- **[Groq](https://lobechat.com/discover/provider/groq)**: Groq 的 LPU 推理引擎在最新的独立大语言模型(LLM)基准测试中表现卓越,以其惊人的速度和效率重新定义了 AI 解决方案的标准。Groq 是一种即时推理速度的代表,在基于云的部署中展现了良好的性能。
|
207
207
|
- **[Perplexity](https://lobechat.com/discover/provider/perplexity)**: Perplexity 是一家领先的对话生成模型提供商,提供多种先进的 Llama 3.1 模型,支持在线和离线应用,特别适用于复杂的自然语言处理任务。
|
208
208
|
- **[Mistral](https://lobechat.com/discover/provider/mistral)**: Mistral 提供先进的通用、专业和研究型模型,广泛应用于复杂推理、多语言任务、代码生成等领域,通过功能调用接口,用户可以集成自定义功能,实现特定应用。
|
209
|
-
- **[ModelScope](https://lobechat.com/discover/provider/modelscope)**:
|
209
|
+
- **[ModelScope](https://lobechat.com/discover/provider/modelscope)**: ModelScope 是阿里云推出的模型即服务平台,提供丰富的 AI 模型和推理服务。
|
210
210
|
- **[Ai21Labs](https://lobechat.com/discover/provider/ai21)**: AI21 Labs 为企业构建基础模型和人工智能系统,加速生成性人工智能在生产中的应用。
|
211
211
|
- **[Upstage](https://lobechat.com/discover/provider/upstage)**: Upstage 专注于为各种商业需求开发 AI 模型,包括 Solar LLM 和文档 AI,旨在实现工作的人造通用智能(AGI)。通过 Chat API 创建简单的对话代理,并支持功能调用、翻译、嵌入以及特定领域应用。
|
212
212
|
- **[xAI (Grok)](https://lobechat.com/discover/provider/xai)**: xAI 是一家致力于构建人工智能以加速人类科学发现的公司。我们的使命是推动我们对宇宙的共同理解。
|
@@ -359,11 +359,11 @@ LobeChat 的插件生态系统是其核心功能的重要扩展,它极大地
|
|
359
359
|
| 最近新增 | 描述 |
|
360
360
|
| --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- |
|
361
361
|
| [学术论文阅读导师](https://lobechat.com/discover/assistant/paper-understanding)<br/><sup>By **[AdijeShen](https://github.com/AdijeShen)** on **2025-05-09**</sup> | 擅长将复杂学术论文通俗易懂讲解<br/>`学术知道` `论文解析` |
|
362
|
-
| [营养顾问](https://lobechat.com/discover/assistant/nutritionist)<br/><sup>By **[egornomic](https://github.com/egornomic)** on **2025-04-15**</sup> |
|
362
|
+
| [营养顾问](https://lobechat.com/discover/assistant/nutritionist)<br/><sup>By **[egornomic](https://github.com/egornomic)** on **2025-04-15**</sup> | 专注于提供食品项目的详细营养信息。<br/>`营养` `食品` `健康` `信息` |
|
363
363
|
| [改写为翻译腔](https://lobechat.com/discover/assistant/rewrite-in-a-translation-tone)<br/><sup>By **[q2019715](https://github.com/q2019715)** on **2025-03-13**</sup> | 将一段话重写为翻译腔<br/>`翻译腔` `创意写作` `语言风格` `文段重写` `文化` |
|
364
364
|
| [学术论文综述专家](https://lobechat.com/discover/assistant/academic-paper-overview)<br/><sup>By **[arvinxx](https://github.com/arvinxx)** on **2025-03-11**</sup> | 擅长高质量文献检索与分析的学术研究助手<br/>`学术研究` `文献检索` `数据分析` `信息提取` `咨询` |
|
365
365
|
|
366
|
-
> 📊 Total agents: [<kbd>**
|
366
|
+
> 📊 Total agents: [<kbd>**499**</kbd> ](https://lobechat.com/discover/assistants)
|
367
367
|
|
368
368
|
<!-- AGENT LIST -->
|
369
369
|
|
package/changelog/v1.json
CHANGED
@@ -1,4 +1,22 @@
|
|
1
1
|
[
|
2
|
+
{
|
3
|
+
"children": {
|
4
|
+
"improvements": [
|
5
|
+
"Refactor <think> & </think> handling."
|
6
|
+
]
|
7
|
+
},
|
8
|
+
"date": "2025-06-09",
|
9
|
+
"version": "1.93.2"
|
10
|
+
},
|
11
|
+
{
|
12
|
+
"children": {
|
13
|
+
"fixes": [
|
14
|
+
"Fix openai default Responses API issue."
|
15
|
+
]
|
16
|
+
},
|
17
|
+
"date": "2025-06-08",
|
18
|
+
"version": "1.93.1"
|
19
|
+
},
|
2
20
|
{
|
3
21
|
"children": {
|
4
22
|
"features": [
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@lobehub/chat",
|
3
|
-
"version": "1.93.
|
3
|
+
"version": "1.93.2",
|
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",
|
@@ -231,16 +231,16 @@ describe('LobePerplexityAI', () => {
|
|
231
231
|
expect(noSpeedStream).toEqual(
|
232
232
|
[
|
233
233
|
'id: 506d64fb-e7f2-4d94-b80f-158369e9446d',
|
234
|
-
'event:
|
235
|
-
'data: "
|
234
|
+
'event: reasoning',
|
235
|
+
'data: ""\n',
|
236
236
|
'id: 506d64fb-e7f2-4d94-b80f-158369e9446d',
|
237
237
|
'event: grounding',
|
238
238
|
'data: {"citations":[{"title":"https://www.weather.com.cn/weather/101210101.shtml","url":"https://www.weather.com.cn/weather/101210101.shtml"},{"title":"https://tianqi.moji.com/weather/china/zhejiang/hangzhou","url":"https://tianqi.moji.com/weather/china/zhejiang/hangzhou"},{"title":"https://weather.cma.cn/web/weather/58457.html","url":"https://weather.cma.cn/web/weather/58457.html"},{"title":"https://tianqi.so.com/weather/101210101","url":"https://tianqi.so.com/weather/101210101"},{"title":"https://www.accuweather.com/zh/cn/hangzhou/106832/weather-forecast/106832","url":"https://www.accuweather.com/zh/cn/hangzhou/106832/weather-forecast/106832"},{"title":"https://www.hzqx.com","url":"https://www.hzqx.com"},{"title":"https://www.hzqx.com/pc/hztq/","url":"https://www.hzqx.com/pc/hztq/"}]}\n',
|
239
239
|
'id: 506d64fb-e7f2-4d94-b80f-158369e9446d',
|
240
|
-
'event:
|
240
|
+
'event: reasoning',
|
241
241
|
'data: "杭州今"\n',
|
242
242
|
'id: 506d64fb-e7f2-4d94-b80f-158369e9446d',
|
243
|
-
'event:
|
243
|
+
'event: reasoning',
|
244
244
|
'data: "天和未来几天的"\n',
|
245
245
|
'id: 506d64fb-e7f2-4d94-b80f-158369e9446d',
|
246
246
|
'event: usage',
|
@@ -7,6 +7,65 @@ import { OllamaStream } from './ollama';
|
|
7
7
|
|
8
8
|
describe('OllamaStream', () => {
|
9
9
|
describe('should transform Ollama stream to protocol stream', () => {
|
10
|
+
it('reasoning', async () => {
|
11
|
+
vi.spyOn(uuidModule, 'nanoid').mockReturnValueOnce('2');
|
12
|
+
|
13
|
+
const messages = [
|
14
|
+
'<think>',
|
15
|
+
'这是一个思考过程',
|
16
|
+
',需要仔细分析问题。',
|
17
|
+
'</think>',
|
18
|
+
'根据分析,我的答案是:',
|
19
|
+
'这是最终答案。',
|
20
|
+
];
|
21
|
+
|
22
|
+
const mockOllamaStream = new ReadableStream<ChatResponse>({
|
23
|
+
start(controller) {
|
24
|
+
messages.forEach((content) => {
|
25
|
+
controller.enqueue({ message: { content }, done: false } as ChatResponse);
|
26
|
+
});
|
27
|
+
controller.enqueue({ message: { content: '' }, done: true } as ChatResponse);
|
28
|
+
controller.close();
|
29
|
+
},
|
30
|
+
});
|
31
|
+
|
32
|
+
const protocolStream = OllamaStream(mockOllamaStream);
|
33
|
+
|
34
|
+
const decoder = new TextDecoder();
|
35
|
+
const chunks = [];
|
36
|
+
|
37
|
+
// @ts-ignore
|
38
|
+
for await (const chunk of protocolStream) {
|
39
|
+
chunks.push(decoder.decode(chunk, { stream: true }));
|
40
|
+
}
|
41
|
+
|
42
|
+
expect(chunks).toEqual(
|
43
|
+
[
|
44
|
+
'id: chat_2',
|
45
|
+
'event: reasoning',
|
46
|
+
`data: ""\n`,
|
47
|
+
'id: chat_2',
|
48
|
+
'event: reasoning',
|
49
|
+
`data: "这是一个思考过程"\n`,
|
50
|
+
'id: chat_2',
|
51
|
+
'event: reasoning',
|
52
|
+
`data: ",需要仔细分析问题。"\n`,
|
53
|
+
'id: chat_2',
|
54
|
+
'event: text',
|
55
|
+
`data: ""\n`,
|
56
|
+
'id: chat_2',
|
57
|
+
'event: text',
|
58
|
+
`data: "根据分析,我的答案是:"\n`,
|
59
|
+
'id: chat_2',
|
60
|
+
'event: text',
|
61
|
+
`data: "这是最终答案。"\n`,
|
62
|
+
'id: chat_2',
|
63
|
+
'event: stop',
|
64
|
+
`data: "finished"\n`,
|
65
|
+
].map((line) => `${line}\n`)
|
66
|
+
);
|
67
|
+
});
|
68
|
+
|
10
69
|
it('text', async () => {
|
11
70
|
vi.spyOn(uuidModule, 'nanoid').mockReturnValueOnce('1');
|
12
71
|
|
@@ -32,7 +32,20 @@ const transformOllamaStream = (chunk: ChatResponse, stack: StreamContext): Strea
|
|
32
32
|
type: 'tool_calls',
|
33
33
|
};
|
34
34
|
}
|
35
|
-
|
35
|
+
|
36
|
+
// 判断是否有 <think> 或 </think> 标签,更新 thinkingInContent 状态
|
37
|
+
if (chunk.message.content.includes('<think>')) {
|
38
|
+
stack.thinkingInContent = true;
|
39
|
+
} else if (chunk.message.content.includes('</think>')) {
|
40
|
+
stack.thinkingInContent = false;
|
41
|
+
}
|
42
|
+
|
43
|
+
// 清除 <think> 及 </think> 标签,并根据当前思考模式确定返回类型
|
44
|
+
return {
|
45
|
+
data: chunk.message.content.replaceAll(/<\/?think>/g, ''),
|
46
|
+
id: stack.id,
|
47
|
+
type: stack?.thinkingInContent ? 'reasoning' : 'text',
|
48
|
+
};
|
36
49
|
};
|
37
50
|
|
38
51
|
export const OllamaStream = (
|
@@ -904,6 +904,169 @@ describe('OpenAIStream', () => {
|
|
904
904
|
});
|
905
905
|
|
906
906
|
describe('Reasoning', () => {
|
907
|
+
it('should handle <think></think> tags in streaming content', async () => {
|
908
|
+
const data = [
|
909
|
+
{
|
910
|
+
id: '1',
|
911
|
+
object: 'chat.completion.chunk',
|
912
|
+
created: 1737563070,
|
913
|
+
model: 'deepseek-reasoner',
|
914
|
+
system_fingerprint: 'fp_1c5d8833bc',
|
915
|
+
choices: [
|
916
|
+
{
|
917
|
+
index: 0,
|
918
|
+
delta: { content: '<think>' },
|
919
|
+
logprobs: null,
|
920
|
+
finish_reason: null,
|
921
|
+
},
|
922
|
+
],
|
923
|
+
},
|
924
|
+
{
|
925
|
+
id: '1',
|
926
|
+
object: 'chat.completion.chunk',
|
927
|
+
created: 1737563070,
|
928
|
+
model: 'deepseek-reasoner',
|
929
|
+
system_fingerprint: 'fp_1c5d8833bc',
|
930
|
+
choices: [
|
931
|
+
{
|
932
|
+
index: 0,
|
933
|
+
delta: { content: '这是一个思考过程' },
|
934
|
+
logprobs: null,
|
935
|
+
finish_reason: null,
|
936
|
+
},
|
937
|
+
],
|
938
|
+
},
|
939
|
+
{
|
940
|
+
id: '1',
|
941
|
+
object: 'chat.completion.chunk',
|
942
|
+
created: 1737563070,
|
943
|
+
model: 'deepseek-reasoner',
|
944
|
+
system_fingerprint: 'fp_1c5d8833bc',
|
945
|
+
choices: [
|
946
|
+
{
|
947
|
+
index: 0,
|
948
|
+
delta: { content: ',需要仔细分析问题。' },
|
949
|
+
logprobs: null,
|
950
|
+
finish_reason: null,
|
951
|
+
},
|
952
|
+
],
|
953
|
+
},
|
954
|
+
{
|
955
|
+
id: '1',
|
956
|
+
object: 'chat.completion.chunk',
|
957
|
+
created: 1737563070,
|
958
|
+
model: 'deepseek-reasoner',
|
959
|
+
system_fingerprint: 'fp_1c5d8833bc',
|
960
|
+
choices: [
|
961
|
+
{
|
962
|
+
index: 0,
|
963
|
+
delta: { content: '</think>' },
|
964
|
+
logprobs: null,
|
965
|
+
finish_reason: null,
|
966
|
+
},
|
967
|
+
],
|
968
|
+
},
|
969
|
+
{
|
970
|
+
id: '1',
|
971
|
+
object: 'chat.completion.chunk',
|
972
|
+
created: 1737563070,
|
973
|
+
model: 'deepseek-reasoner',
|
974
|
+
system_fingerprint: 'fp_1c5d8833bc',
|
975
|
+
choices: [
|
976
|
+
{
|
977
|
+
index: 0,
|
978
|
+
delta: { content: '根据分析,我的答案是:' },
|
979
|
+
logprobs: null,
|
980
|
+
finish_reason: null,
|
981
|
+
},
|
982
|
+
],
|
983
|
+
},
|
984
|
+
{
|
985
|
+
id: '1',
|
986
|
+
object: 'chat.completion.chunk',
|
987
|
+
created: 1737563070,
|
988
|
+
model: 'deepseek-reasoner',
|
989
|
+
system_fingerprint: 'fp_1c5d8833bc',
|
990
|
+
choices: [
|
991
|
+
{
|
992
|
+
index: 0,
|
993
|
+
delta: { content: '这是最终答案。' },
|
994
|
+
logprobs: null,
|
995
|
+
finish_reason: null,
|
996
|
+
},
|
997
|
+
],
|
998
|
+
},
|
999
|
+
{
|
1000
|
+
id: '1',
|
1001
|
+
object: 'chat.completion.chunk',
|
1002
|
+
created: 1737563070,
|
1003
|
+
model: 'deepseek-reasoner',
|
1004
|
+
system_fingerprint: 'fp_1c5d8833bc',
|
1005
|
+
choices: [
|
1006
|
+
{
|
1007
|
+
index: 0,
|
1008
|
+
delta: { content: '' },
|
1009
|
+
logprobs: null,
|
1010
|
+
finish_reason: 'stop',
|
1011
|
+
},
|
1012
|
+
],
|
1013
|
+
usage: {
|
1014
|
+
prompt_tokens: 10,
|
1015
|
+
completion_tokens: 50,
|
1016
|
+
total_tokens: 60,
|
1017
|
+
prompt_tokens_details: { cached_tokens: 0 },
|
1018
|
+
completion_tokens_details: { reasoning_tokens: 20 },
|
1019
|
+
prompt_cache_hit_tokens: 0,
|
1020
|
+
prompt_cache_miss_tokens: 10,
|
1021
|
+
},
|
1022
|
+
},
|
1023
|
+
];
|
1024
|
+
|
1025
|
+
const mockOpenAIStream = new ReadableStream({
|
1026
|
+
start(controller) {
|
1027
|
+
data.forEach((chunk) => {
|
1028
|
+
controller.enqueue(chunk);
|
1029
|
+
});
|
1030
|
+
controller.close();
|
1031
|
+
},
|
1032
|
+
});
|
1033
|
+
|
1034
|
+
const protocolStream = OpenAIStream(mockOpenAIStream);
|
1035
|
+
const decoder = new TextDecoder();
|
1036
|
+
const chunks = [];
|
1037
|
+
|
1038
|
+
// @ts-ignore
|
1039
|
+
for await (const chunk of protocolStream) {
|
1040
|
+
chunks.push(decoder.decode(chunk, { stream: true }));
|
1041
|
+
}
|
1042
|
+
|
1043
|
+
expect(chunks).toEqual(
|
1044
|
+
[
|
1045
|
+
'id: 1',
|
1046
|
+
'event: reasoning',
|
1047
|
+
`data: ""\n`,
|
1048
|
+
'id: 1',
|
1049
|
+
'event: reasoning',
|
1050
|
+
`data: "这是一个思考过程"\n`,
|
1051
|
+
'id: 1',
|
1052
|
+
'event: reasoning',
|
1053
|
+
`data: ",需要仔细分析问题。"\n`,
|
1054
|
+
'id: 1',
|
1055
|
+
'event: text',
|
1056
|
+
`data: ""\n`,
|
1057
|
+
'id: 1',
|
1058
|
+
'event: text',
|
1059
|
+
`data: "根据分析,我的答案是:"\n`,
|
1060
|
+
'id: 1',
|
1061
|
+
'event: text',
|
1062
|
+
`data: "这是最终答案。"\n`,
|
1063
|
+
'id: 1',
|
1064
|
+
'event: usage',
|
1065
|
+
`data: {"inputCacheMissTokens":10,"inputTextTokens":10,"outputReasoningTokens":20,"outputTextTokens":30,"totalInputTokens":10,"totalOutputTokens":50,"totalTokens":60}\n`,
|
1066
|
+
].map((i) => `${i}\n`),
|
1067
|
+
);
|
1068
|
+
});
|
1069
|
+
|
907
1070
|
it('should handle reasoning event in official DeepSeek api', async () => {
|
908
1071
|
const data = [
|
909
1072
|
{
|
@@ -210,6 +210,17 @@ const transformOpenAIStream = (
|
|
210
210
|
}
|
211
211
|
|
212
212
|
if (typeof content === 'string') {
|
213
|
+
// 清除 <think> 及 </think> 标签
|
214
|
+
const thinkingContent = content.replaceAll(/<\/?think>/g, '');
|
215
|
+
|
216
|
+
// 判断是否有 <think> 或 </think> 标签,更新 thinkingInContent 状态
|
217
|
+
if (content.includes('<think>')) {
|
218
|
+
streamContext.thinkingInContent = true;
|
219
|
+
} else if (content.includes('</think>')) {
|
220
|
+
streamContext.thinkingInContent = false;
|
221
|
+
}
|
222
|
+
|
223
|
+
// 判断是否有 citations 内容,更新 returnedCitation 状态
|
213
224
|
if (!streamContext?.returnedCitation) {
|
214
225
|
const citations =
|
215
226
|
// in Perplexity api, the citation is in every chunk, but we only need to return it once
|
@@ -237,12 +248,17 @@ const transformOpenAIStream = (
|
|
237
248
|
id: chunk.id,
|
238
249
|
type: 'grounding',
|
239
250
|
},
|
240
|
-
{ data:
|
251
|
+
{ data: thinkingContent, id: chunk.id, type: streamContext?.thinkingInContent ? 'reasoning' : 'text' },
|
241
252
|
];
|
242
253
|
}
|
243
254
|
}
|
244
255
|
|
245
|
-
|
256
|
+
// 根据当前思考模式确定返回类型
|
257
|
+
return {
|
258
|
+
data: thinkingContent,
|
259
|
+
id: chunk.id,
|
260
|
+
type: streamContext?.thinkingInContent ? 'reasoning' : 'text',
|
261
|
+
};
|
246
262
|
}
|
247
263
|
}
|
248
264
|
|
@@ -31,6 +31,18 @@ export interface StreamContext {
|
|
31
31
|
id: string;
|
32
32
|
name: string;
|
33
33
|
};
|
34
|
+
/**
|
35
|
+
* Indicates whether the current state is within a "thinking" segment of the model output
|
36
|
+
* (e.g., when processing lmstudio responses).
|
37
|
+
*
|
38
|
+
* When parsing output containing <think> and </think> tags:
|
39
|
+
* - Set to `true` upon encountering a <think> tag (entering reasoning mode)
|
40
|
+
* - Set to `false` upon encountering a </think> tag (exiting reasoning mode)
|
41
|
+
*
|
42
|
+
* While `thinkingInContent` is `true`, subsequent content should be stored in `reasoning_content`.
|
43
|
+
* When `false`, content should be stored in the regular `content` field.
|
44
|
+
*/
|
45
|
+
thinkingInContent?: boolean;
|
34
46
|
tool?: {
|
35
47
|
id: string;
|
36
48
|
index: number;
|
@@ -618,7 +618,6 @@ describe('ChatService', () => {
|
|
618
618
|
stream: true,
|
619
619
|
...DEFAULT_AGENT_CONFIG.params,
|
620
620
|
...params,
|
621
|
-
apiMode: 'responses',
|
622
621
|
};
|
623
622
|
|
624
623
|
await chatService.getChatCompletion(params, options);
|
@@ -682,7 +681,6 @@ describe('ChatService', () => {
|
|
682
681
|
stream: true,
|
683
682
|
...DEFAULT_AGENT_CONFIG.params,
|
684
683
|
...params,
|
685
|
-
apiMode: 'responses',
|
686
684
|
};
|
687
685
|
|
688
686
|
const result = await chatService.getChatCompletion(params, options);
|
@@ -106,7 +106,7 @@ const isProviderEnableResponseApi = (id: string) => (s: AIProviderStoreState) =>
|
|
106
106
|
|
107
107
|
if (typeof enableResponseApi === 'boolean') return enableResponseApi;
|
108
108
|
|
109
|
-
return
|
109
|
+
return false;
|
110
110
|
};
|
111
111
|
|
112
112
|
export const aiProviderSelectors = {
|