Fix LangGraph content-block serialization in gen_ai.output.messages (#189)#193
Merged
JacksonWeber merged 4 commits intoJun 8, 2026
Conversation
…icrosoft#189) LangChain/LangGraph AIMessage.content may be a list of content-block dicts (e.g. [{'type': 'text', 'text': '...', 'phase': 'final_answer', 'id': '...'}]). The previous _langchain_content helper called str(c) on this value, producing a Python-repr blob with single quotes and leaked phase/index/id keys inside what the GenAI semconv requires to be a plain TextPart.content string. Changes: - New _flatten_lc_content_blocks helper concatenates the text of every type=='text' block (joined with newline) into a plain string. - _langchain_content now delegates to that helper for both BaseMessage and dict-shaped messages. - _langchain_tool_calls additionally harvests {'type': 'tool_use', ...} entries embedded in list-shaped content as ToolCallRequest parts so they surface as spec-typed parts instead of being dropped. - Three regression tests covering the exact issue shape, multi-block text join, and embedded tool_use harvest. - CHANGELOG entry under Unreleased. Verified the serialized output against the upstream gen-ai-output-messages.json schema. Fixes microsoft#189 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Performance comparisonThreshold: regressions >15.0% on gating scenarios fail the build. Higher ops/s is better; positive Δ means the PR is slower.
|
Contributor
There was a problem hiding this comment.
Pull request overview
This PR fixes LangChain/LangGraph AIMessage.content serialization for GenAI semantic conventions by normalizing list-shaped “content blocks” into spec-compliant output message parts, preventing Python repr blobs from being emitted in gen_ai.output.messages.
Changes:
- Add
_flatten_lc_content_blocks()to extract/concatenate text from LangGraph-style content-block lists forTextPart.content. - Extend tool-call extraction to also harvest
tool_useblocks embedded inside list-shapedcontent. - Add regression tests covering content-block flattening, multi-block joining, and embedded
tool_usehandling; document the fix inCHANGELOG.md.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
tests/langchain/test_utils.py |
Adds regression tests to ensure LangGraph content blocks serialize to spec-compliant text/tool-call parts. |
src/microsoft/opentelemetry/_genai/_langchain/_utils.py |
Implements content-block flattening and harvesting of embedded tool_use blocks into tool-call parts. |
CHANGELOG.md |
Notes the fix under “Unreleased”. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…ELOG heading level - _langchain_tool_calls now copies raw_calls before appending harvested tool_use blocks, so it never mutates BaseMessage.tool_calls or additional_kwargs['tool_calls']. - New test_extraction_does_not_mutate_input_message guards against regressions. - CHANGELOG Unreleased heading switched from ## to # to match the surrounding release headings. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
rads-1996
reviewed
Jun 8, 2026
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
rads-1996
reviewed
Jun 8, 2026
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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
Fixes #189.
LangChain/LangGraph
AIMessage.contentmay be either a plain string or a list of content-block dicts (e.g.[{"type": "text", "text": "...", "phase": "final_answer", "id": "msg_..."}]). The previous_langchain_contenthelper calledstr(c)on this value, producing a Python-reprblob with single quotes and leakedphase/index/idkeys inside what the GenAI semconv requires to be a plainTextPart.contentstring. This broke Foundry's cloud trace evaluators (builtin.coherence,builtin.fluency, etc.) which read the assistant text out ofgen_ai.output.messages[*].parts[*].content.Verification
Serialized a real LangGraph-shaped output and validated against the upstream
gen-ai-output-messages.jsonschema:[ { "role": "assistant", "parts": [ { "type": "text", "content": "# One-Day Food Walk in Vancouver\n## Assumptions" }, { "type": "tool_call", "id": "tool_1", "name": "search", "arguments": "{\"q\":\"food\"}" } ], "finish_reason": "stop" } ]All spec constraints satisfied:
role∈ allowed enum,TextPart.contentis a plain string with no repr blob or leaked metadata,ToolCallRequestParthas consttype: "tool_call"+ requiredname,finish_reasonin allowed enum.tests/langchain/test_utils.py: 128/128 pass (was 125; +3 new).