pgadmin-org / pgadmin-org/pgadmin4

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

Open Beginner friendly
#10,348 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Bug
Dominant language
Python
Stars
3.8k
Forks
891
Avg merge
4d 7h
Merged PRs (30d)
8

Description

Describe the bug

In web/pgadmin/llm/providers/openai.py, chat_stream() accumulates streamed function-call arguments in a tool_calls_data dict keyed by call_id:

elif event_type == 'response.output_item.added':
    item = data.get('item', {})
    if item.get('type') == 'function_call':
        call_id = item.get('call_id', '')
        tool_calls_data[call_id] = {'name': item.get('name', ''), 'arguments': ''}

elif event_type == 'response.function_call_arguments.delta':
    call_id = data.get('call_id', '')
    if call_id not in tool_calls_data:
        tool_calls_data[call_id] = {'name': '', 'arguments': ''}
    tool_calls_data[call_id]['arguments'] += data.get('delta', '')

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 field on that event type (source). So data.get('call_id', '') always evaluates to ''.

That means every response.function_call_arguments.delta event fails the call_id not in tool_calls_data check (since the real call_id entry was already created by response.output_item.added), creates a second dict entry keyed by the empty string, and accumulates all the argument deltas there instead. The result: a tool call comes back as two separate ToolCall entries — one with the correct name and empty arguments, and another with empty name and the actual (accumulated) arguments — rather than one complete tool call.

To Reproduce

  1. Configure the AI Assistant / SQL Chat feature with an OpenAI provider and a model exposed via the Responses API (/v1/responses).
  2. Ask a question that triggers a streamed tool/function call.
  3. Inspect the resulting tool calls: the name and arguments arrive in two separate entries instead of one.

Expected behavior

The delta events should be correlated using item_id (matching item.id from the response.output_item.added event, not item.call_id) so all argument deltas accumulate against the same entry that was seeded with the function name, and the final ToolCall is emitted with both name and arguments populated correctly.

Found while re-verifying #9795 (which is otherwise correctly fixed for the "unsupported model" fallback to /v1/responses) — this is a separate, follow-up defect in that same streaming path.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in web/pgadmin/llm/providers/openai.py, focusing on chat_stream() and its response.output_item.added and response.function_call_arguments.delta handlers. Compare the event fields described in the issue and trace how tool_calls_data becomes final ToolCall entries. Done means streamed function-call arguments remain correlated with the function name in one complete ToolCall.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
ai, api, backend
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
76/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.