Skip to content

Document streamed tool call arguments and runtime events#6438

Open
lorenzejay wants to merge 4 commits into
mainfrom
lorene/add-tool-call-streaming-docs
Open

Document streamed tool call arguments and runtime events#6438
lorenzejay wants to merge 4 commits into
mainfrom
lorene/add-tool-call-streaming-docs

Conversation

@lorenzejay

@lorenzejay lorenzejay commented Jul 2, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Clarify how streamed tool-call arguments surface across llm and tools channels
  • Explain the difference between argument construction and tool execution in the runtime contract
  • Update stream execution docs and add examples for accumulated arguments versus latest deltas

Testing

  • Not run (not requested)
  • Added unit tests covering OpenAI Responses streaming and console formatter behavior for tool-call chunks

Note

Medium Risk
Touches core LLM streaming and OpenAI Responses event handling; behavior changes for verbose console and stream consumers, though covered by new unit tests.

Overview
Adds streamed tool-call argument support for the OpenAI Responses API (sync and async): partial function_call_arguments deltas are accumulated and emitted as LLMStreamChunkEvent with call_type=TOOL_CALL, via a shared _emit_tool_call_stream_chunk_event helper on BaseLLM.

Event and verbose console behavior is adjusted so tool-call construction is not mixed with text streaming: the event listener forwards accumulated arguments for TOOL_CALL chunks without writing to the text buffer; the console formatter stops the live “final answer” panel for tool-call chunks and pauses live updates when tool usage panels appear.

Documentation explains the split between llm (llm_stream_chunk while the model builds the call: chunk vs tool_call.function.arguments) and tools (actual execution), with examples for frame streaming, crew TOOL_CALL chunks, and the runtime contract.

Reviewed by Cursor Bugbot for commit 0b8df05. Bugbot is set up for automated code reviews on this repo. Configure here.

@mintlify

mintlify Bot commented Jul 2, 2026

Copy link
Copy Markdown

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
crewai 🟢 Ready View Preview Jul 2, 2026, 6:44 PM

💡 Tip: Enable Workflows to automatically generate PRs for you.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit b6a4af5. Configure here.

"streamed_arguments": False,
},
)
call_data["id"] = call_data.get("id") or getattr(item, "call_id", None)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Wrong tool id in stream

Medium Severity

Responses streaming can emit tool_call.id from the output item id when argument deltas create state before output_item.added. Later merging keeps that id because _process_responses_function_call_started only sets id when missing, so frames and StreamChunk.tool_id may not match the provider call_id the docs and tests expect.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit b6a4af5. Configure here.

@coderabbitai

coderabbitai Bot commented Jul 2, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds streaming of incremental tool-call argument deltas during LLM tool calls in the OpenAI Responses API completion handler, propagated via a new BaseLLM helper and event listener branching, with console formatter changes to pause Live rendering for tool events, plus tests and documentation.

Changes

Tool call streaming feature

Layer / File(s) Summary
Tool-call stream chunk emission helper
lib/crewai/src/crewai/llms/base_llm.py
Adds _emit_tool_call_stream_chunk_event to emit normalized tool-call streaming chunks with call_type=TOOL_CALL.
OpenAI Responses API incremental function-call assembly
lib/crewai/src/crewai/llms/providers/openai/completion.py
Sync and async streaming handlers track streaming_function_calls per call; new helpers key, accumulate deltas, start, and finalize tool-call argument assembly, emitting stream chunk events.
Event listener and console formatter tool-call handling
lib/crewai/src/crewai/events/event_listener.py, lib/crewai/src/crewai/events/utils/console_formatter.py
on_llm_stream_chunk branches on LLMCallType.TOOL_CALL to forward tool-call arguments; ConsoleFormatter pauses Live updates for tool usage events and skips streaming-panel rendering for TOOL_CALL chunks, simplifying non-tool-call panel styling.
Tests for tool-call streaming and console pause behavior
lib/crewai/tests/llms/openai/test_openai.py, lib/crewai/tests/utilities/test_console_formatter_pause_resume.py
New tests assert incremental tool-call argument delta emission in sync/async OpenAI streaming and that Live panels are skipped/stopped for TOOL_CALL chunks and tool usage start.
Documentation of tool-call streaming semantics
docs/edge/en/concepts/streaming.mdx, docs/edge/en/learn/consuming-streams.mdx, docs/edge/en/learn/streaming-crew-execution.mdx, docs/edge/en/learn/streaming-runtime-contract.mdx
Documents tool-call streaming phases, channels, frame payload structure, and usage examples for reading argument deltas and accumulated arguments.

Sequence Diagram(s)

sequenceDiagram
  participant OpenAIAPI
  participant OpenAICompletion
  participant BaseLLM
  participant EventBus
  OpenAIAPI->>OpenAICompletion: response.output_item.added (function_call)
  OpenAICompletion->>OpenAICompletion: _process_responses_function_call_started
  OpenAICompletion->>BaseLLM: _emit_tool_call_stream_chunk_event
  BaseLLM->>EventBus: LLMStreamChunkEvent(call_type=TOOL_CALL)
  OpenAIAPI->>OpenAICompletion: response.function_call_arguments.delta
  OpenAICompletion->>OpenAICompletion: _process_responses_function_call_delta
  OpenAICompletion->>BaseLLM: _emit_tool_call_stream_chunk_event
  BaseLLM->>EventBus: LLMStreamChunkEvent(accumulated arguments)
  OpenAIAPI->>OpenAICompletion: response.output_item.done
  OpenAICompletion->>OpenAICompletion: _process_responses_function_call_done
  OpenAICompletion->>OpenAICompletion: merge into function_calls list
Loading

Suggested reviewers: lucasgomide, gabemilani

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the main change: documenting streamed tool-call arguments and runtime events.
Description check ✅ Passed The description directly matches the documented streaming behavior updates, runtime contract changes, and added tests.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch lorene/add-tool-call-streaming-docs

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
lib/crewai/tests/llms/openai/test_openai.py (1)

1893-1947: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider extracting shared stream_events fixture.

The sync and async tests construct nearly identical stream_events lists. A shared helper/fixture would reduce duplication if these tests evolve.

Also applies to: 1980-2034

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/crewai/tests/llms/openai/test_openai.py` around lines 1893 - 1947, The
sync and async OpenAI streaming tests are duplicating the same `stream_events`
setup, so extract that repeated event list into a shared fixture or helper used
by both test cases. Update the tests around the existing `stream_events`
construction in the OpenAI test module so the common response/function-call
event sequence is defined once and reused, keeping the sync and async assertions
unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@lib/crewai/tests/llms/openai/test_openai.py`:
- Around line 2036-2057: The async streaming test is mocking `responses.create`
with a synchronous lambda, but `_ahandle_streaming_responses` awaits
`self._get_async_client().responses.create(...)`, so the mock must be awaitable.
Update the test setup around `MockAsyncStream` and `fake_client` to use an async
factory or `AsyncMock` for `responses.create`, so the await succeeds before the
stream is iterated.

In `@scripts/stream_tool_call_runner.py`:
- Around line 1-6: The module docstring in stream_tool_call_runner references
“Fill in OPENAI_API_KEY below,” but the script actually reads the key from the
environment via load_dotenv() and os.getenv("OPENAI_API_KEY") in the main flow.
Update the docstring to instruct users to set OPENAI_API_KEY as an environment
variable or in a .env file instead, and keep the wording aligned with the
behavior in the runner entrypoint.

---

Nitpick comments:
In `@lib/crewai/tests/llms/openai/test_openai.py`:
- Around line 1893-1947: The sync and async OpenAI streaming tests are
duplicating the same `stream_events` setup, so extract that repeated event list
into a shared fixture or helper used by both test cases. Update the tests around
the existing `stream_events` construction in the OpenAI test module so the
common response/function-call event sequence is defined once and reused, keeping
the sync and async assertions unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: fba17fea-4e13-40f4-bd10-dd257439964d

📥 Commits

Reviewing files that changed from the base of the PR and between 24901cd and b6a4af5.

📒 Files selected for processing (11)
  • docs/edge/en/concepts/streaming.mdx
  • docs/edge/en/learn/consuming-streams.mdx
  • docs/edge/en/learn/streaming-crew-execution.mdx
  • docs/edge/en/learn/streaming-runtime-contract.mdx
  • lib/crewai/src/crewai/events/event_listener.py
  • lib/crewai/src/crewai/events/utils/console_formatter.py
  • lib/crewai/src/crewai/llms/base_llm.py
  • lib/crewai/src/crewai/llms/providers/openai/completion.py
  • lib/crewai/tests/llms/openai/test_openai.py
  • lib/crewai/tests/utilities/test_console_formatter_pause_resume.py
  • scripts/stream_tool_call_runner.py

Comment thread lib/crewai/tests/llms/openai/test_openai.py
Comment thread scripts/stream_tool_call_runner.py Outdated
Comment on lines +1 to +6
"""Real OpenAI Responses API runner for streamed tool-call deltas.

Fill in ``OPENAI_API_KEY`` below, then run from the repository root:

uv run python scripts/stream_tool_call_runner.py
"""

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Docstring instructs "Fill in OPENAI_API_KEY below" but the script never has a place to fill it in.

The script only reads the key via os.getenv("OPENAI_API_KEY") after load_dotenv() (Lines 17, 32) — there's no literal placeholder to edit. Update the docstring to say to set the OPENAI_API_KEY environment variable (or .env entry) instead of "fill in ... below".

📝 Proposed docstring fix
 """Real OpenAI Responses API runner for streamed tool-call deltas.

-Fill in ``OPENAI_API_KEY`` below, then run from the repository root:
+Set the ``OPENAI_API_KEY`` environment variable (e.g. in a ``.env`` file),
+then run from the repository root:

     uv run python scripts/stream_tool_call_runner.py
 """

Also applies to: 29-37

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/stream_tool_call_runner.py` around lines 1 - 6, The module docstring
in stream_tool_call_runner references “Fill in OPENAI_API_KEY below,” but the
script actually reads the key from the environment via load_dotenv() and
os.getenv("OPENAI_API_KEY") in the main flow. Update the docstring to instruct
users to set OPENAI_API_KEY as an environment variable or in a .env file
instead, and keep the wording aligned with the behavior in the runner
entrypoint.

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant