mirror of
https://gitee.com/wanwujie/deer-flow
synced 2026-04-21 21:24:46 +08:00
feat: display ask_clarification tool messages directly in frontend
Simplify clarification message handling by having the frontend detect and display ask_clarification tool messages directly, instead of relying on backend to add an extra AIMessage. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -5,7 +5,7 @@ from typing import override
|
|||||||
|
|
||||||
from langchain.agents import AgentState
|
from langchain.agents import AgentState
|
||||||
from langchain.agents.middleware import AgentMiddleware
|
from langchain.agents.middleware import AgentMiddleware
|
||||||
from langchain_core.messages import AIMessage, ToolMessage
|
from langchain_core.messages import ToolMessage
|
||||||
from langgraph.graph import END
|
from langgraph.graph import END
|
||||||
from langgraph.prebuilt.tool_node import ToolCallRequest
|
from langgraph.prebuilt.tool_node import ToolCallRequest
|
||||||
from langgraph.types import Command
|
from langgraph.types import Command
|
||||||
@@ -118,17 +118,13 @@ class ClarificationMiddleware(AgentMiddleware[ClarificationMiddlewareState]):
|
|||||||
name="ask_clarification",
|
name="ask_clarification",
|
||||||
)
|
)
|
||||||
|
|
||||||
ai_response_message = AIMessage(content=formatted_message)
|
|
||||||
|
|
||||||
# Return a Command that:
|
# Return a Command that:
|
||||||
# 1. Adds the formatted tool message (keeping the AI message intact)
|
# 1. Adds the formatted tool message
|
||||||
# 2. Interrupts execution by going to __end__
|
# 2. Interrupts execution by going to __end__
|
||||||
# Note: We don't modify the AI message to preserve all fields (reasoning_content, tool_calls, etc.)
|
# Note: We don't add an extra AIMessage here - the frontend will detect
|
||||||
# This is especially important for thinking mode where reasoning_content is required
|
# and display ask_clarification tool messages directly
|
||||||
|
|
||||||
# Return Command to add the tool message and interrupt
|
|
||||||
return Command(
|
return Command(
|
||||||
update={"messages": [tool_message, ai_response_message]},
|
update={"messages": [tool_message]},
|
||||||
goto=END,
|
goto=END,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -53,6 +53,17 @@ export function MessageList({
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if (group.type === "assistant:clarification") {
|
||||||
|
const message = group.messages[0];
|
||||||
|
if (message && hasContent(message)) {
|
||||||
|
return (
|
||||||
|
<MessageResponse key={group.id} rehypePlugins={rehypePlugins}>
|
||||||
|
{extractContentFromMessage(message)}
|
||||||
|
</MessageResponse>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
if (group.type === "assistant:present-files") {
|
if (group.type === "assistant:present-files") {
|
||||||
const files: string[] = [];
|
const files: string[] = [];
|
||||||
for (const message of group.messages) {
|
for (const message of group.messages) {
|
||||||
|
|||||||
@@ -14,11 +14,14 @@ interface AssistantMessageGroup extends GenericMessageGroup<"assistant"> {}
|
|||||||
|
|
||||||
interface AssistantPresentFilesGroup extends GenericMessageGroup<"assistant:present-files"> {}
|
interface AssistantPresentFilesGroup extends GenericMessageGroup<"assistant:present-files"> {}
|
||||||
|
|
||||||
|
interface AssistantClarificationGroup extends GenericMessageGroup<"assistant:clarification"> {}
|
||||||
|
|
||||||
type MessageGroup =
|
type MessageGroup =
|
||||||
| HumanMessageGroup
|
| HumanMessageGroup
|
||||||
| AssistantProcessingGroup
|
| AssistantProcessingGroup
|
||||||
| AssistantMessageGroup
|
| AssistantMessageGroup
|
||||||
| AssistantPresentFilesGroup;
|
| AssistantPresentFilesGroup
|
||||||
|
| AssistantClarificationGroup;
|
||||||
|
|
||||||
export function groupMessages<T>(
|
export function groupMessages<T>(
|
||||||
messages: Message[],
|
messages: Message[],
|
||||||
@@ -38,10 +41,28 @@ export function groupMessages<T>(
|
|||||||
messages: [message],
|
messages: [message],
|
||||||
});
|
});
|
||||||
} else if (message.type === "tool") {
|
} else if (message.type === "tool") {
|
||||||
if (
|
// Check if this is a clarification tool message
|
||||||
|
if (isClarificationToolMessage(message)) {
|
||||||
|
// Add to processing group if available (to maintain tool call association)
|
||||||
|
if (
|
||||||
|
lastGroup &&
|
||||||
|
lastGroup.type !== "human" &&
|
||||||
|
lastGroup.type !== "assistant" &&
|
||||||
|
lastGroup.type !== "assistant:clarification"
|
||||||
|
) {
|
||||||
|
lastGroup.messages.push(message);
|
||||||
|
}
|
||||||
|
// Also create a separate clarification group for prominent display
|
||||||
|
groups.push({
|
||||||
|
id: message.id,
|
||||||
|
type: "assistant:clarification",
|
||||||
|
messages: [message],
|
||||||
|
});
|
||||||
|
} else if (
|
||||||
lastGroup &&
|
lastGroup &&
|
||||||
lastGroup.type !== "human" &&
|
lastGroup.type !== "human" &&
|
||||||
lastGroup.type !== "assistant"
|
lastGroup.type !== "assistant" &&
|
||||||
|
lastGroup.type !== "assistant:clarification"
|
||||||
) {
|
) {
|
||||||
lastGroup.messages.push(message);
|
lastGroup.messages.push(message);
|
||||||
} else {
|
} else {
|
||||||
@@ -190,6 +211,10 @@ export function hasPresentFiles(message: Message) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isClarificationToolMessage(message: Message) {
|
||||||
|
return message.type === "tool" && message.name === "ask_clarification";
|
||||||
|
}
|
||||||
|
|
||||||
export function extractPresentFilesFromMessage(message: Message) {
|
export function extractPresentFilesFromMessage(message: Message) {
|
||||||
if (message.type !== "ai" || !hasPresentFiles(message)) {
|
if (message.type !== "ai" || !hasPresentFiles(message)) {
|
||||||
return [];
|
return [];
|
||||||
|
|||||||
Reference in New Issue
Block a user