fix(lingua): skip content ToolUse blocks when tool_calls is non-empty#170
Open
ekeith (evanmkeith) wants to merge 1 commit intomainfrom
Conversation
## Summary
LangGraph stores AIMessages in graph state with two representations of the
same tool call: `content[].tool_use` (Anthropic/Bedrock provider format) and
`tool_calls[]` (LangChain-native format). `parse_assistant_content` processed
both without deduplication, emitting two `ToolCall` parts that rendered as
duplicate entries in the LLM view tab.
## Change
In `parse_assistant_content` (`langchain.rs`), skip `LangChainContentPartCompat::ToolUse`
blocks in the `content[]` branch when `tool_calls` is non-empty. The
`tool_calls[]` array is the canonical source in this case. Text parts in
`content[]` continue to be processed normally.
The early-return path (when `tool_calls.is_empty() == true`) is unchanged —
providers that only populate `content[]` are unaffected.
## Notes
- Visual-only bug. Raw span data in brainstore is unaffected.
- Workaround for affected users: Thread view or JSON view.
- Related: the surviving entry's arguments may still show a
`$serde_json::private::Number` artifact for integer values due to a
`serde_wasm_bindgen` / `serde_json::Value` serialization issue — tracked
separately.
## Testing
- [ ] Existing Lingua tests pass (`cargo test`)
- [ ] New test: `parse_assistant_content` with dual-representation input
produces exactly one `ToolCall` part
- [ ] End-to-end: reproduction script at
`pylon-13952/reproduce_tool_call_duplication.py` shows tool call once in
LLM view tab
Fixes Pylon #13952 / BT-4608
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
LangGraph stores AIMessages in graph state with two representations of the
same tool call:
content[].tool_use(Anthropic/Bedrock provider format) andtool_calls[](LangChain-native format).parse_assistant_contentprocessedboth without deduplication, emitting two
ToolCallparts that rendered asduplicate entries in the LLM view tab.
Change
In
parse_assistant_content(langchain.rs), skipLangChainContentPartCompat::ToolUseblocks in the
content[]branch whentool_callsis non-empty. Thetool_calls[]array is the canonical source in this case. Text parts incontent[]continue to be processed normally.The early-return path (when
tool_calls.is_empty() == true) is unchanged —providers that only populate
content[]are unaffected.Notes
$serde_json::private::Numberartifact for integer values due to aserde_wasm_bindgen/serde_json::Valueserialization issue — tracked separately.Testing
cargo test)parse_assistant_contentwith dual-representation input produces exactly oneToolCallpartpylon-13952/reproduce_tool_call_duplication.pyshows tool call once in LLM view tabLinear bug here