fix(tools): await coroutine returned by sync wrapper in CrewStructuredTool.ainvoke - #7475
Conversation
…dTool.ainvoke An async @tool reaches ainvoke through Tool._run, a sync wrapper, so the executor returned the un-awaited coroutine and the tool body never ran. Await the result when it is awaitable, as invoke() already does.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Advanced Run ID: 📒 Files selected for processing (2)
Included review availability: Your plan provides up to 10 included reviews per hour; 9 remain after this review. 📝 WalkthroughWalkthrough
ChangesAwaitable result handling
Suggested reviewers: Priority: ⬇️ Low Merge Risk: ⚪ Minimal · up to The async tool execution issue is addressed and covered by a focused test; no concrete merge-blocking risk remains. 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Related issue
Fixes #7474
Summary
For an async
@tool,to_structured_tool()storesfunc=self._run, andTool._runis a sync method that returnsself.func(...). So inCrewStructuredTool.ainvoke(),inspect.iscoroutinefunction(self.func)isFalse, the call goes throughrun_in_executor, and the coroutine it returns was handed straight back. The tool body never ran, and on the async agent path the LLM saw<coroutine object ...>as the observation.ainvoke()now awaits the result when it is awaitable, the same wayinvoke(),BaseTool.run,Tool.runandTool._arunalready handle a returned coroutine. Theget_event_loop()call is left as is, since #5969 and #4832 cover that line.Verification
Added
test_ainvoke_awaits_coroutine_from_sync_wrapper: an async@toolconverted withto_structured_tool()returns its value fromainvoke(), runs its body once, and counts one usage. It fails onmainwithassert <coroutine object ...> == 'data:x'and passes with the fix.Full
lib/crewai/tests/: 5472 passed, 43 skipped. ruff and mypy clean on the changed files.