Skip to content

Fix OpenAI streaming tool calls being split into two entries - #10352

Open
dpage wants to merge 1 commit into
pgadmin-org:masterfrom
dpage:fix/issue-10348-openai-tool-call-item-id
Open

Fix OpenAI streaming tool calls being split into two entries#10352
dpage wants to merge 1 commit into
pgadmin-org:masterfrom
dpage:fix/issue-10348-openai-tool-call-item-id

Conversation

@dpage

@dpage dpage commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Summary

  • OpenAIClient._read_responses_stream() accumulated Responses API (/v1/responses) streamed function-call arguments in a tool_calls_data dict keyed by call_id. But per OpenAI's Responses API streaming event reference, response.function_call_arguments.delta events carry item_id, output_index, delta and sequence_number — there is no call_id on that event. data.get('call_id', '') always evaluated to '', so every delta missed the entry seeded by the preceding response.output_item.added event and accumulated into a second, empty-name entry instead — one tool call came back as two separate ToolCalls (name with no arguments, and arguments with no name).
  • Correlates delta events on item_id (matching item.id from response.output_item.added) instead, while still carrying the real call_id through so the emitted ToolCall.id (used later to reference the call, e.g. 'call_id': tc.id when replaying tool calls back to the API) is correct.

Test plan

  • Added two scenarios to web/pgadmin/llm/tests/test_openai_stream.py covering a single streamed tool call and two parallel tool calls, using fake Responses API SSE events.
  • Confirmed both new tests fail against the pre-fix call_id-keyed logic and pass with the fix.
  • pycodestyle clean on the changed/added files.

Closes #10348

Summary by CodeRabbit

  • Bug Fixes

    • Improved handling of streamed AI tool calls.
    • Preserved correct associations for parallel tool calls, including their IDs, names, and arguments.
  • Tests

    • Added coverage for streamed function-call handling and argument accumulation.

_read_responses_stream() keyed its tool_calls_data dict by call_id, but
response.function_call_arguments.delta events carry item_id, not
call_id, so the delta accumulation always missed the entry seeded by
the preceding response.output_item.added event and created a second,
empty-name entry instead. Correlate on item_id, keeping the real
call_id alongside it so the emitted ToolCall.id is still correct.

Closes pgadmin-org#10348
@coderabbitai

coderabbitai Bot commented Aug 26, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 6435cd1b-7b18-4521-b995-bd66db0c3929

📥 Commits

Reviewing files that changed from the base of the PR and between bc58657 and 0f7c2ae.

📒 Files selected for processing (2)
  • web/pgadmin/llm/providers/openai.py
  • web/pgadmin/llm/tests/test_openai_stream.py

Included review availability: Your plan provides up to 8 included reviews per hour; 6 remain after this review.


Walkthrough

The Responses API stream parser now correlates function-call argument deltas by item_id. Final tool calls retain their call_id, name, and parsed arguments. Tests cover single and parallel streamed function calls.

Changes

Responses tool-call correlation

Layer / File(s) Summary
Correlate and construct tool calls
web/pgadmin/llm/providers/openai.py
The parser keys accumulated tool-call data by item_id, stores call_id separately, and uses it when constructing final ToolCall objects.
Validate streamed tool calls
web/pgadmin/llm/tests/test_openai_stream.py
Tests verify argument accumulation, tool-call identifiers, names, parsed arguments, and separate handling of parallel function calls.

Estimated code review effort: 2 (Simple) | ~10 minutes

Merge Risk: ⚪ Minimal · up to 0f7c2

This localized fix corrects streamed tool-call assembly and adds regression coverage for single and parallel calls; no actionable merge-blocking risk remains beyond normal checks and review.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 3 functions across 2 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the primary change: preventing OpenAI streaming tool calls from splitting into separate entries.
Linked Issues check ✅ Passed The changes satisfy issue #10348 by correlating argument delta events with response.output_item.added events using item_id, preserving call_id for ToolCall.id, and adding coverage for single and paral…
Out of Scope Changes check ✅ Passed The code changes and tests are directly related to the linked issue and PR objectives. No unrelated changes are present.
Full details: Linked Issues check

Explanation

The changes satisfy issue #10348 by correlating argument delta events with response.output_item.added events using item_id, preserving call_id for ToolCall.id, and adding coverage for single and parallel tool calls.

  • Fix all pre-merge checks with AI
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@kundansable kundansable added this to the 9.18 milestone Aug 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

AI Assistant: OpenAI streaming tool calls split into two entries (function_call_arguments.delta correlated on the wrong field)

2 participants