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];
|
||||
});
|
||||
// Clear streaming when final message arrives
|
||||
streamRef.current = null;
|
||||
setStreaming(null);
|
||||
});
|
||||
return () => { unsub?.(); };
|
||||
}, [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(() => {
|
||||
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) => {
|
||||
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) => {
|
||||
if (data.chat_id !== chatId) return;
|
||||
setStreaming((prev) => {
|
||||
if (!prev) return { agentSlug: data.agent_slug || "agent", text: "", thinking: "", tools: [] };
|
||||
if (data.block_type === "thinking") {
|
||||
return { ...prev, thinking: prev.thinking + (data.text || "") };
|
||||
const s = streamRef.current;
|
||||
if (!s) {
|
||||
streamRef.current = { agentSlug: data.agent_slug || "agent", text: "", thinking: "", tools: [] };
|
||||
}
|
||||
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) => {
|
||||
if (data.chat_id !== chatId) return;
|
||||
setStreaming((prev) => {
|
||||
if (!prev) return null;
|
||||
const tools = [...prev.tools];
|
||||
const existing = tools.findIndex((t) => t.tool === data.tool);
|
||||
const s = streamRef.current;
|
||||
if (!s) return;
|
||||
const existing = s.tools.findIndex((t) => t.tool === data.tool);
|
||||
if (existing >= 0) {
|
||||
tools[existing] = { tool: data.tool, status: data.status };
|
||||
s.tools[existing] = { tool: data.tool, status: data.status };
|
||||
} 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) => {
|
||||
if (data.chat_id !== chatId) return;
|
||||
@ -117,6 +135,8 @@ export default function ChatPanel({ chatId, projectId }: Props) {
|
||||
unsubDelta?.();
|
||||
unsubTool?.();
|
||||
unsubEnd?.();
|
||||
if (streamRafRef.current) cancelAnimationFrame(streamRafRef.current);
|
||||
streamRef.current = null;
|
||||
};
|
||||
}, [chatId]);
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user