Skip to content

fix(core): deduplicate tool_calls in content_blocks with OpenAI Responses API streaming#36986

Closed
Anmol Mishra (anmolxlight) wants to merge 1 commit intolangchain-ai:masterfrom
anmolxlight:fix/duplicate-tool-calls-content-blocks-responses-api
Closed

fix(core): deduplicate tool_calls in content_blocks with OpenAI Responses API streaming#36986
Anmol Mishra (anmolxlight) wants to merge 1 commit intolangchain-ai:masterfrom
anmolxlight:fix/duplicate-tool-calls-content-blocks-responses-api

Conversation

@anmolxlight
Copy link
Copy Markdown

Closes #36985


Problem

When using the OpenAI Responses API, streaming produces partial tool_call blocks in content (fc_ prefix, empty args) while tool_calls holds the finalized blocks (call_ prefix, full args). The previous ID-based dedup logic in AIMessage.content_blocks did not recognize these as the same calls, resulting in duplicate entries.

Root cause

The old logic at AIMessage.content_blocks compared tool_call IDs between content and self.tool_calls:

content_tool_call_ids = {block.get("id") for block in self.content ...}
for tool_call in self.tool_calls:
    if id_ not in content_tool_call_ids:
        blocks.append(tool_call_block)  # always added, IDs differ

Since fc_12345 != call_abc123, both the partial and finalized blocks ended up in content_blocks.

Fix

Match tool_call blocks by name and position instead of ID. When a tool_call block from content matches a tool_call from self.tool_calls at the same position (same name), replace the partial with the finalized version. Any remaining tool_calls not represented in content are appended.

This preserves the existing behavior for:

  • Tool_calls already present in content with matching IDs (no duplicate added)
  • Tool_calls only in self.tool_calls (appended as before)
  • Multiple tool_calls with the same name (matched by position)

Changes

  • libs/core/langchain_core/messages/ai.py -- rewritten dedup logic in AIMessage.content_blocks
  • libs/core/tests/unit_tests/messages/test_ai.py -- two new regression tests:
    • test_content_blocks_no_duplicate_tool_calls_openai_responses_streaming -- verifies the fc_/call_ dedup
    • test_content_blocks_no_duplicate_tool_calls_mixed_content -- verifies matching-ID dedup still works

All 200 existing message unit tests continue to pass.


This contribution was developed with the assistance of an AI coding agent.

…nses API streaming

When using the OpenAI Responses API, streaming produces partial tool_call
blocks in content (fc_ prefix, empty args) while tool_calls holds the
finalized blocks (call_ prefix, full args). The previous ID-based dedup
logic did not recognize these as the same calls, resulting in duplicate
entries in content_blocks.

Match tool_call blocks by name and position instead of ID, replacing
partial blocks with their finalized counterparts from tool_calls.

Closes langchain-ai#36985
@github-actions github-actions Bot added core `langchain-core` package issues & PRs fix For PRs that implement a fix size: S 50-199 LOC labels Apr 24, 2026
@github-actions
Copy link
Copy Markdown

This PR has been automatically closed because you are not assigned to the linked issue.

External contributors must be assigned to an issue before opening a PR for it. Please:

  1. Comment on the linked issue to request assignment from a maintainer
  2. Once assigned, your PR will be reopened automatically

Maintainers: reopen this PR or remove the missing-issue-link label to bypass this check.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

core `langchain-core` package issues & PRs external fix For PRs that implement a fix missing-issue-link new-contributor size: S 50-199 LOC

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Duplicate tool_calls in content_blocks with OpenAI Responses API streaming

1 participant