fix(webui): render local CLI image artifacts
This commit is contained in:
@@ -193,6 +193,15 @@ function stampLastAssistantLatency(prev: UIMessage[], latencyMs: number): UIMess
|
||||
return prev;
|
||||
}
|
||||
|
||||
function findLatestAssistantAnswerIndex(prev: UIMessage[]): number | null {
|
||||
for (let i = prev.length - 1; i >= 0; i -= 1) {
|
||||
const m = prev[i];
|
||||
if (m.role === "assistant" && m.kind !== "trace") return i;
|
||||
if (m.role === "user") break;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function absorbCompleteAssistantMessage(
|
||||
prev: UIMessage[],
|
||||
message: Omit<UIMessage, "id" | "role" | "createdAt">,
|
||||
@@ -489,23 +498,41 @@ export function useNanobotStream(
|
||||
[appendAnswerChunk, ensureActivitySegmentId],
|
||||
);
|
||||
|
||||
const flushPendingStreamEvents = useCallback((options?: { closeAnswerSegment?: boolean }) => {
|
||||
const flushPendingStreamEvents = useCallback((options?: {
|
||||
closeAnswerSegment?: boolean;
|
||||
finalAnswerText?: string;
|
||||
}) => {
|
||||
if (streamFrameRef.current !== null) {
|
||||
window.cancelAnimationFrame(streamFrameRef.current);
|
||||
streamFrameRef.current = null;
|
||||
}
|
||||
const events = pendingStreamEventsRef.current;
|
||||
if (events.length === 0) {
|
||||
const finalAnswerText = options?.finalAnswerText;
|
||||
if (events.length === 0 && finalAnswerText === undefined) {
|
||||
if (options?.closeAnswerSegment) closeActiveAssistantStream();
|
||||
return;
|
||||
}
|
||||
pendingStreamEventsRef.current = [];
|
||||
setMessages((prev) => {
|
||||
const next = applyPendingStreamEvents(prev, events);
|
||||
let next = events.length > 0 ? applyPendingStreamEvents(prev, events) : prev;
|
||||
if (finalAnswerText !== undefined) {
|
||||
const targetIndex =
|
||||
resolveActiveAssistantIndex(next)
|
||||
?? findStreamingAssistantIndex(next, closedAssistantStreamIdsRef.current)
|
||||
?? findLatestAssistantAnswerIndex(next);
|
||||
if (targetIndex !== null) {
|
||||
const target = next[targetIndex];
|
||||
next = replaceMessageAt(next, targetIndex, {
|
||||
...target,
|
||||
content: finalAnswerText,
|
||||
isStreaming: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
if (options?.closeAnswerSegment) closeActiveAssistantStream();
|
||||
return next;
|
||||
});
|
||||
}, [applyPendingStreamEvents, closeActiveAssistantStream]);
|
||||
}, [applyPendingStreamEvents, closeActiveAssistantStream, resolveActiveAssistantIndex]);
|
||||
|
||||
const schedulePendingStreamFlush = useCallback(() => {
|
||||
if (streamFrameRef.current !== null) return;
|
||||
@@ -583,7 +610,10 @@ export function useNanobotStream(
|
||||
}
|
||||
|
||||
if (ev.event === "stream_end") {
|
||||
flushPendingStreamEvents({ closeAnswerSegment: true });
|
||||
flushPendingStreamEvents({
|
||||
closeAnswerSegment: true,
|
||||
...(typeof ev.text === "string" ? { finalAnswerText: ev.text } : {}),
|
||||
});
|
||||
if (suppressStreamUntilTurnEndRef.current) return;
|
||||
// stream_end only means the text segment finished — the model may
|
||||
// still be executing tools. Do NOT reset isStreaming here; the
|
||||
|
||||
Reference in New Issue
Block a user