Event Loop Handling for Pipeline Tools #11858
-
|
Hi Team, I wanted to open up this as a discussion before submitting an issue since I'm not sure this is intentional. I've been looking into how the async and event loop handling is done and came across this issue, which seems a bit unintuitive to me, so I just wanted to clarify things. My setup is a simple However, I wanted to write to an external event queue from inside of my tool and I came across issues with async/threading. I hunted this down to the following.
So my question is. Is this an intentional design choice or is this an oversight? Because to me it doesn't seem entirely clear that wrapping an Thanks |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments
-
|
Your read is right, and it's not specific to your setup. Wrapping an
And the async agent doesn't save you either: Two ways to push to your async queue from inside the tool:
On "intentional or oversight" — it's a current limitation of the tool layer, not a bug in your async pipeline. Tools are sync by design right now, so an async component always gets driven through its sync |
Beta Was this translation helpful? Give feedback.
-
|
Great, thank you for your answer. Yes, in the end I did what you're suggesting - and which is also done in Hayhooks. This makes e.g the chat completion functions a bit clunky, but here's the workaround: async def run_chat_completion_async(self, model: str, messages: list[dict], body: dict) -> AsyncGenerator:
async def stream() -> AsyncGenerator:
queue = asyncio.Queue()
loop = asyncio.get_running_loop()
try:
# set event loops + pipeline logic
finally:
# reset loop and queue
return stream()Then create helper functions used in the tools which can write to that current context stream and loop. |
Beta Was this translation helpful? Give feedback.
Your read is right, and it's not specific to your setup. Wrapping an
AsyncPipelinein aPipelineToolalways runs it synchronously. Here's the chain, from the currentmain:Toolis sync by contract.Tool.__post_init__actually raises if you hand it a coroutine function, so there's no async tool body anywhere in the tool layer.ComponentToolbuilds that synccomponent_invoker, and it callscomponent.run(...)(component_tool.py) — neverrun_async. ThenSuperComponent.runcallsself.pipeline.run(...), the sync driver, even when the pipeline is anAsyncPipeline.SuperComponent.run_asyncexists but nothing in the tool path ever reaches it. That's the exact log line you saw.And the async agent …