Repository navigation
fix(tools): route bound task and batch tools' sync path through the bound runtime - #5861
Merged
WillemJiang merged 1 commit intoSep 25, 2026
Conversation
…ound runtime bind_task_tool and _bind_batch_tool copied their process-wide singletons with only the coroutine rebound. Once get_available_tools had wrapped a singleton in place with a sync func, the copy inherited that wrapper around the unbound coroutine, so synchronous invocation bypassed the explicit SDK submitter/capacity and fell through to the process-global one -- or raised NotImplementedError in a fresh process where the singleton was never wrapped. Rebind the copy's func to a sync wrapper around the bound coroutine so both invocation paths use the runtime's own submitter and capacity.
willem-bd
reviewed
Sep 25, 2026
willem-bd
left a comment
Contributor
There was a problem hiding this comment.
Reviewed at 426a017 — no blocking findings; the fix looks correct and well-tested.
Verification performed:
- Reproduced the bug: with the two source changes reverted to the base revision, all three new regression tests fail (
KeyError: 'execution_capacity'on the task sync path, fallback-submitter result /func is Noneon the batch sync path); all pass with the fix applied, and the fulltest_batch_task_tool.py+test_task_tool_core_logic.py+test_batch_acceptance.pysuites pass (135 tests). - Checked the concern about LangGraph's injected-arg detection on the bound copies:
langgraph.prebuilt.tool_node._get_all_injected_argsreturns identical results for the singleton, the old bound copy, and the new bound copy (runtime+tool_call_idforbatch_task,runtimeforbatch_status/cancel_batch), so the new sync wrapper does not disturb ToolNode injection. - Searched for other
model_copysites that rebind onlycoroutine;bind_task_tooland_bind_batch_toolare the only ones with this pattern, and_clone_tool_with_descriptionclones keep the original coroutine so they are unaffected. - The monkeypatch snapshot idiom (
setattr(tool, "func", tool.func)before the in-place_ensure_sync_invocable_toolwrap) correctly restores the process-wide singletons after each test, andruff format --check/ruff checkare clean on all four changed files.
The change is minimal and matches the existing sync-wrapper machinery (make_sync_tool_wrapper) already used by _ensure_sync_invocable_tool.
WillemJiang
approved these changes
Sep 25, 2026
hawkli-1994
pushed a commit
to rongxinzy/deer-flow
that referenced
this pull request
Sep 27, 2026
…ound runtime (bytedance#5861) bind_task_tool and _bind_batch_tool copied their process-wide singletons with only the coroutine rebound. Once get_available_tools had wrapped a singleton in place with a sync func, the copy inherited that wrapper around the unbound coroutine, so synchronous invocation bypassed the explicit SDK submitter/capacity and fell through to the process-global one -- or raised NotImplementedError in a fresh process where the singleton was never wrapped. Rebind the copy's func to a sync wrapper around the bound coroutine so both invocation paths use the runtime's own submitter and capacity.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
get_available_toolswraps the process-wide tool singletons in place with a syncfunc(backend/packages/harness/deerflow/tools/tools.py:72, inside_ensure_sync_invocable_tool, reached from lines 172 and 305) so synchronous agent callers can invoke them.bind_task_tool(backend/packages/harness/deerflow/tools/builtins/task_tool.py:457onmain) and_bind_batch_tool(backend/packages/harness/deerflow/tools/builtins/batch_task_tool.py:84onmain) rebind only thecoroutineof their copies, so each copy inherits the singleton'sfunc— a sync wrapper around the unbound coroutine. A synchronous invocation of a bound copy therefore bypassed the explicit SDK submitter / execution capacity and fell through to the process-global one — the exact fallbackbind_batch_tools' docstring rules out ("never fall through to another application's process-global submitter") — or raisedNotImplementedErrorin a fresh process where the singleton had never been wrapped.What changed
bind_task_tooland_bind_batch_toolnow also rebind the copy'sfunctomake_sync_tool_wrapper(bound_coroutine, ...)— the same helper_ensure_sync_invocable_tooluses — so both invocation paths of a bound copy go through the bound coroutine and its runtime's own submitter/capacity.Surface area
backend/packages/harness/deerflow/tools/(no prompt changes, no new data sources in text)Bug fix verification
backend/tests/test_batch_task_tool.py::test_bound_batch_tools_sync_path_uses_the_explicit_submitterbackend/tests/test_batch_task_tool.py::test_bound_batch_tools_expose_a_sync_invocation_pathbackend/tests/test_task_tool_core_logic.py::test_bound_task_tool_sync_path_uses_the_explicit_capacitymainand green on this branch? yes — with the two source changes reverted (tests kept), all three fail: the batch sync path returns a fallback-submitter result instead of the explicit submitter's, bound copies havefunc is None, and the task sync path raisesKeyError: 'execution_capacity'. All three pass on this branch.Validation
All commands run in
backend/:make lint→All checks passed!/1633 files already formatted3 passedPYTHONPATH=. uv run pytest tests/test_batch_task_tool.py::test_bound_batch_tools_sync_path_uses_the_explicit_submitter tests/test_batch_task_tool.py::test_bound_batch_tools_expose_a_sync_invocation_path tests/test_task_tool_core_logic.py::test_bound_task_tool_sync_path_uses_the_explicit_capacity103 passedPYTHONPATH=. uv run pytest tests/test_batch_task_tool.py tests/test_task_tool_core_logic.py32 passed(tests/test_batch_acceptance.py),14 passed(tests/test_mcp_sync_wrapper.py tests/test_batch_extension_snapshot.py)PYTHONPATH=. uv run pytest -m "not live" --ignore=tests/blocking_io tests/ -q):19474 passed, 17 failedon this branch vs19471 passed, 20 failedwith the fix reverted — the 3 extra failures without the fix are the new regression tests; the 17 remaining failures are pre-existing onmainin this environment (browser automation / web-fetch / extension-install tests, untouched by this change)._get_all_injected_args) still resolvesruntimeandtool_call_idon the bound copies, since the new wrapper's__wrapped__chain points at the bound coroutine:runtimeandtool_call_idare detected forbatch_task/task,runtimeforbatch_status/cancel_batch, matching the singletons.AI assistance
Tool(s) used: Claude Code (autonomous coding agent, submitted from the BlueX888 account)
How you used it: The agent diagnosed the root cause, wrote the fix and the regression tests, and ran every validation command listed above; this PR text was also drafted by the agent.