|
14 | 14 | import logging |
15 | 15 | import random |
16 | 16 | from dataclasses import dataclass, field |
17 | | -from typing import Any, Callable |
| 17 | +from typing import TYPE_CHECKING, Any, Callable |
18 | 18 |
|
19 | 19 | from openadapt_ml.training.grpo.config import GRPOConfig |
20 | 20 | from openadapt_ml.training.grpo.reward import binary_task_success |
| 21 | +from openadapt_ml.training.grpo.rollout_env import RolloutEnv |
21 | 22 |
|
22 | | -logger = logging.getLogger(__name__) |
| 23 | +if TYPE_CHECKING: # pragma: no cover - typing only |
| 24 | + # The concrete environment/adapter live in openadapt-evals and implement |
| 25 | + # the RolloutEnv Protocol defined in openadapt-ml. They are imported |
| 26 | + # lazily (inside __init__) at runtime to keep openadapt-ml a leaf with no |
| 27 | + # module-level openadapt-evals import. |
| 28 | + from openadapt_evals.adapters import WAALiveAdapter |
23 | 29 |
|
24 | | -# Deferred imports for openadapt-evals dependencies (optional at install time) |
25 | | -try: |
26 | | - from openadapt_evals.adapters import ( |
27 | | - RLEnvironment, |
28 | | - RolloutStep, |
29 | | - WAALiveAdapter, |
30 | | - WAALiveConfig, |
31 | | - ) |
32 | | - from openadapt_evals.adapters.rl_env import ResetConfig |
33 | | -except ImportError: |
34 | | - RLEnvironment = None # type: ignore[assignment, misc] |
35 | | - RolloutStep = None # type: ignore[assignment, misc] |
36 | | - WAALiveAdapter = None # type: ignore[assignment, misc] |
37 | | - WAALiveConfig = None # type: ignore[assignment, misc] |
38 | | - ResetConfig = None # type: ignore[assignment, misc] |
| 30 | +logger = logging.getLogger(__name__) |
39 | 31 |
|
40 | 32 |
|
41 | 33 | @dataclass |
@@ -81,25 +73,35 @@ def __init__( |
81 | 73 | config: GRPOConfig, |
82 | 74 | task_configs: dict[str, Any] | None = None, |
83 | 75 | ) -> None: |
84 | | - if RLEnvironment is None: |
| 76 | + # Lazy import: the concrete WAA adapter + RLEnvironment live in |
| 77 | + # openadapt-evals and implement the RolloutEnv Protocol. Importing |
| 78 | + # them here (not at module scope) keeps openadapt-ml a leaf. |
| 79 | + try: |
| 80 | + from openadapt_evals.adapters import ( |
| 81 | + RLEnvironment, |
| 82 | + WAALiveAdapter, |
| 83 | + WAALiveConfig, |
| 84 | + ) |
| 85 | + except ImportError as exc: |
85 | 86 | raise ImportError( |
86 | 87 | "openadapt-evals is required for rollout collection. " |
87 | 88 | "Install it with: uv add openadapt-evals" |
88 | | - ) |
| 89 | + ) from exc |
89 | 90 |
|
90 | 91 | self._config = config |
91 | 92 | self._task_configs = task_configs or {} |
92 | | - self._adapter = WAALiveAdapter( |
| 93 | + self._adapter: WAALiveAdapter = WAALiveAdapter( |
93 | 94 | WAALiveConfig( |
94 | 95 | server_url=config.server_url, |
95 | 96 | evaluate_url=config.evaluate_url, |
96 | 97 | ) |
97 | 98 | ) |
98 | | - self._env = RLEnvironment(self._adapter) |
| 99 | + # RLEnvironment structurally satisfies the RolloutEnv Protocol. |
| 100 | + self._env: RolloutEnv = RLEnvironment(self._adapter) |
99 | 101 |
|
100 | 102 | @property |
101 | | - def env(self) -> Any: |
102 | | - """The underlying RLEnvironment instance.""" |
| 103 | + def env(self) -> RolloutEnv: |
| 104 | + """The underlying environment (implements the RolloutEnv Protocol).""" |
103 | 105 | return self._env |
104 | 106 |
|
105 | 107 | def collect_group( |
|
0 commit comments