mirror of
https://github.com/HKUDS/nanobot.git
synced 2026-05-20 16:42:25 +00:00
fix(webui): split reasoning at tool trace boundaries
Live rendering merged reasoning chunks by scanning backward to the latest assistant row. That fixed late reasoning, but the scan skipped trace rows, so reasoning after a tool call crossed the Used tools block and attached to the previous assistant iteration. Refresh looked correct because persisted history reconstructs assistant/tool boundaries. Treat trace rows as hard phase boundaries, just like user messages. A reasoning_delta after Used tools now starts a fresh assistant placeholder, so live rendering matches replay: Thinking -> Used tools -> Thinking -> Used tools / answer. Add a regression for reasoning_delta -> reasoning_end -> tool_hint -> reasoning_delta. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
278affc25e
commit
521aaa5ecf
@ -33,7 +33,11 @@ function attachReasoningChunk(prev: UIMessage[], chunk: string): UIMessage[] {
|
|||||||
// A user turn is a hard boundary: reasoning after it belongs to the new
|
// A user turn is a hard boundary: reasoning after it belongs to the new
|
||||||
// assistant turn, never to an earlier assistant reply.
|
// assistant turn, never to an earlier assistant reply.
|
||||||
if (candidate.role === "user") break;
|
if (candidate.role === "user") break;
|
||||||
if (candidate.role !== "assistant" || candidate.kind === "trace") continue;
|
// A trace row (e.g. Used tools) is also a phase boundary. Reasoning after
|
||||||
|
// tools belongs to the next assistant iteration, not the assistant turn
|
||||||
|
// that produced those tool calls.
|
||||||
|
if (candidate.kind === "trace") break;
|
||||||
|
if (candidate.role !== "assistant") continue;
|
||||||
const hasAnswer = candidate.content.length > 0;
|
const hasAnswer = candidate.content.length > 0;
|
||||||
if (
|
if (
|
||||||
candidate.reasoningStreaming
|
candidate.reasoningStreaming
|
||||||
|
|||||||
@ -276,6 +276,45 @@ describe("useNanobotStream", () => {
|
|||||||
expect(result.current.messages[2].reasoningStreaming).toBe(true);
|
expect(result.current.messages[2].reasoningStreaming).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("does not attach reasoning across a tool trace boundary", () => {
|
||||||
|
const fake = fakeClient();
|
||||||
|
const { result } = renderHook(() => useNanobotStream("chat-r7", EMPTY_MESSAGES), {
|
||||||
|
wrapper: wrap(fake.client),
|
||||||
|
});
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
fake.emit("chat-r7", {
|
||||||
|
event: "reasoning_delta",
|
||||||
|
chat_id: "chat-r7",
|
||||||
|
text: "First reasoning.",
|
||||||
|
});
|
||||||
|
fake.emit("chat-r7", { event: "reasoning_end", chat_id: "chat-r7" });
|
||||||
|
fake.emit("chat-r7", {
|
||||||
|
event: "message",
|
||||||
|
chat_id: "chat-r7",
|
||||||
|
text: "web_search({\"query\":\"OpenClaw\"})",
|
||||||
|
kind: "tool_hint",
|
||||||
|
});
|
||||||
|
fake.emit("chat-r7", {
|
||||||
|
event: "reasoning_delta",
|
||||||
|
chat_id: "chat-r7",
|
||||||
|
text: "Second reasoning.",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.current.messages).toHaveLength(3);
|
||||||
|
expect(result.current.messages.map((m) => m.kind ?? "message")).toEqual([
|
||||||
|
"message",
|
||||||
|
"trace",
|
||||||
|
"message",
|
||||||
|
]);
|
||||||
|
expect(result.current.messages[0].reasoning).toBe("First reasoning.");
|
||||||
|
expect(result.current.messages[1].traces).toEqual([
|
||||||
|
"web_search({\"query\":\"OpenClaw\"})",
|
||||||
|
]);
|
||||||
|
expect(result.current.messages[2].reasoning).toBe("Second reasoning.");
|
||||||
|
});
|
||||||
|
|
||||||
it("attaches assistant media_urls to complete messages", () => {
|
it("attaches assistant media_urls to complete messages", () => {
|
||||||
const fake = fakeClient();
|
const fake = fakeClient();
|
||||||
const { result } = renderHook(() => useNanobotStream("chat-m", EMPTY_MESSAGES), {
|
const { result } = renderHook(() => useNanobotStream("chat-m", EMPTY_MESSAGES), {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user