mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-05-05 23:42:03 +00:00
fix(cli): preserve description in subject-bearing thought chunks (#3691)
When a streamed reasoning chunk arrived with both a parsed subject (from **Title**) and a description (the body text after \n\n in the same chunk), the Thought event handler routed only to setThought and discarded the description. As a result, the first body word that happened to share a chunk with the closing ** was dropped from the persistent reasoning display. Treat subject-only chunks as discrete loading-indicator updates and route all chunks carrying streamed text through the throttled buffer. The existing flush merger preserves the subject across batched events.
This commit is contained in:
parent
e973dabf37
commit
6763124a05
2 changed files with 51 additions and 3 deletions
|
|
@ -2734,6 +2734,51 @@ describe('useGeminiStream', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('should render descriptions from subject-bearing thought chunks', async () => {
|
||||
mockSendMessageStream.mockReturnValue(
|
||||
(async function* () {
|
||||
yield {
|
||||
type: ServerGeminiEventType.Thought,
|
||||
value: {
|
||||
subject: 'Evaluating installation approach',
|
||||
description: 'The',
|
||||
},
|
||||
};
|
||||
yield {
|
||||
type: ServerGeminiEventType.Thought,
|
||||
value: {
|
||||
subject: '',
|
||||
description: ' user mentioned globally installed qwen,',
|
||||
},
|
||||
};
|
||||
yield {
|
||||
type: ServerGeminiEventType.Finished,
|
||||
value: { reason: 'STOP', usageMetadata: undefined },
|
||||
};
|
||||
})(),
|
||||
);
|
||||
|
||||
const { result } = renderTestHook();
|
||||
|
||||
await act(async () => {
|
||||
await result.current.submitQuery('Streamed thought');
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockAddItem).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
type: 'gemini_thought',
|
||||
text: 'The user mentioned globally installed qwen,',
|
||||
}),
|
||||
expect.any(Number),
|
||||
);
|
||||
});
|
||||
expect(result.current.thought).toEqual({
|
||||
subject: 'Evaluating installation approach',
|
||||
description: 'The user mentioned globally installed qwen,',
|
||||
});
|
||||
});
|
||||
|
||||
it('should show a retry countdown and update pending history over time', async () => {
|
||||
vi.useFakeTimers();
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -1285,9 +1285,12 @@ export const useGeminiStream = (
|
|||
dualOutput?.processEvent(event);
|
||||
switch (event.type) {
|
||||
case ServerGeminiEventType.Thought:
|
||||
// If the thought has a subject, it's a discrete status update rather than
|
||||
// a streamed textual thought, so we update the thought state directly.
|
||||
if (event.value.subject) {
|
||||
// Subject-only chunks are discrete status updates for the
|
||||
// loading indicator and render immediately. Anything carrying
|
||||
// streamed text (with or without a subject) goes through the
|
||||
// throttled buffer so it batches with adjacent reasoning
|
||||
// chunks; the flush merger preserves the subject.
|
||||
if (event.value.subject && !event.value.description) {
|
||||
flushBufferedStreamEvents();
|
||||
setThought(event.value);
|
||||
} else {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue