Skip to content

fix(ai): keep streamed OpenAI tool calls that have no index - #1091

Merged
marandaneto merged 4 commits into
PostHog:mainfrom
pcbeingused333:fix/openai-stream-tool-calls-without-index
Oct 23, 2026
Merged

marandaneto merged 4 commits into
PostHog:mainfrom
pcbeingused333:fix/openai-stream-tool-calls-without-index

Conversation

@pcbeingused333

Copy link
Copy Markdown
Contributor

💡 Motivation and Context

accumulate_openai_tool_calls skipped every tool call delta whose index is None. Some OpenAI-compatible providers send streamed tool calls without an index, so those calls never reached $ai_output for the Chat Completions streaming wrappers (sync and async share this function).

A delta without an index is now matched by its id when one is present and already known. Otherwise a delta with a new id or a function name opens a new call, and a delta with neither id nor function name appends its arguments to the latest call. Deltas with an index take the same path as before.

💚 How did you test it?

  • Four new tests in posthog/test/ai/openai/test_openai_converter.py: two index-less calls sent whole through _ChatCompletionsStreamState, argument fragments joined onto the latest call, two whole calls with a name and neither id nor index giving two calls, and a guard that indexed deltas behave as before. All but the guard fail on main (the calls are dropped).
  • posthog/test/ai: 1069 passed, 15 skipped. ruff check reports the same 54 findings as on main; ruff format is clean on the changed lines.
  • The chunks in the tests are synthetic; no real index-less provider stream was recorded and nothing ran against a live API.

📝 Checklist

  • I reviewed the submitted code.
  • I added tests to verify the changes.
  • I updated the docs if needed. (n/a)
  • No breaking change or entry added to the changelog.

If releasing new changes

  • Ran sampo add to generate a changeset file (added .sampo/changesets/openai-stream-tool-calls-without-index.md in the same format)

🤖 Agent context

Autonomy: Fully autonomous

  • Written with Claude Code (Claude Sonnet 5.5): the fix, the tests and this description.
  • When the index is missing, matching by id and then by function name is a heuristic for providers that send each call whole; a provider that interleaves several index-less calls with partial arguments and no ids cannot be told apart, and a continuation fragment that repeats the function name would open a new call.

🤖 Generated with Claude Code

@pcbeingused333
pcbeingused333 requested a review from a team as a code owner October 20, 2026 20:13

@marandaneto marandaneto left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated advisory code review.

Comment thread posthog/ai/openai/openai_converter.py Outdated
@marandaneto
marandaneto requested a review from a team October 21, 2026 14:29
@pcbeingused333

Copy link
Copy Markdown
Contributor Author

Thanks, good catch. Fixed in 8fed35a: calls without an index now get keys of their own (no-index-N) instead of max + 1, so they can no longer take an index the provider sends later, and an argument-only delta continues the most recently added call. Added your scenario as test_indexless_call_does_not_corrupt_later_indexed_call (it fails on the previous head with {}{} and passes now) plus a test that the calls keep their arrival order. a0f8122 widens the accumulator's key type in _streaming.py to match.

@marandaneto marandaneto left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated advisory code review.

Comment thread posthog/ai/openai/openai_converter.py
@marandaneto
marandaneto requested a review from a team October 22, 2026 10:12
pcbeingused333 and others added 3 commits October 23, 2026 11:00
accumulate_openai_tool_calls skipped every delta without an index, so
tool calls from OpenAI-compatible providers that omit it never reached
$ai_output. A delta with a new id now opens a call, and one without an
id continues the latest call.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
A call without an index took key max + 1, which a later call with that real index overwrote, merging two calls and dropping one. Index-less calls now get string keys of their own.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
@marandaneto
marandaneto force-pushed the fix/openai-stream-tool-calls-without-index branch from a0f8122 to 3a9320f Compare October 23, 2026 09:01
@marandaneto

Copy link
Copy Markdown
Member

thanks @pcbeingused333

@marandaneto
marandaneto merged commit ffde3c5 into PostHog:main Oct 23, 2026
67 of 69 checks passed
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.

2 participants