ag-ui-protocol / ag-ui-protocol/ag-ui

[Bug]: ToolMessage.name=None on OnToolEnd causes downstream validation errors in MessagesSnapshot

Đang mở
#1,742 0 bình luận 0 reaction 0 người được giao Xem trên GitHub
bug Integration
Ngôn ngữ chính
Python
Star
15.9k
Fork
1.4k
Merge trung bình
1 ngày 17 giờ
Pull request đã merge (30 ngày)
163

Mô tả

### Pre-flight Checklist

- [x] I have searched [existing issues](https://github.com/ag-ui-protocol/ag-ui/issues) and this hasn't been reported yet.
- [x] I am using the **latest** version AG-UI.

### Describe the Bug

When a LangGraph tool returns a `ToolMessage` with `name=None` (which is a valid default — LangChain's `ToolMessage` has `name: Optional[str] = None`), the `OnToolEnd` handler in `agent.py` correctly falls back to `event.get("name", "")` when emitting `ToolCallStartEvent` (line 1243). However, **the `ToolMessage` object itself retains `name=None`** in the LangGraph checkpoint.

This causes two downstream problems:

1. **`langchain_messages_to_agui`** converts these messages for the `MessagesSnapshotEvent`. The AG-UI `ToolMessage` type expects a non-None `name` field, and `None` produces an invalid snapshot or a Pydantic `ValidationError` depending on the consumer.

2. **`Command` objects** — when a tool returns a `Command(update={"messages": [ToolMessage(tool_call_id=..., content=..., name=None)]})`, the `name=None` propagates through the same path.

The root cause is that the `OnToolEnd` handler only compensates for `None` at the *event emission* layer (`ToolCallStartEvent`) but does not fix the underlying `ToolMessage` object that gets committed to the checkpoint and later fed to `langchain_messages_to_agui`.

### Steps to Reproduce

1. Create a LangGraph tool that returns a `ToolMessage` without setting `name`:

```python
from langchain_core.messages import ToolMessage

def my_tool(query: str) -> ToolMessage:
return ToolMessage(content="result", tool_call_id="tc-123")
# name defaults to None
```

2. Run the agent through `ag-ui-langgraph`.
3. After the tool completes, the `MessagesSnapshotEvent` includes a `ToolMessage` with `name=None`.
4. Depending on the AG-UI client, this causes a validation error or renders incorrectly.

### Expected Behavior

The `OnToolEnd` handler should patch `ToolMessage.name` on the actual message object (not just on the event), so that downstream consumers (especially `langchain_messages_to_agui` for `MessagesSnapshotEvent`) see a valid name.

Suggested fix — patch the `ToolMessage` before processing:

```python
elif event_type == LangGraphEventTypes.OnToolEnd:
tool_call_output = event["data"]["output"]
tool_name = event.get("name") or "unknown"

# Patch ToolMessage.name when None to avoid downstream validation errors
if isinstance(tool_call_output, ToolMessage) and tool_call_output.name is None:
tool_call_output.name = tool_name
elif isinstance(tool_call_output, Command) and tool_call_output.update:
for msg in tool_call_output.update.get("messages", []):
if isinstance(msg, ToolMessage) and msg.name is None:
msg.name = tool_name
# ... rest of handler
```

### Environment

```text
ag-ui-langgraph: 0.0.35
ag-ui-protocol: 0.1.18
langgraph: 0.4.7
langchain-core: 0.3.59
Python: 3.13
```

### Additional Context

Workaround: override `_handle_single_event` and patch `ToolMessage.name` before delegating to the base class.

Happy to send a PR with the fix and a regression test if assigned.

Hướng dẫn đóng góp

Mở hướng dẫn đóng góp

Đánh giá

Issue này chưa được đánh giá.

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.