Repository navigation
fix(tools): resolve awaitables returned by synchronous wrappers - #7506
BichengWang wants to merge 4 commits into
Conversation
Await results from synchronous FunctionTool callables after inline or thread-pool invocation. This executes wrapped async tools and evaluates wrapped confirmation predicates using their resolved decisions instead of coroutine objects.
|
@GWeale @xuanyang15 could you review this focused FunctionTool invocation fix? I selected you based on #6799: George reviewed that tool-dispatch change and authored the landed FunctionTool thread-pool preflight fix ( The failure is a synchronous wrapper returning an unresolved coroutine/Future. The fix awaits that result after inline or worker invocation, without changing async-function/generator dispatch or adding a configuration flag. Verification: 225 affected-module tests passed; real Runner smoke runs passed with the tool pool off/on, including serialized tool feedback, persisted state, and a wrapped async confirmation predicate resolving to False. Pre-commit checks passed. Full-suite execution was not performed. |
Keep the upstream invocation helper, which already awaits synchronous wrapper results, and retain the regression tests and documentation.
Keep the upstream decorated-function tests alongside coverage for returned Futures, awaitable failures, confirmation predicates and caller-loop ownership.
|
Merged current @GWeale could you review the remaining coverage and documentation, and approve the pending CI workflow? |
Link to Issue or Description of Change
Problem:
FunctionTool.run_async()only awaits a callable wheninspect.iscoroutinefunction()recognizes the callable (or its__call__). A synchronous wrapper or factory returning a coroutine/Future therefore returns the unresolved awaitable as the tool result. The async body never executes. The shared invocation helper also treats a synchronous wrapper around an async confirmation predicate as a truthy coroutine, so a resolvedFalseincorrectly requests confirmation.Expected: Return the resolved tool result, propagate errors from the awaitable, and use the resolved confirmation decision.
Solution: After invoking a synchronous callable, await its result when
inspect.isawaitable()is true. Do this after both inline and thread-pool invocation so the synchronous wrapper still runs in the worker while its asynchronous continuation runs on the caller's event loop. Existing async-function and generator dispatch is unchanged; no public signatures or configuration options change.Update the existing FunctionTool guide and add regressions for coroutine/Future results, asynchronous exceptions, confirmation decisions, and thread-pool event-loop ownership.
Minimal reproduction
On unmodified main (
bca4a425b), Python 3.14.7:Before: prints
coroutine; assertion fails and the coroutine is never awaited.After: prints
str; returnsresolved:hello.Testing Plan
Unit Tests:
.venv/bin/pytest \ tests/unittests/tools/test_function_tool.py \ tests/unittests/flows/llm_flows/tools/test_functions_thread_pool.py \ tests/unittests/flows/llm_flows/tools/test_functions_simple.py -q # 225 passedThe coroutine/Future result and exception-propagation tests failed on the unmodified implementation before applying the production fix. Existing synchronous, asynchronous, generator, live-mode and thread-pool coverage remains passing. The full repository suite was not run.
Manual End-to-End (E2E) Tests:
Ran a real
Runnerwith anAgent,InMemorySessionService, and a local scriptedBaseLlm(no network/model credentials). The model emits awrapped_tool(value="hello")function call, then a finaldoneresponse. The synchronous tool wrapper returns a coroutine that verifies its event loop, writestool_context.state["resolved"], and returns a dictionary. A synchronous confirmation wrapper returns a coroutine resolving toFalse.Exercised both
RunConfig()andRunConfig(tool_thread_pool_config=ToolThreadPoolConfig()). Checked the serializable FunctionResponse, feedback in the second model request, persisted session state, one tool invocation, and final agent response:Checklist