Skip to content

fix(sdk): yield manual tool calls from getNewMessagesStream()#6

Merged
mattapperson merged 3 commits intomainfrom
fix-getNewMessagesStream-manual-tools
Apr 4, 2026
Merged

fix(sdk): yield manual tool calls from getNewMessagesStream()#6
mattapperson merged 3 commits intomainfrom
fix-getNewMessagesStream-manual-tools

Conversation

@mattapperson
Copy link
Copy Markdown
Collaborator

Summary

  • Fixes getNewMessagesStream() silently dropping function_call items for manual tools (no execute function) when mixed with auto-execute tools
  • The data was present in finalResponse but never yielded because the finalResponse block only yielded messages
  • Now yields function_call items from finalResponse before the message, matching the existing behavior of getItemsStream()

Re-creation of https://github.com/OpenRouterTeam/openrouter-web/pull/16470 on this repo after the SDK was broken out of the monorepo.

Changes

  • Added isManualToolCall() and yieldManualToolCalls() helper methods to ModelResult
  • getNewMessagesStream() now yields manual tool function_call items from finalResponse
  • Added unit test for duplicate detection in getItemsStream with manual tools
  • Added e2e tests for manual tool calls (mixed auto+manual, and manual-only scenarios)

Test plan

  • npm run build passes
  • All unit tests pass (npx vitest run tests/unit/)
  • E2E tests pass with live API (npx vitest run tests/e2e/call-model.test.ts)
  • Manual verification: use callModel with mixed auto-execute + manual tools, consume via getNewMessagesStream(), confirm manual tool function_call items are yielded

When callModel is used with a mix of auto-execute and manual (no execute
function) tools, getNewMessagesStream() would silently drop function_call
items for manual tools. The data was present in finalResponse but never
yielded because the finalResponse block only yielded messages.

Now yields function_call items from finalResponse before the message,
matching the behavior of getItemsStream() which already handles this
correctly via the broadcaster pattern.
Covers edge cases in getNewMessagesStream and getItemsStream:
- Duplicate suppression when manual tools appear in both rounds and finalResponse
- Multiple manual tools in a single response
- Unknown/hallucinated tool names not in the tools list
- Auto-execute tools not leaking through yieldManualToolCalls
- Null and empty finalResponse handling
- Non-function_call items in finalResponse ignored
- Empty arguments string
- in_progress status function_call items
- No tools configured with function_calls in response
- Ordering: manual tool calls yielded before final message
Track yielded callIds across execution rounds and finalResponse to prevent
the same manual tool function_call from being yielded twice when it appears
in both a round's response.output and in finalResponse.
@mattapperson mattapperson merged commit b598300 into main Apr 4, 2026
1 check 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.

3 participants