Make AgentPool worker callbacks spawn-safe and add pickle regression test#10
Conversation
|
Caution Review failedPull request was closed or merged during review 📝 WalkthroughWalkthroughExtracts worker lifecycle and session handling from AgentPool into module-level functions using a shared Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 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 |
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/openrtc/pool.py (1)
1-1:⚠️ Potential issue | 🟡 MinorFix formatting to pass the linting pipeline.
The CI pipeline indicates that
ruff format --checkfailed for this file. Run the formatter to resolve.#!/bin/bash # Run ruff format to see what changes are needed ruff format --diff src/openrtc/pool.py🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/openrtc/pool.py` at line 1, The file fails ruff formatting; run and apply ruff format to src/openrtc/pool.py (the current top-level "from __future__ import annotations" import line indicates formatting changes are needed) so the file conforms to ruff/PEP8 style; simply run `ruff format src/openrtc/pool.py` (or `ruff format --diff` to preview), accept the changes, and commit the updated formatted file.
🧹 Nitpick comments (2)
tests/test_pool.py (2)
166-174: Consider using themonkeypatchfixture for cleaner test setup.Creating
pytest.MonkeyPatch()instances manually works, but using the pytest fixture is more idiomatic and ensures automatic cleanup even if exceptions occur.♻️ Refactor to use monkeypatch fixture
Change the test signature to accept the fixture:
def test_worker_callbacks_are_pickleable_and_keep_registered_agents( monkeypatch: pytest.MonkeyPatch, ) -> None:Then replace manual
MonkeyPatch()instantiation andtry/finallyblocks with directmonkeypatch.setattr()calls—pytest handles cleanup automatically.Also applies to: 203-208
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/test_pool.py` around lines 166 - 174, Refactor the test to use pytest's monkeypatch fixture instead of creating pytest.MonkeyPatch() manually: update the test function signature (e.g., test_worker_callbacks_are_pickleable_and_keep_registered_agents) to accept monkeypatch: pytest.MonkeyPatch, then replace the manual instantiation and try/finally around monkeypatch.setattr("openrtc.pool._load_shared_runtime_dependencies", lambda: (FakeSilero, lambda: "turn")) with a direct monkeypatch.setattr(...) call so that pytest performs automatic cleanup; apply the same change for the other occurrences around lines referencing the same pattern (the block using FakeSilero and the _load_shared_runtime_dependencies replacement).
188-194: Class-levelinstanceslist may cause test pollution across test runs.
FakeSession.instancesis a class variable that persists across tests. If this test file is extended or run in certain pytest configurations, stale instances could affect assertions.♻️ Clear instances list before use
class FakeSession: instances: list[FakeSession] = [] def __init__(self, **kwargs: object) -> None: self.kwargs = kwargs self.started = False FakeSession.instances.append(self) + + FakeSession.instances.clear()Add the clear call after the class definition and before
ctx = FakeJobContext().🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/test_pool.py` around lines 188 - 194, The class-level list FakeSession.instances can leak state between tests; reset it before creating the test context by calling FakeSession.instances.clear() immediately after the FakeSession class definition and before instantiating ctx = FakeJobContext(), ensuring each test run starts with an empty instances list.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Outside diff comments:
In `@src/openrtc/pool.py`:
- Line 1: The file fails ruff formatting; run and apply ruff format to
src/openrtc/pool.py (the current top-level "from __future__ import annotations"
import line indicates formatting changes are needed) so the file conforms to
ruff/PEP8 style; simply run `ruff format src/openrtc/pool.py` (or `ruff format
--diff` to preview), accept the changes, and commit the updated formatted file.
---
Nitpick comments:
In `@tests/test_pool.py`:
- Around line 166-174: Refactor the test to use pytest's monkeypatch fixture
instead of creating pytest.MonkeyPatch() manually: update the test function
signature (e.g.,
test_worker_callbacks_are_pickleable_and_keep_registered_agents) to accept
monkeypatch: pytest.MonkeyPatch, then replace the manual instantiation and
try/finally around
monkeypatch.setattr("openrtc.pool._load_shared_runtime_dependencies", lambda:
(FakeSilero, lambda: "turn")) with a direct monkeypatch.setattr(...) call so
that pytest performs automatic cleanup; apply the same change for the other
occurrences around lines referencing the same pattern (the block using
FakeSilero and the _load_shared_runtime_dependencies replacement).
- Around line 188-194: The class-level list FakeSession.instances can leak state
between tests; reset it before creating the test context by calling
FakeSession.instances.clear() immediately after the FakeSession class definition
and before instantiating ctx = FakeJobContext(), ensuring each test run starts
with an empty instances list.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 953a812e-6950-480c-bd35-2ccd63f65c79
📒 Files selected for processing (2)
src/openrtc/pool.pytests/test_pool.py
…test ### Motivation - The AgentPool previously registered a nested `universal_session` closure and used a bound `self._prewarm` method as worker callbacks, which captures `self` and is not pickleable under multiprocessing `spawn`, causing macOS dev-mode failures. - Worker bootstrap must be serialization-safe so workers can be spawned without accidentally pickling the entire `AgentPool` instance. - Add a regression test to prevent reintroducing this class of bug. ### Description - Introduce a small serializable state carrier `@dataclass _PoolRuntimeState` that holds the shared `agents` mapping. - Add module-level spawn-safe entrypoints `def _prewarm_worker(...)` and `async def _run_universal_session(...)` that operate on `_PoolRuntimeState` instead of closing over `AgentPool` instances. - Bind the entrypoints to the `livekit` hooks using `functools.partial` so `AgentServer.setup_fnc` and the RTC session handler reference only module-level callables plus the serializable runtime state. - Move routing and dependency-loading helpers to module-level helpers (`_resolve_agent_config`, `_load_shared_runtime_dependencies`, and associated metadata helpers) and make `AgentPool` delegate to them, preserving external behavior while removing instance-bound callbacks. - Remove the nested closure and the instance-bound `_prewarm` method; keep runtime behavior (prewarm, session creation, greeting generation) unchanged. - Add a regression test `test_worker_callbacks_are_pickleable_and_keep_registered_agents` in `tests/test_pool.py` that pickles/unpickles the registered callbacks, executes the setup callback to populate `proc.userdata`, and runs the session callback against a fake context with a fake `AgentSession` to verify behavior. ### Testing - Ran `pytest tests/test_pool.py tests/test_routing.py` and all tests passed (`25 passed`). - Ran `ruff check src/openrtc/pool.py tests/test_pool.py tests/test_routing.py` and linting passed (`All checks passed`).
Motivation
universal_sessionclosure and used a boundself._prewarmmethod as worker callbacks, which capturesselfand is not pickleable under multiprocessingspawn, causing macOS dev-mode failures.AgentPoolinstance.Description
@dataclass _PoolRuntimeStatethat holds the sharedagentsmapping.def _prewarm_worker(...)andasync def _run_universal_session(...)that operate on_PoolRuntimeStateinstead of closing overAgentPoolinstances.livekithooks usingfunctools.partialsoAgentServer.setup_fncand the RTC session handler reference only module-level callables plus the serializable runtime state._resolve_agent_config,_load_shared_runtime_dependencies, and associated metadata helpers) and makeAgentPooldelegate to them, preserving external behavior while removing instance-bound callbacks._prewarmmethod; keep runtime behavior (prewarm, session creation, greeting generation) unchanged.test_worker_callbacks_are_pickleable_and_keep_registered_agentsintests/test_pool.pythat pickles/unpickles the registered callbacks, executes the setup callback to populateproc.userdata, and runs the session callback against a fake context with a fakeAgentSessionto verify behavior.Testing
pytest tests/test_pool.py tests/test_routing.pyand all tests passed (25 passed).ruff check src/openrtc/pool.py tests/test_pool.py tests/test_routing.pyand linting passed (All checks passed).Codex Task
Summary by CodeRabbit
Refactor
Tests
Note: Internal improvements only; no changes to user-facing functionality or APIs.