From 592df5e6914ee2e08cbcec232673ee66507d7196 Mon Sep 17 00:00:00 2001 From: ScriptSmith Date: Sat, 21 Mar 2026 14:08:16 +1000 Subject: [PATCH 01/23] Fix overwriting final round text --- ui/src/pages/chat/useChat.ts | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/ui/src/pages/chat/useChat.ts b/ui/src/pages/chat/useChat.ts index eb8bc3c..33d9b2a 100644 --- a/ui/src/pages/chat/useChat.ts +++ b/ui/src/pages/chat/useChat.ts @@ -202,6 +202,8 @@ const MAX_TOOL_ITERATIONS = 5; /** Result from streaming a response, including any tool calls */ interface StreamResponseResult { content: string; + /** Whether any output_text deltas were received (vs reasoning-only fallback) */ + hasOutputText: boolean; usage?: MessageUsage; reasoningContent?: string; /** Tool calls detected during streaming (only when clientSideToolExecution is enabled) */ @@ -800,6 +802,7 @@ export function useChat({ let usage: MessageUsage | undefined; // Fallback: extract tool calls from response.completed if not captured during streaming let completedToolCalls: ParsedToolCall[] = []; + let hasOutputText = false; // Capture response output for debugging let responseOutput: unknown[] | undefined; @@ -856,6 +859,7 @@ export function useChat({ // Handle different Responses API event types if (event.type === "response.output_text.delta" && event.delta) { + hasOutputText = true; content += event.delta; streamingStore.appendContent(storeKey, event.delta); } else if ( @@ -1129,6 +1133,7 @@ export function useChat({ return { content, + hasOutputText, usage, reasoningContent: reasoningContent || undefined, toolCalls: toolCalls.length > 0 ? toolCalls : undefined, @@ -1312,6 +1317,7 @@ export function useChat({ ? null : { content: accumulatedContent, + hasOutputText: true, usage: accumulatedUsage, reasoningContent: lastReasoningContent, toolExecutionRounds: executionRounds.length > 0 ? executionRounds : undefined, @@ -1329,10 +1335,9 @@ export function useChat({ } // Accumulate content across rounds with separator. - // Skip reasoning-only rounds (where content was set from reasoning fallback - // rather than actual output text — e.g., rounds that only call display_artifacts). - const isActualOutput = result.content && result.content !== result.reasoningContent; - if (isActualOutput) { + // Only include rounds that had actual text output (output_text deltas), + // skipping reasoning-only rounds (e.g., rounds that only call display_artifacts). + if (result.hasOutputText) { if (accumulatedContent) { accumulatedContent += "\n\n---\n\n" + result.content; } else { @@ -1554,11 +1559,17 @@ export function useChat({ // builds on top of the accumulated content with a visual break. // Only add if this round had actual text output (avoid double separators // from rounds that only had tool calls with no text). - if (isActualOutput) { + if (result.hasOutputText) { streamingStore.appendContent(storeKey, "\n\n---\n\n"); } } + // Sync streaming store with final accumulated content (removes any + // trailing separators and ensures consistency with committed content) + if (iterations > 1) { + streamingStore.setContent(storeKey, accumulatedContent); + } + // Complete debug capture successfully if (messageId) { debugStore.completeDebugCapture(messageId, model, true); @@ -1566,6 +1577,7 @@ export function useChat({ return { content: accumulatedContent, + hasOutputText: true, usage: accumulatedUsage, reasoningContent: lastReasoningContent, toolExecutionRounds: executionRounds.length > 0 ? executionRounds : undefined, From a54706b9996b8a71df151c04a9d7adfff8cadeda Mon Sep 17 00:00:00 2001 From: ScriptSmith Date: Sat, 21 Mar 2026 15:16:41 +1000 Subject: [PATCH 02/23] Separate reasoning blocks --- .../MultiModelResponse/MultiModelResponse.tsx | 81 ++++++++++++++++--- ui/src/components/chat-types.ts | 4 +- ui/src/pages/chat/useChat.ts | 14 ++++ ui/src/stores/streamingStore.ts | 21 ++++- 4 files changed, 107 insertions(+), 13 deletions(-) diff --git a/ui/src/components/MultiModelResponse/MultiModelResponse.tsx b/ui/src/components/MultiModelResponse/MultiModelResponse.tsx index 7cb3b61..32f4b9d 100644 --- a/ui/src/components/MultiModelResponse/MultiModelResponse.tsx +++ b/ui/src/components/MultiModelResponse/MultiModelResponse.tsx @@ -189,8 +189,10 @@ interface ModelResponse { */ label?: string; content: string; - /** Reasoning content (extended thinking) */ + /** Reasoning content for current/last round (extended thinking) */ reasoningContent?: string; + /** Completed rounds' reasoning and content for multi-round tool execution */ + completedRounds?: Array<{ reasoning?: string; content?: string }>; isStreaming: boolean; error?: string; usage?: MessageUsage; @@ -804,15 +806,7 @@ const ModelResponseCard = memo(function ModelResponseCard({ <> {/* Tool call indicator (shown above content when tools are executing) */} {hasActiveToolCalls && } - {/* Reasoning section (extended thinking) */} - {(response.reasoningContent || response.usage?.reasoningContent) && ( - - )} - {/* Main response content */} + {/* Reasoning + content — interleaved per round for multi-round, or single block */} {isEditing ? (