Repository navigation
Python: Is wrapping an executor's handler maps a safe way to run code around every handler call #10043
Replies: 2 comments
|
Not a maintainer, but I read through
On stability: |
|
Two additional Python-level edge cases to consider alongside the private-API caveat above:
These are observations about the simplified wrapper, not a claim that the private maps are a supported extension point. I have not run your workflow or tested checkpoint/restore behavior. |
Uh oh!
There was an error while loading. Please reload this page.
Category: Q&A
Title: Python: Is wrapping an executor's handler maps a safe way to run code around every handler call?
What we're trying to do
We run workflows built from our own executors plus
AgentExecutorsubclasses. Each time an executor handles a message, we want to run our own code around that handler call, so that we can:request_info) in our own store;ContextVar, so that the agent's chat and function middleware can link each model call and tool call to the executor execution that made it;executor.processspan.There are agent, chat and function middleware, but we couldn't find a hook at the executor level, so we'd like to check that our approach is sound.
What we plan to do
In a base class shared by our executors, once
super().__init__()has discovered the handlers, wrap every value of_handlersand_response_handlers. Simplified:Why we chose this, from reading agent-framework-core 1.24.0:
Executor.__init__stores the discovered handlers inself._handlers(_workflows/_executor.py~L221) andself._response_handlers(_request_info_mixin.py~L81).Executor.execute()takes the execution lock, opens theexecutor.processspan, looks the handler up in those maps (_find_handler/_find_response_handler, which binds the original request withfunctools.partial), and callsawait handler(message, context)(_executor.py~L276–330).AgentExecutor.handle_user_input_response, and routing is unchanged because the dict keys stay the same.self.run(...)) doesn't go through the maps, so it isn't counted twice.Other options we looked at
executor_invoked/executor_completed/executor_failed): they're read in the task that consumes the run stream, not the one running the handler. AContextVarset there isn't visible to the agent's chat or function middleware, and the event may arrive after the model calls it should be linked to.AgentMiddleware: only covers agent executors, not our other executors. It also misses failures outsideagent.runand response-handler calls that don't reach the agent.SpanProcessoronexecutor.process: only works while instrumentation is on and nothing is sampled out. Its callbacks are synchronous, so we can't await our store there.Executor.execute(): its docstring says not to. It would also run before the lock and the span.Questions
executor_completed, edge or fan-in delivery, or anything else inexecute()?All reactions