| """Pydantic action models for ForgeEnv (compatible with OpenEnv 0.2.x). | |
| Episodes have two phases — drift_gen (Challenger) and repair (Solver) — so | |
| we expose a single union ForgeAction that carries either a BreakageAction | |
| or a RepairAction. The environment dispatches on which sub-field is set. | |
| """ | |
| from __future__ import annotations | |
| from typing import Any, Literal, Optional | |
| from pydantic import Field | |
| from openenv.core import Action | |
| class BreakageAction(Action): | |
| """Drift Generator's action: pick a primitive type + parameters.""" | |
| action_type: Literal["breakage"] = "breakage" | |
| primitive_type: str = Field( | |
| ..., description="One of the registered breakage primitive class names" | |
| ) | |
| params: dict[str, Any] = Field( | |
| default_factory=dict, description="Primitive-specific parameters" | |
| ) | |
| class RepairAction(Action): | |
| """Repair Agent's action: a unified diff (or full replacement script).""" | |
| action_type: Literal["repair"] = "repair" | |
| unified_diff: str = Field(..., description="Unified diff or full replacement script") | |
| class ForgeAction(Action): | |
| """Union action: exactly one of `breakage` / `repair` must be set. | |
| This is the type registered with OpenEnv's `create_app`. It avoids | |
| Pydantic discriminated unions to keep the OpenAPI schema flat and | |
| cross-version-friendly. | |
| """ | |
| breakage: Optional[BreakageAction] = None | |
| repair: Optional[RepairAction] = None | |
| def model_post_init(self, __context: Any) -> None: | |
| if (self.breakage is None) == (self.repair is None): | |
| raise ValueError( | |
| "ForgeAction requires exactly one of `breakage` or `repair` to be set." | |
| ) | |