Python: fix(python): add release_session API to prevent BackgroundAgentsProvider memory leaks - #7450
Conversation
There was a problem hiding this comment.
Pull request overview
Adds a host-facing lifecycle API to the Python harness BackgroundAgentsProvider so long-lived processes can explicitly evict per-session non-serializable runtime state (in-flight asyncio.Tasks and child AgentSessions), addressing unbounded retention over the process lifetime.
Changes:
- Introduces
async BackgroundAgentsProvider.release_session(session_id, *, cancel_running=True)to cancel/await in-flight tasks and drop per-session runtime state. - Adds unit tests covering release behavior (cancellation path, guard when
cancel_running=False, idempotency, and session isolation).
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| python/packages/core/agent_framework/_harness/_background_agents.py | Adds release_session() implementation to evict per-session runtime and cancel/await tasks. |
| python/packages/core/tests/core/test_harness_background_agents.py | Adds new async unit tests for the new session-release lifecycle API. |
|
Evan Mattson (@moonbox3) Eduard van Valkenburg (@eavanvalkenburg) Does this PR fulfill all the requirements of the issue, or does it need any further modifications? |
|
Thanks for the PR pratik wayase (@PratikWayase), added two small comments, and there are some type checking failures to fix. See the associated build. |
Python Test Coverage Report •
Python Unit Test Overview
|
||||||||||||||||||||||||||||||
|
westey (@westey-m) I've addressed both of your comments. All tests and linting are passing locally now. Ready for another look when you have a moment! |
Motivation & Context
Currently,
BackgroundAgentsProviderkeeps non-serializable per-session runtime (tasks, child sessions) inself._runtime, keyed bysession_id. Entries are inserted on first use and never removed.In a long-lived host serving many sessions concurrently (e.g., a shared-agent web server), this leaks one
_RuntimeStateper session that ever started a background task, for the entire process lifetime. Furthermore, naive eviction by the host is unsafe: tasks still running when a session is abandoned are never cancelled, resulting in orphaned work that keeps executing (and calling tools) with results nobody will read, or causing "Task was destroyed but it is pending" errors if garbage collected mid-flight.Description & Review Guide
What are the major changes?
async BackgroundAgentsProvider.release_session(session_id, *, cancel_running=True).awaits all in-flightasyncio.Taskobjects before dropping the_RuntimeStateentry.RuntimeErrorifcancel_running=Falseand unfinished tasks still exist, preventing the accidental orphaning of background work.cancel_running=Falseguard.What is the impact of these changes?
What do you want reviewers to focus on?
background_sessionsentry when finalized" as an "at minimum" fallback option. I intentionally did not implement this fallback because it conflicts with thebackground_agents_continue_tasktool. If we automatically drop the childAgentSessionfromruntime.background_sessionsthe moment a task finishes (inside_finalize_task), the LLM can no longer callcontinue_taskon it, as the session reference is gone. Because the preferred Option 1 (Eviction API) is implemented here, the long-lived process memory leak is fully resolved via the host callingrelease_session()without breaking the task continuation feature.asyncio.waitcancellation semantics and thecancel_running=Falseguard logic.Related Issue
Fixes #7385
Contribution Checklist
breaking changelabel (or add "[BREAKING]" to the title prefix, before or after any language prefix) — a workflow keeps the label and title prefix in sync automatically.