Skip to content

fix(langgraph): offload sync remote tool coroutines - #39

Merged
spichen merged 1 commit into
spichen:oi-forkfrom
fashkl:fix/spichen-offload-sync-langgraph-tools
Aug 17, 2026
Merged

fix(langgraph): offload sync remote tool coroutines#39
spichen merged 1 commit into
spichen:oi-forkfrom
fashkl:fix/spichen-offload-sync-langgraph-tools

Conversation

@fashkl

@fashkl fashkl commented Aug 16, 2026

Copy link
Copy Markdown

Problem

This mirrors the upstream Oracle contribution in oracle#229 and tracks oracle#228.

LangGraph RemoteTool conversion exposes a coroutine for sync remote-tool callables, but the coroutine currently calls the synchronous function directly. For RemoteTool, that function executes httpx.request(...), so async LangGraph execution can block the event loop while waiting on network I/O.

This is worse than leaving StructuredTool.coroutine unset. LangChain's own StructuredTool._arun checks self.coroutine first; when it is None, it falls back to the base async implementation, whose source comment says it is expected to delegate _run to a separate thread. By supplying a fake async wrapper, pyagentspec bypasses that safe default and runs blocking sync I/O on the event-loop thread.

This also makes RemoteTool inconsistent with the existing ApiNode executor split in this codebase: ApiNodeExecutor._execute() uses sync httpx.request(...), while ApiNodeExecutor._aexecute() uses httpx.AsyncClient. RemoteTool is the outlier because its async entrypoint is only async-shaped, not async-safe.

In services with event-loop based liveness/readiness handling, a slow or stuck remote tool request can therefore freeze unrelated async work long enough to trip health checks and restart the service.

Fix

Offload synchronous structured-tool coroutine execution with asyncio.to_thread(...) while preserving native async callables unchanged.

This is a small general safety net for sync callables that pyagentspec intentionally exposes through a coroutine, including the current RemoteTool and ClientTool conversion paths. A future native async RemoteTool implementation using httpx.AsyncClient would still be compatible with this change and could further reduce thread usage for RemoteTool specifically.

Testing

  • Added test_remote_tool_coroutine_does_not_block_event_loop
  • Ran SKIP_LLM_TESTS=1 uv run python -m pytest -q tests/adapters/langgraph/test_tools.py -k 'remote_tool_coroutine_does_not_block_event_loop or async_server_tool_callable_converts_to_structured_tool_coroutine'
  • Ran SKIP_LLM_TESTS=1 uv run python -m pytest -q tests/adapters/langgraph/test_tools.py
  • Ran black and isort checks for the changed files

Commit is signed off per the Oracle contribution guide.

@fashkl
fashkl changed the base branch from main to oi-fork August 17, 2026 10:54
@fashkl
fashkl changed the base branch from oi-fork to main August 17, 2026 10:54
@fashkl
fashkl changed the base branch from main to oi-fork August 17, 2026 10:54
Signed-off-by: Mohamed Ali <mohamed.ali@openinnovation.ai>
@fashkl
fashkl force-pushed the fix/spichen-offload-sync-langgraph-tools branch from 6ec1a87 to f596356 Compare August 17, 2026 10:57
@spichen
spichen merged commit c83e113 into spichen:oi-fork Aug 17, 2026
@fashkl
fashkl deleted the fix/spichen-offload-sync-langgraph-tools branch August 17, 2026 12:54
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