From ca5b7225711c990e8bd3a8d1afed08fdcde74605 Mon Sep 17 00:00:00 2001 From: lofyer Date: Wed, 12 Aug 2026 23:10:25 +0800 Subject: [PATCH] fix: keep streamed reasoning expanded --- src/main/agent/model-runtime.test.ts | 86 ++++++++++++++++++++++++++++ src/renderer/src/App.test.tsx | 5 ++ src/renderer/src/App.tsx | 6 +- 3 files changed, 92 insertions(+), 5 deletions(-) diff --git a/src/main/agent/model-runtime.test.ts b/src/main/agent/model-runtime.test.ts index bf266c4..82cdaed 100644 --- a/src/main/agent/model-runtime.test.ts +++ b/src/main/agent/model-runtime.test.ts @@ -453,6 +453,92 @@ describe('ModelAgentRuntime', () => { expect(toolProvider.listTools).not.toHaveBeenCalled() }) + it('streams OpenAI-compatible reasoning deltas before the answer', async () => { + const stream = [ + `data: ${JSON.stringify({ + choices: [ + { + delta: { + reasoning_content: '先分析' + } + } + ] + })}`, + '', + `data: ${JSON.stringify({ + choices: [ + { + delta: { + reasoning_content: ',再验证' + } + } + ] + })}`, + '', + `data: ${JSON.stringify({ + choices: [ + { + delta: { + content: '最终回答' + } + } + ] + })}`, + '', + 'data: [DONE]', + '', + '' + ].join('\n') + const runtime = new ModelAgentRuntime({ + apiKey: 'test-key', + baseUrl: 'https://api.deepseek.com', + model: 'deepseek-reasoner', + protocol: 'openai-chat-completions', + authentication: 'api-key', + fetcher: vi.fn(async () => + new Response(stream, { + status: 200, + headers: { 'content-type': 'text/event-stream' } + }) + ) + }) + const events = [] + + for await (const event of runtime.run( + { + requestId: 'a431666e-5ec8-45e6-beb4-654132eed128', + conversationId: 'conversation-deepseek-reasoning', + prompt: '分析这个问题' + }, + new AbortController().signal + )) { + events.push(event) + } + + expect( + events.filter( + (event) => event.type === 'reasoning' || event.type === 'text' + ) + ).toEqual([ + { + requestId: 'a431666e-5ec8-45e6-beb4-654132eed128', + type: 'reasoning', + delta: '先分析' + }, + { + requestId: 'a431666e-5ec8-45e6-beb4-654132eed128', + type: 'reasoning', + delta: ',再验证' + }, + { + requestId: 'a431666e-5ec8-45e6-beb4-654132eed128', + type: 'text', + delta: '最终回答' + } + ]) + expect(events.at(-1)).toMatchObject({ type: 'done' }) + }) + it('keeps browser and workspace tools out of Ask mode', async () => { const fetcher = vi.fn(async () => new Response('data: {"choices":[{"delta":{"content":"只读回答"}}]}\n\ndata: [DONE]\n\n', { diff --git a/src/renderer/src/App.test.tsx b/src/renderer/src/App.test.tsx index 924c3b9..3e3eed3 100644 --- a/src/renderer/src/App.test.tsx +++ b/src/renderer/src/App.test.tsx @@ -1399,6 +1399,11 @@ describe('App', () => { (_, element) => element?.textContent === rawToolOutput ) ).toBeVisible() + const activeReasoning = screen.getAllByText('正在推理') + expect(activeReasoning).toHaveLength(2) + for (const reasoning of activeReasoning) { + expect(reasoning.closest('details')).toHaveAttribute('open') + } act(() => { if (!request) { diff --git a/src/renderer/src/App.tsx b/src/renderer/src/App.tsx index 58dfa96..e9522c0 100644 --- a/src/renderer/src/App.tsx +++ b/src/renderer/src/App.tsx @@ -5446,11 +5446,7 @@ function App(): React.JSX.Element {
{message.state === 'streaming'