Prewarm_fnc can't pre-warm async resources tied to the event loop (e.g. DB clients) — first job per process pays full connection cost

What I’m trying to do

I have a voice agent (agents 1.5.17, Python) that needs to read a small
amount of candidate context from MongoDB Atlas at the start of each call.
I want this DB connection to be warm before a job starts, the same way
prewarm_fnc lets me pre-load VAD/TTS models.

What’s happening instead

The first DB query inside the entrypoint takes 6-11 seconds, even though the
query itself returns in ~150ms once the connection is established. This
latency is fully visible to the end user as dead air at call start.

Is there a supported way to:

1. Run async setup (e.g. opening a DB connection pool) once per ‘worker process’, persisted across multiple jobs handled by the same `num_idle_processes` warm process — rather than once per job?

2. Or, is `prewarm_fnc` intentionally sync-only, and the expected pattern is to keep long-lived async resources alive at the module level across jobs within the same process (bypassing the “bind to current running loop” pattern some async DB drivers recommend)?

This is the best resource I am aware of for how to use MongoDB with agents:

@jitendra, prewarm runs before the process has an event loop: the proc client calls your prewarm_fnc in its initialize step, then creates the asyncio loop only afterward [ agents ipc/proc_client.py ]. That’s why an async client opened there binds to the wrong loop. So yes, it’s effectively sync for this.

What solves it: a warm process runs every job it handles on a single event loop created once per process [ same file, run() makes one new_event_loop() ], and ctx.proc.userdata is a process-lifetime dict [ agents job.py ]. So lazy-init the client on the first job and cache it there; later jobs in that process reuse it on the same loop, so you pay the 6-11s connect once per process, not per call, and you don’t break the driver’s bind-to-running-loop rule.

async def entrypoint(ctx: JobContext):
    if "mongo" not in ctx.proc.userdata:
        client = AsyncIOMotorClient(MONGO_URI)
        await client.admin.command("ping")     # pay the connect once, on the job loop
        ctx.proc.userdata["mongo"] = client
    db = ctx.proc.userdata["mongo"]["yourdb"]
    # ~150ms queries from here

Keep enough num_idle_processes warm that a ready process is usually waiting [ agents worker.py ]; the only call that still pays the connect is the first one each fresh process handles.