Description
Hi,
I found an issue related to synchronous tool execution inside the Microsoft Agent Framework.
Problem
Tools are currently executed directly inside the agent async event loop.
If a tool contains blocking synchronous code (for example time.sleep(120) or any long-running sync I/O), the entire event loop gets blocked.
In our case, this prevents Azure Bot from polling the Responses API (GET endpoint) for ResponsesAgentServerHost (azure.ai.agentserver.responses), because the polling mechanism is running inside the same event loop.
Current behavior
At the moment, tool execution appears to behave like this (around lines 682 and 733 of https://github.com/microsoft/agent-framework/blob/main/python/packages/core/agent_framework/_tools.py):
res = self.__call__(**call_kwargs)
result = await res if inspect.isawaitable(res) else res
The issue is that the synchronous function is already executed before checking whether the result is awaitable.
This means that blocking synchronous code blocks the event loop immediately.
Reproduction example
import asyncio
import datetime
import time
from agent_framework import tool
def _get_current_date() -> str:
time.sleep(120)
return datetime.datetime.now().strftime("%A, %d %B %Y")
@tool(approval_mode="never_require")
async def get_current_date() -> str:
"""Returns today's date and day of the week."""
return await asyncio.to_thread(_get_current_date)
# Sync version - actually not working because blocks eventloop
# @tool(approval_mode="never_require")
# def get_current_date() -> str:
# time.sleep(120)
# return datetime.datetime.now().strftime("%A, %d %B %Y")
When this tool is executed by the agent, the entire async loop becomes blocked for 120 seconds.
As a consequence:
- Responses API polling stops
- Azure Bot cannot retrieve updates
- The agent appears frozen until the sync function returns
Verified workarounds
We verified that everything works correctly when:
1. The tool is implemented asynchronously
async def tool():
await asyncio.sleep(120)
2. The synchronous tool is explicitly executed in a thread
await asyncio.to_thread(blocking_tool)
Proposed fix
Instead of executing the function first and checking afterward whether the result is awaitable, the framework should distinguish between sync and async functions before invocation.
Suggested implementation:
Instead of:
res = self.__call__(**call_kwargs)
result = await res if inspect.isawaitable(res) else res
Use:
if inspect.iscoroutinefunction(self.__call__):
result = await self.__call__(**call_kwargs)
else:
result = await asyncio.to_thread(self.__call__, **call_kwargs)
This would prevent blocking synchronous functions from freezing the event loop while preserving compatibility with async tools.
Expected behavior
- Async tools should continue running normally in the event loop
- Sync tools should automatically be executed in a worker thread
- Responses API polling should remain responsive even during long-running synchronous tool execution
Thanks!
Code Sample
Error Messages / Stack Traces
Package Versions
agent-framework: 1.3.0
Python Version
No response
Additional Context
No response
Description
Hi,
I found an issue related to synchronous tool execution inside the Microsoft Agent Framework.
Problem
Tools are currently executed directly inside the agent async event loop.
If a tool contains blocking synchronous code (for example
time.sleep(120)or any long-running sync I/O), the entire event loop gets blocked.In our case, this prevents Azure Bot from polling the Responses API (
GETendpoint) for ResponsesAgentServerHost (azure.ai.agentserver.responses), because the polling mechanism is running inside the same event loop.Current behavior
At the moment, tool execution appears to behave like this (around lines 682 and 733 of https://github.com/microsoft/agent-framework/blob/main/python/packages/core/agent_framework/_tools.py):
The issue is that the synchronous function is already executed before checking whether the result is awaitable.
This means that blocking synchronous code blocks the event loop immediately.
Reproduction example
When this tool is executed by the agent, the entire async loop becomes blocked for 120 seconds.
As a consequence:
Verified workarounds
We verified that everything works correctly when:
1. The tool is implemented asynchronously
2. The synchronous tool is explicitly executed in a thread
Proposed fix
Instead of executing the function first and checking afterward whether the result is awaitable, the framework should distinguish between sync and async functions before invocation.
Suggested implementation:
Instead of:
Use:
This would prevent blocking synchronous functions from freezing the event loop while preserving compatibility with async tools.
Expected behavior
Thanks!
Code Sample
Error Messages / Stack Traces
Package Versions
agent-framework: 1.3.0
Python Version
No response
Additional Context
No response