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:
hetaoBackend
2026-01-29 01:25:05 +08:00
parent a010953880
commit d4bfed271b
3 changed files with 44 additions and 12 deletions

View File

@@ -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,
) )

View File

@@ -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) {

View File

@@ -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 [];