Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions integrations/langgraph/python/ag_ui_langgraph/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,9 @@ def agui_messages_to_langchain(messages: List[AGUIMessage]) -> List[BaseMessage]
id=message.id,
content=message.content,
tool_call_id=message.tool_call_id,
# Carry the AG-UI failure signal onto LangChain's tool-result status, so a
# client-reported tool failure is not delivered to the model as a success.
status="error" if message.error else "success",
))
else:
raise ValueError(f"Unsupported message role: {role}")
Expand Down
16 changes: 16 additions & 0 deletions integrations/langgraph/python/tests/test_message_conversion.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,22 @@ def test_tool_message(self):
assert isinstance(result[0], ToolMessage)
assert result[0].content == "42"
assert result[0].tool_call_id == "tc1"
# A tool result with no error maps to LangChain's default "success" status.
assert result[0].status == "success"

def test_tool_message_error_maps_to_status(self):
# A client-reported tool failure must reach the model as an error, not a
# silent success -- AG-UI's ToolMessage.error becomes status="error".
msg = AGUIToolMessage(
id="t1",
role="tool",
content="Tool failed: invalid id",
tool_call_id="tc1",
error="invalid id",
)
result = agui_messages_to_langchain([msg])
assert isinstance(result[0], ToolMessage)
assert result[0].status == "error"

def test_multimodal_with_url(self):
msg = AGUIUserMessage(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,23 @@ describe("Message Conversion - All Types", () => {
expect(result[0].type).toBe("tool");
expect(result[0].content).toBe("42");
expect(result[0].tool_call_id).toBe("tc1");
// A tool result with no error maps to LangChain's default "success" status.
expect(result[0].status).toBe("success");
});

it("should map a tool message error onto LangChain's status flag", () => {
// A client-reported tool failure must reach the model as an error, not a
// silent success — AG-UI's ToolMessage.error becomes status: "error".
const msg: Message = {
id: "t1",
role: "tool",
content: "Tool failed: invalid id",
toolCallId: "tc1",
error: "invalid id",
};
const result: any[] = aguiMessagesToLangChain([msg]);
expect(result[0].type).toBe("tool");
expect(result[0].status).toBe("error");
});

it("should throw for unsupported role", () => {
Expand Down
3 changes: 3 additions & 0 deletions integrations/langgraph/typescript/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,9 @@ export function aguiMessagesToLangChain(messages: Message[]): LangGraphMessage[]
type: message.role,
tool_call_id: message.toolCallId,
id: message.id,
// Carry the AG-UI failure signal onto LangChain's tool-result status, so a
// client-reported tool failure is not delivered to the model as a success.
status: message.error ? "error" : "success",
} as LangGraphMessage);
break;
default:
Expand Down