fix(chat): refine tool phase detection and clean stale reply from graph cache
- hasCompletedToolPhase now checks that the last assistant message in the segment has no tool_use blocks, preventing false positives during intermediate tool rounds that would suppress the trailing thinking indicator - Filter reply text from cached graph steps when a completed run falls back to the step cache, preventing the final response from appearing inside the graph when expanding after completion - Remove debug logging Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -278,13 +278,19 @@ export function Chat() {
|
|||||||
// Three signals indicate "tools finished, now streaming the reply":
|
// Three signals indicate "tools finished, now streaming the reply":
|
||||||
// 1. `pendingFinal` — set by tool-result final events
|
// 1. `pendingFinal` — set by tool-result final events
|
||||||
// 2. `allToolsCompleted` — all entries in streamingTools are completed
|
// 2. `allToolsCompleted` — all entries in streamingTools are completed
|
||||||
// 3. `hasCompletedToolPhase` — historical messages (loaded by the poll)
|
// 3. `hasCompletedToolPhase` — the Gateway executed tools server-side
|
||||||
// contain tool_use blocks, meaning the Gateway executed tools
|
// (no streaming tool events) and the tool phase is over. Detected
|
||||||
// server-side without sending streaming tool events to the client
|
// by finding tool_use in historical messages AND the last assistant
|
||||||
|
// message in the segment having no tool_use blocks (meaning the
|
||||||
|
// model has moved past tool calls into the reply phase).
|
||||||
const allToolsCompleted = streamingTools.length > 0 && !hasRunningStreamToolStatus;
|
const allToolsCompleted = streamingTools.length > 0 && !hasRunningStreamToolStatus;
|
||||||
const hasCompletedToolPhase = segmentMessages.some((msg) =>
|
const lastAssistantInSegment = [...segmentMessages].reverse().find((m) => m.role === 'assistant');
|
||||||
|
const segmentHasTools = segmentMessages.some((msg) =>
|
||||||
msg.role === 'assistant' && extractToolUse(msg).length > 0,
|
msg.role === 'assistant' && extractToolUse(msg).length > 0,
|
||||||
);
|
);
|
||||||
|
const lastAssistantHasNoTools = lastAssistantInSegment != null
|
||||||
|
&& extractToolUse(lastAssistantInSegment).length === 0;
|
||||||
|
const hasCompletedToolPhase = segmentHasTools && lastAssistantHasNoTools;
|
||||||
const rawStreamingReplyCandidate = isLatestOpenRun
|
const rawStreamingReplyCandidate = isLatestOpenRun
|
||||||
&& (pendingFinal || allToolsCompleted || hasCompletedToolPhase)
|
&& (pendingFinal || allToolsCompleted || hasCompletedToolPhase)
|
||||||
&& (hasStreamText || hasStreamImages)
|
&& (hasStreamText || hasStreamImages)
|
||||||
@@ -323,6 +329,16 @@ export function Chat() {
|
|||||||
}
|
}
|
||||||
const cached = graphStepCache[runKey];
|
const cached = graphStepCache[runKey];
|
||||||
if (!cached) return [];
|
if (!cached) return [];
|
||||||
|
// When using cached steps for a completed run, filter out message
|
||||||
|
// steps whose text matches the final reply. The cache was captured
|
||||||
|
// during streaming when all text was narration; now that the run is
|
||||||
|
// complete the reply should not appear inside the graph.
|
||||||
|
const cachedReplyIdx = cached.replyIndex;
|
||||||
|
const replyMsg = cachedReplyIdx != null ? messages[cachedReplyIdx] : null;
|
||||||
|
const replyText = replyMsg ? extractText(replyMsg).trim() : '';
|
||||||
|
const cleanedSteps = replyText
|
||||||
|
? cached.steps.filter((s) => !(s.kind === 'message' && s.detail?.trim() === replyText))
|
||||||
|
: cached.steps;
|
||||||
return [{
|
return [{
|
||||||
triggerIndex: idx,
|
triggerIndex: idx,
|
||||||
replyIndex: cached.replyIndex,
|
replyIndex: cached.replyIndex,
|
||||||
@@ -330,8 +346,8 @@ export function Chat() {
|
|||||||
agentLabel: cached.agentLabel,
|
agentLabel: cached.agentLabel,
|
||||||
sessionLabel: cached.sessionLabel,
|
sessionLabel: cached.sessionLabel,
|
||||||
segmentEnd: nextUserIndex === -1 ? messages.length - 1 : nextUserIndex - 1,
|
segmentEnd: nextUserIndex === -1 ? messages.length - 1 : nextUserIndex - 1,
|
||||||
steps: cached.steps,
|
steps: cleanedSteps,
|
||||||
messageStepTexts: getPrimaryMessageStepTexts(cached.steps),
|
messageStepTexts: getPrimaryMessageStepTexts(cleanedSteps),
|
||||||
streamingReplyText: null,
|
streamingReplyText: null,
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user