crewai - ✅(Solved) Fix [BUG] Async stream does not work with function calls [3 pull requests, 1 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
crewAIInc/crewAI#4442Fetched 2026-04-08 00:41:57
View on GitHub
Comments
1
Participants
2
Timeline
11
Reactions
0
Author
Participants
Timeline (top)
cross-referenced ×3referenced ×2closed ×1commented ×1

Hi,

I would like to stream my crew output asynchronously but it works pretty well when I do not register tool to my agent but when I register a tool to my agent it fails like. As far as I understand, tool call is not finishing.

[Tool: calculator] [Args: ]

[Tool: calculator] [Args: {"]

[Tool: calculator] [Args: {"expression]

[Tool: calculator] [Args: {"expression":"]

[Tool: calculator] [Args: {"expression":"25]

[Tool: calculator] [Args: {"expression":"25 *]

[Tool: calculator] [Args: {"expression":"25 * ]

[Tool: calculator] [Args: {"expression":"25 * 4]

[Tool: calculator] [Args: {"expression":"25 * 4 +]

[Tool: calculator] [Args: {"expression":"25 * 4 + ]

[Tool: calculator] [Args: {"expression":"25 * 4 + 10]

[Tool: calculator] [Args: {"expression":"25 * 4 + 10"}]

[Tool: calculator] [Args: ]

[Tool: calculator] [Args: {"]

[Tool: calculator] [Args: {"expression]

[Tool: calculator] [Args: {"expression":"]

[Tool: calculator] [Args: {"expression":"25]

[Tool: calculator] [Args: {"expression":"25 *]

[Tool: calculator] [Args: {"expression":"25 * ]

[Tool: calculator] [Args: {"expression":"25 * 4]

[Tool: calculator] [Args: {"expression":"25 * 4 +]

[Tool: calculator] [Args: {"expression":"25 * 4 + ]

[Tool: calculator] [Args: {"expression":"25 * 4 + 10]

[Tool: calculator] [Args: {"expression":"25 * 4 + 10"}] [CrewAIEventsBus] Warning: Event pairing mismatch. 'task_failed' closed 'agent_execution_started' (expected 'task_started') [CrewAIEventsBus] Warning: Event pairing mismatch. 'crew_kickoff_failed' closed 'agent_execution_started' (expected 'crew_kickoff_started')

[Tool: calculator] [Args: ]

[Tool: calculator] [Args: {"]

[Tool: calculator] [Args: {"expression]

[Tool: calculator] [Args: {"expression":"]

[Tool: calculator] [Args: {"expression":"25]

[Tool: calculator] [Args: {"expression":"25 *]

[Tool: calculator] [Args: {"expression":"25 * ]

[Tool: calculator] [Args: {"expression":"25 * 4]

[Tool: calculator] [Args: {"expression":"25 * 4 +]

[Tool: calculator] [Args: {"expression":"25 * 4 + ]

[Tool: calculator] [Args: {"expression":"25 * 4 + 10]

[Tool: calculator] [Args: {"expression":"25 * 4 + 10"}]

and finally I get this error:

File "/home/x/x/x/x/.venv/lib/python3.10/site-packages/crewai/agents/crew_agent_executor.py", line 1212, in _ainvoke_loop_native_tools answer = await aget_llm_response( File "/home/x/x/x/x/.venv/lib/python3.10/site-packages/crewai/utilities/agent_utils.py", line 443, in aget_llm_response raise ValueError("Invalid response from LLM call - None or empty.") ValueError: Invalid response from LLM call - None or empty.

Error Message

import asyncio from crewai import Agent, Crew, Task from crewai.tools import tool from crewai.types.streaming import StreamChunkType

@tool("Calculator") def calculator(expression: str) -> str: """Evaluate a mathematical expression and return the result.""" try: result = eval(expression) return f"The result of {expression} is {result}" except Exception as e: return f"Error: {e}"

async def stream_crew(): # Create the agent researcher = Agent( role="Math Assistant", goal="Help users with calculations", backstory="You are an expert mathematician.", tools=[calculator] # Assign the custom tool )

expression_to_calculate = "25 * 4 + 10"


task = Task(
    description=f"Calculate {expression_to_calculate} using the Calculator tool and explain the result",
    agent=researcher,
    expected_output="The calculation result with explanation."
)


crew = Crew(
    agents=[researcher],
    tasks=[task],
    stream=True
)


streaming = await crew.akickoff(inputs={})

async for chunk in streaming:
    if chunk.chunk_type == StreamChunkType.TEXT:
        print(chunk.content, end="", flush=True)
    elif chunk.chunk_type == StreamChunkType.TOOL_CALL and chunk.tool_call:
        print(f"\n[Tool: {chunk.tool_call.tool_name}]")
        print(f"[Args: {chunk.tool_call.arguments}]")

print(f"\n\nFinal: {streaming.result.raw}")

asyncio.run(stream_crew())

Root Cause

Hi,

I would like to stream my crew output asynchronously but it works pretty well when I do not register tool to my agent but when I register a tool to my agent it fails like. As far as I understand, tool call is not finishing.

[Tool: calculator] [Args: ]

[Tool: calculator] [Args: {"]

[Tool: calculator] [Args: {"expression]

[Tool: calculator] [Args: {"expression":"]

[Tool: calculator] [Args: {"expression":"25]

[Tool: calculator] [Args: {"expression":"25 *]

[Tool: calculator] [Args: {"expression":"25 * ]

[Tool: calculator] [Args: {"expression":"25 * 4]

[Tool: calculator] [Args: {"expression":"25 * 4 +]

[Tool: calculator] [Args: {"expression":"25 * 4 + ]

[Tool: calculator] [Args: {"expression":"25 * 4 + 10]

[Tool: calculator] [Args: {"expression":"25 * 4 + 10"}]

[Tool: calculator] [Args: ]

[Tool: calculator] [Args: {"]

[Tool: calculator] [Args: {"expression]

[Tool: calculator] [Args: {"expression":"]

[Tool: calculator] [Args: {"expression":"25]

[Tool: calculator] [Args: {"expression":"25 *]

[Tool: calculator] [Args: {"expression":"25 * ]

[Tool: calculator] [Args: {"expression":"25 * 4]

[Tool: calculator] [Args: {"expression":"25 * 4 +]

[Tool: calculator] [Args: {"expression":"25 * 4 + ]

[Tool: calculator] [Args: {"expression":"25 * 4 + 10]

[Tool: calculator] [Args: {"expression":"25 * 4 + 10"}] [CrewAIEventsBus] Warning: Event pairing mismatch. 'task_failed' closed 'agent_execution_started' (expected 'task_started') [CrewAIEventsBus] Warning: Event pairing mismatch. 'crew_kickoff_failed' closed 'agent_execution_started' (expected 'crew_kickoff_started')

[Tool: calculator] [Args: ]

[Tool: calculator] [Args: {"]

[Tool: calculator] [Args: {"expression]

[Tool: calculator] [Args: {"expression":"]

[Tool: calculator] [Args: {"expression":"25]

[Tool: calculator] [Args: {"expression":"25 *]

[Tool: calculator] [Args: {"expression":"25 * ]

[Tool: calculator] [Args: {"expression":"25 * 4]

[Tool: calculator] [Args: {"expression":"25 * 4 +]

[Tool: calculator] [Args: {"expression":"25 * 4 + ]

[Tool: calculator] [Args: {"expression":"25 * 4 + 10]

[Tool: calculator] [Args: {"expression":"25 * 4 + 10"}]

and finally I get this error:

File "/home/x/x/x/x/.venv/lib/python3.10/site-packages/crewai/agents/crew_agent_executor.py", line 1212, in _ainvoke_loop_native_tools answer = await aget_llm_response( File "/home/x/x/x/x/.venv/lib/python3.10/site-packages/crewai/utilities/agent_utils.py", line 443, in aget_llm_response raise ValueError("Invalid response from LLM call - None or empty.") ValueError: Invalid response from LLM call - None or empty.

Fix Action

Fixed

PR fix notes

PR #4443: fix: handle streaming tool calls when available_functions is None (fixes #4442)

Description (problem / solution / changelog)

fix: handle streaming tool calls when available_functions is None (#4442)

Summary

When using crew.akickoff() with stream=True and agents that have tools, the streaming path in the OpenAI provider fails with ValueError: Invalid response from LLM call - None or empty. This happens because 4 streaming methods accumulate tool call chunks but only handle the case where available_functions is provided (execute tools inline). When available_functions is None (the executor handles tool execution), the methods fall through and return empty/None.

This PR adds an early-return path in each of the 4 affected streaming methods to format and return accumulated tool calls when available_functions is None, matching the behavior already present in:

  • The non-streaming counterparts of the same methods
  • The Azure provider's streaming implementation

Methods fixed in lib/crewai/src/crewai/llms/providers/openai/completion.py:

  • _handle_streaming_completion (sync Chat Completions)
  • _ahandle_streaming_completion (async Chat Completions)
  • _handle_streaming_responses (sync Responses API)
  • _ahandle_streaming_responses (async Responses API)

9 unit tests added covering single/multiple tool calls, sync/async, both API styles, and a regression check that available_functions being provided still executes tools as before.

Review & Testing Checklist for Human

  • Verify the returned tool call format matches what CrewAgentExecutor._handle_native_tool_calls() expects. The Chat Completions path returns {"id", "type", "function": {"name", "arguments"}} dicts, while the Responses API path returns {"id", "name", "arguments"} dicts — confirm both are consumed correctly downstream.
  • Check if other providers (Anthropic, Gemini, Bedrock) have the same streaming gap. Only OpenAI was patched here; Azure already had the fix. The same bug pattern may exist in other providers' streaming methods.
  • End-to-end test: Run the reproduction code from issue #4442 — specifically crew.akickoff(inputs={}) with stream=True and tool-equipped agents — to confirm the fix resolves the actual user-reported scenario.

Notes

Changed files

  • lib/crewai/src/crewai/llms/providers/openai/completion.py (modified, +62/-0)
  • lib/crewai/tests/llms/openai/test_openai_streaming_tool_calls_no_available_functions.py (added, +449/-0)

PR #4444: Fix async/sync streaming to return tool calls when available_functions is None

Description (problem / solution / changelog)

Summary

Fixes #4442 - Async stream does not work with function calls

When streaming is enabled (crew.stream=True) and the agent has tools registered, the executor receives ValueError: Invalid response from LLM call - None or empty because streaming methods return an empty string instead of the accumulated tool calls.

Root Cause

The CrewAgentExecutor calls the LLM with available_functions=None when it wants to handle tool execution externally (via _handle_native_tool_calls).

Non-streaming methods correctly handle this case:

# In _ahandle_completion (lines 1918-1932)
if message.tool_calls and not available_functions:
    return list(message.tool_calls)  # ✅ Returns tool calls for external handling

Streaming methods only checked if tool_calls and available_functions: and would return an empty full_response string when tool calls existed but available_functions was None:

# Before fix - streaming methods
if tool_calls and available_functions:  # ❌ Misses case where available_functions=None
    # execute tools internally
# ...
return full_response  # Returns "" when tool calls exist but weren't handled

Solution

  1. Added import for ChatCompletionMessageToolCall and Function types

  2. Added _build_tool_call_list() helper to convert accumulated streaming tool call data into proper ChatCompletionMessageToolCall objects

  3. Added the check for tool_calls and not available_functions to both sync (_handle_streaming_completion) and async (_ahandle_streaming_completion) methods:

if tool_calls and not available_functions:
    tool_call_list = self._build_tool_call_list(tool_calls)
    if tool_call_list:
        return tool_call_list
  1. Added regression tests in test_streaming_tool_calls_without_functions.py

Checklist

  • Code follows existing patterns in the codebase
  • Added regression tests
  • Syntax verified with py_compile
  • Consistent with how non-streaming methods handle this case

Changed files

  • lib/crewai/src/crewai/llms/providers/openai/completion.py (modified, +71/-0)
  • lib/crewai/tests/llms/test_streaming_tool_calls_without_functions.py (added, +161/-0)

PR #4456: fix: ensure openai tool call stream is finalized

Description (problem / solution / changelog)

fixes #4442

Changed files

  • lib/crewai/src/crewai/llms/providers/openai/completion.py (modified, +112/-88)
  • lib/crewai/tests/llms/openai/test_openai.py (modified, +165/-0)

Code Example

import asyncio
from crewai import Agent, Crew, Task
from crewai.tools import tool
from crewai.types.streaming import StreamChunkType

@tool("Calculator")
def calculator(expression: str) -> str:
    """Evaluate a mathematical expression and return the result."""
    try:
        result = eval(expression)
        return f"The result of {expression} is {result}"
    except Exception as e:
        return f"Error: {e}"

async def stream_crew():
    # Create the agent
    researcher = Agent(
        role="Math Assistant",
        goal="Help users with calculations",
        backstory="You are an expert mathematician.",
        tools=[calculator]  # Assign the custom tool
    )


    expression_to_calculate = "25 * 4 + 10"


    task = Task(
        description=f"Calculate {expression_to_calculate} using the Calculator tool and explain the result",
        agent=researcher,
        expected_output="The calculation result with explanation."
    )


    crew = Crew(
        agents=[researcher],
        tasks=[task],
        stream=True
    )


    streaming = await crew.akickoff(inputs={})

    async for chunk in streaming:
        if chunk.chunk_type == StreamChunkType.TEXT:
            print(chunk.content, end="", flush=True)
        elif chunk.chunk_type == StreamChunkType.TOOL_CALL and chunk.tool_call:
            print(f"\n[Tool: {chunk.tool_call.tool_name}]")
            print(f"[Args: {chunk.tool_call.arguments}]")

    print(f"\n\nFinal: {streaming.result.raw}")


asyncio.run(stream_crew())

---

`import asyncio
from crewai import Agent, Crew, Task
from crewai.tools import tool
from crewai.types.streaming import StreamChunkType


@tool("Calculator")
def calculator(expression: str) -> str:
    """Evaluate a mathematical expression and return the result."""
    try:
        result = eval(expression)
        return f"The result of {expression} is {result}"
    except Exception as e:
        return f"Error: {e}"


async def stream_crew():
    # Create the agent
    researcher = Agent(
        role="Math Assistant",
        goal="Help users with calculations",
        backstory="You are an expert mathematician.",
        # tools=[calculator]  # Assign the custom tool
    )

    # Expression to calculate
    expression_to_calculate = "25 * 4 + 10"

    # Create the task, passing the expression directly
    task = Task(
        description=f"Calculate {expression_to_calculate} using the Calculator tool and explain the result",
        agent=researcher,
        expected_output="The calculation result with explanation."
    )

    # Create the crew
    crew = Crew(
        agents=[researcher],
        tasks=[task],
        stream=True
    )

    # Start streaming the task
    streaming = await crew.akickoff(inputs={})

    async for chunk in streaming:
        if chunk.chunk_type == StreamChunkType.TEXT:
            print(chunk.content, end="", flush=True)
        elif chunk.chunk_type == StreamChunkType.TOOL_CALL and chunk.tool_call:
            print(f"\n[Tool: {chunk.tool_call.tool_name}]")
            print(f"[Args: {chunk.tool_call.arguments}]")

    print(f"\n\nFinal: {streaming.result.raw}")

# Run the async function
asyncio.run(stream_crew())
`
RAW_BUFFERClick to expand / collapse

Description

Hi,

I would like to stream my crew output asynchronously but it works pretty well when I do not register tool to my agent but when I register a tool to my agent it fails like. As far as I understand, tool call is not finishing.

[Tool: calculator] [Args: ]

[Tool: calculator] [Args: {"]

[Tool: calculator] [Args: {"expression]

[Tool: calculator] [Args: {"expression":"]

[Tool: calculator] [Args: {"expression":"25]

[Tool: calculator] [Args: {"expression":"25 *]

[Tool: calculator] [Args: {"expression":"25 * ]

[Tool: calculator] [Args: {"expression":"25 * 4]

[Tool: calculator] [Args: {"expression":"25 * 4 +]

[Tool: calculator] [Args: {"expression":"25 * 4 + ]

[Tool: calculator] [Args: {"expression":"25 * 4 + 10]

[Tool: calculator] [Args: {"expression":"25 * 4 + 10"}]

[Tool: calculator] [Args: ]

[Tool: calculator] [Args: {"]

[Tool: calculator] [Args: {"expression]

[Tool: calculator] [Args: {"expression":"]

[Tool: calculator] [Args: {"expression":"25]

[Tool: calculator] [Args: {"expression":"25 *]

[Tool: calculator] [Args: {"expression":"25 * ]

[Tool: calculator] [Args: {"expression":"25 * 4]

[Tool: calculator] [Args: {"expression":"25 * 4 +]

[Tool: calculator] [Args: {"expression":"25 * 4 + ]

[Tool: calculator] [Args: {"expression":"25 * 4 + 10]

[Tool: calculator] [Args: {"expression":"25 * 4 + 10"}] [CrewAIEventsBus] Warning: Event pairing mismatch. 'task_failed' closed 'agent_execution_started' (expected 'task_started') [CrewAIEventsBus] Warning: Event pairing mismatch. 'crew_kickoff_failed' closed 'agent_execution_started' (expected 'crew_kickoff_started')

[Tool: calculator] [Args: ]

[Tool: calculator] [Args: {"]

[Tool: calculator] [Args: {"expression]

[Tool: calculator] [Args: {"expression":"]

[Tool: calculator] [Args: {"expression":"25]

[Tool: calculator] [Args: {"expression":"25 *]

[Tool: calculator] [Args: {"expression":"25 * ]

[Tool: calculator] [Args: {"expression":"25 * 4]

[Tool: calculator] [Args: {"expression":"25 * 4 +]

[Tool: calculator] [Args: {"expression":"25 * 4 + ]

[Tool: calculator] [Args: {"expression":"25 * 4 + 10]

[Tool: calculator] [Args: {"expression":"25 * 4 + 10"}]

and finally I get this error:

File "/home/x/x/x/x/.venv/lib/python3.10/site-packages/crewai/agents/crew_agent_executor.py", line 1212, in _ainvoke_loop_native_tools answer = await aget_llm_response( File "/home/x/x/x/x/.venv/lib/python3.10/site-packages/crewai/utilities/agent_utils.py", line 443, in aget_llm_response raise ValueError("Invalid response from LLM call - None or empty.") ValueError: Invalid response from LLM call - None or empty.

Steps to Reproduce

Run this code:

import asyncio
from crewai import Agent, Crew, Task
from crewai.tools import tool
from crewai.types.streaming import StreamChunkType

@tool("Calculator")
def calculator(expression: str) -> str:
    """Evaluate a mathematical expression and return the result."""
    try:
        result = eval(expression)
        return f"The result of {expression} is {result}"
    except Exception as e:
        return f"Error: {e}"

async def stream_crew():
    # Create the agent
    researcher = Agent(
        role="Math Assistant",
        goal="Help users with calculations",
        backstory="You are an expert mathematician.",
        tools=[calculator]  # Assign the custom tool
    )


    expression_to_calculate = "25 * 4 + 10"


    task = Task(
        description=f"Calculate {expression_to_calculate} using the Calculator tool and explain the result",
        agent=researcher,
        expected_output="The calculation result with explanation."
    )


    crew = Crew(
        agents=[researcher],
        tasks=[task],
        stream=True
    )


    streaming = await crew.akickoff(inputs={})

    async for chunk in streaming:
        if chunk.chunk_type == StreamChunkType.TEXT:
            print(chunk.content, end="", flush=True)
        elif chunk.chunk_type == StreamChunkType.TOOL_CALL and chunk.tool_call:
            print(f"\n[Tool: {chunk.tool_call.tool_name}]")
            print(f"[Args: {chunk.tool_call.arguments}]")

    print(f"\n\nFinal: {streaming.result.raw}")


asyncio.run(stream_crew())

See it is failing and if you want you can remove the tool and try again. It will work.

Expected behavior

Expected behavior is streaming the name of the function and argument.

Screenshots/Code snippets

`import asyncio
from crewai import Agent, Crew, Task
from crewai.tools import tool
from crewai.types.streaming import StreamChunkType


@tool("Calculator")
def calculator(expression: str) -> str:
    """Evaluate a mathematical expression and return the result."""
    try:
        result = eval(expression)
        return f"The result of {expression} is {result}"
    except Exception as e:
        return f"Error: {e}"


async def stream_crew():
    # Create the agent
    researcher = Agent(
        role="Math Assistant",
        goal="Help users with calculations",
        backstory="You are an expert mathematician.",
        # tools=[calculator]  # Assign the custom tool
    )

    # Expression to calculate
    expression_to_calculate = "25 * 4 + 10"

    # Create the task, passing the expression directly
    task = Task(
        description=f"Calculate {expression_to_calculate} using the Calculator tool and explain the result",
        agent=researcher,
        expected_output="The calculation result with explanation."
    )

    # Create the crew
    crew = Crew(
        agents=[researcher],
        tasks=[task],
        stream=True
    )

    # Start streaming the task
    streaming = await crew.akickoff(inputs={})

    async for chunk in streaming:
        if chunk.chunk_type == StreamChunkType.TEXT:
            print(chunk.content, end="", flush=True)
        elif chunk.chunk_type == StreamChunkType.TOOL_CALL and chunk.tool_call:
            print(f"\n[Tool: {chunk.tool_call.tool_name}]")
            print(f"[Args: {chunk.tool_call.arguments}]")

    print(f"\n\nFinal: {streaming.result.raw}")

# Run the async function
asyncio.run(stream_crew())
`

Operating System

Ubuntu 20.04

Python Version

3.11

crewAI Version

1.9.3

crewAI Tools Version

1.9.3

Virtual Environment

Venv

Evidence

File "/home/x/x/x/x/.venv/lib/python3.10/site-packages/crewai/agents/crew_agent_executor.py", line 1212, in _ainvoke_loop_native_tools answer = await aget_llm_response( File "/home/x/x/x/x/.venv/lib/python3.10/site-packages/crewai/utilities/agent_utils.py", line 443, in aget_llm_response raise ValueError("Invalid response from LLM call - None or empty.") ValueError: Invalid response from LLM call - None or empty.

Possible Solution

Maybe there is a way to stop the tool call when it is done once ?

Additional context

It works when you comment out tools=[calculator]

extent analysis

Fix Plan

To resolve the issue, we need to ensure that the tool call is properly completed and a response is returned.

  1. Modify the calculator function: The current implementation of the calculator function does not handle the case where the expression is empty or None. We need to add a check to handle this case.
  2. Return a response from the calculator function: Even if the expression is empty or None, the function should return a response to indicate that the tool call is complete.

Here's an updated version of the calculator function:

@tool("Calculator")
def calculator(expression: str) -> str:
    """Evaluate a mathematical expression and return the result."""
    if not expression:
        return "No expression provided"
    try:
        result = eval(expression)
        return f"The result of {expression} is {result}"
    except Exception as e:
        return f"Error: {e}"
  1. Verify that the tool call is properly closed: After the tool call is complete, we need to ensure that it is properly closed to avoid any issues with event pairing.

Verification

To verify that the fix worked, you can run the stream_crew function again and check that the tool call is properly completed and a response is returned.

Extra Tips

  • Make sure to handle all possible cases in the calculator function to avoid any unexpected errors.
  • Consider adding logging to the calculator function to track any issues that may occur during execution.
  • If you're still experiencing issues, try debugging the calculator function to see where the problem is occurring.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

FAQ

Expected behavior

Expected behavior is streaming the name of the function and argument.

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

crewai - ✅(Solved) Fix [BUG] Async stream does not work with function calls [3 pull requests, 1 comments, 2 participants]