fix: streaming text accumulation via ref + rAF throttle (prevent duplicate chars)
This commit is contained in:
parent
7c0580e666
commit
665024e70b
@ -73,40 +73,58 @@ export default function ChatPanel({ chatId, projectId }: Props) {
|
|||||||
return [...prev, msg];
|
return [...prev, msg];
|
||||||
});
|
});
|
||||||
// Clear streaming when final message arrives
|
// Clear streaming when final message arrives
|
||||||
|
streamRef.current = null;
|
||||||
setStreaming(null);
|
setStreaming(null);
|
||||||
});
|
});
|
||||||
return () => { unsub?.(); };
|
return () => { unsub?.(); };
|
||||||
}, [chatId]);
|
}, [chatId]);
|
||||||
|
|
||||||
// WS: agent streaming events
|
// WS: agent streaming events — use ref to accumulate, throttle renders
|
||||||
|
const streamRef = useRef<StreamingState | null>(null);
|
||||||
|
const streamRafRef = useRef<number>(0);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
const flushStream = () => {
|
||||||
|
setStreaming(streamRef.current ? { ...streamRef.current } : null);
|
||||||
|
};
|
||||||
|
const scheduleFlush = () => {
|
||||||
|
if (!streamRafRef.current) {
|
||||||
|
streamRafRef.current = requestAnimationFrame(() => {
|
||||||
|
streamRafRef.current = 0;
|
||||||
|
flushStream();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const unsubStart = wsClient.on("agent.stream.start", (data: any) => {
|
const unsubStart = wsClient.on("agent.stream.start", (data: any) => {
|
||||||
if (data.chat_id !== chatId) return;
|
if (data.chat_id !== chatId) return;
|
||||||
setStreaming({ agentSlug: data.agent_slug || "agent", text: "", thinking: "", tools: [] });
|
streamRef.current = { agentSlug: data.agent_slug || "agent", text: "", thinking: "", tools: [] };
|
||||||
|
flushStream();
|
||||||
});
|
});
|
||||||
const unsubDelta = wsClient.on("agent.stream.delta", (data: any) => {
|
const unsubDelta = wsClient.on("agent.stream.delta", (data: any) => {
|
||||||
if (data.chat_id !== chatId) return;
|
if (data.chat_id !== chatId) return;
|
||||||
setStreaming((prev) => {
|
const s = streamRef.current;
|
||||||
if (!prev) return { agentSlug: data.agent_slug || "agent", text: "", thinking: "", tools: [] };
|
if (!s) {
|
||||||
if (data.block_type === "thinking") {
|
streamRef.current = { agentSlug: data.agent_slug || "agent", text: "", thinking: "", tools: [] };
|
||||||
return { ...prev, thinking: prev.thinking + (data.text || "") };
|
|
||||||
}
|
}
|
||||||
return { ...prev, text: prev.text + (data.text || "") };
|
if (data.block_type === "thinking") {
|
||||||
});
|
streamRef.current!.thinking += data.text || "";
|
||||||
|
} else {
|
||||||
|
streamRef.current!.text += data.text || "";
|
||||||
|
}
|
||||||
|
scheduleFlush();
|
||||||
});
|
});
|
||||||
const unsubTool = wsClient.on("agent.stream.tool", (data: any) => {
|
const unsubTool = wsClient.on("agent.stream.tool", (data: any) => {
|
||||||
if (data.chat_id !== chatId) return;
|
if (data.chat_id !== chatId) return;
|
||||||
setStreaming((prev) => {
|
const s = streamRef.current;
|
||||||
if (!prev) return null;
|
if (!s) return;
|
||||||
const tools = [...prev.tools];
|
const existing = s.tools.findIndex((t) => t.tool === data.tool);
|
||||||
const existing = tools.findIndex((t) => t.tool === data.tool);
|
|
||||||
if (existing >= 0) {
|
if (existing >= 0) {
|
||||||
tools[existing] = { tool: data.tool, status: data.status };
|
s.tools[existing] = { tool: data.tool, status: data.status };
|
||||||
} else {
|
} else {
|
||||||
tools.push({ tool: data.tool, status: data.status });
|
s.tools.push({ tool: data.tool, status: data.status });
|
||||||
}
|
}
|
||||||
return { ...prev, tools };
|
scheduleFlush();
|
||||||
});
|
|
||||||
});
|
});
|
||||||
const unsubEnd = wsClient.on("agent.stream.end", (data: any) => {
|
const unsubEnd = wsClient.on("agent.stream.end", (data: any) => {
|
||||||
if (data.chat_id !== chatId) return;
|
if (data.chat_id !== chatId) return;
|
||||||
@ -117,6 +135,8 @@ export default function ChatPanel({ chatId, projectId }: Props) {
|
|||||||
unsubDelta?.();
|
unsubDelta?.();
|
||||||
unsubTool?.();
|
unsubTool?.();
|
||||||
unsubEnd?.();
|
unsubEnd?.();
|
||||||
|
if (streamRafRef.current) cancelAnimationFrame(streamRafRef.current);
|
||||||
|
streamRef.current = null;
|
||||||
};
|
};
|
||||||
}, [chatId]);
|
}, [chatId]);
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user