| |
| |
| |
| |
| |
|
|
| """Episteme Environment Client. |
| |
| Provides a typed client for the Episteme environment server. Extends |
| :class:`EnvClient` parameterised with :class:`EpistemeAction` and |
| :class:`EpistemeObservation`. |
| |
| Example (async):: |
| |
| async with EpistemeEnv(base_url="http://localhost:8000") as client: |
| result = await client.reset(task_type="fact_check", difficulty=2) |
| print(result.observation.result) |
| |
| result = await client.step( |
| EpistemeAction(tool_name="search_corpus", |
| arguments={"query": "eiffel tower"}) |
| ) |
| |
| Example (sync wrapper):: |
| |
| env = EpistemeEnv(base_url="http://localhost:8000").sync() |
| with env: |
| result = env.reset(task_type="research_brief", difficulty=1) |
| result = env.step( |
| EpistemeAction(tool_name="read_notes", arguments={}) |
| ) |
| """ |
|
|
| from __future__ import annotations |
|
|
| from typing import Any, Dict, Optional |
|
|
| from openenv.core.env_client import EnvClient |
| from openenv.core.client_types import StepResult |
| from openenv.core.env_server.types import State |
|
|
| from .models import EpistemeAction, EpistemeObservation |
|
|
|
|
| class EpistemeEnv( |
| EnvClient[EpistemeAction, EpistemeObservation, State] |
| ): |
| """Typed WebSocket client for the Episteme environment. |
| |
| Inherits connection management, ``step``, and ``state`` from |
| :class:`EnvClient`. Adds an explicit ``reset`` method that |
| exposes ``task_type`` and ``difficulty`` as keyword arguments. |
| """ |
|
|
| |
| |
| |
|
|
| async def reset( |
| self, |
| *, |
| task_type: str = "fact_check", |
| difficulty: int = 1, |
| seed: Optional[int] = None, |
| episode_id: Optional[str] = None, |
| **kwargs: Any, |
| ) -> StepResult[EpistemeObservation]: |
| """Reset the environment and start a new episode. |
| |
| All keyword arguments are forwarded to the server's ``reset`` |
| endpoint via the WebSocket ``reset`` message. |
| |
| Args: |
| task_type: One of ``"fact_check"``, ``"research_brief"``, |
| ``"market_analysis"``, or ``"notes"``. |
| difficulty: Corpus difficulty level (1-5). |
| seed: Optional random seed for reproducibility. |
| episode_id: Optional custom episode identifier. |
| **kwargs: Additional parameters forwarded to the server. |
| |
| Returns: |
| :class:`StepResult` containing the initial |
| :class:`EpistemeObservation`. |
| """ |
| reset_kwargs: Dict[str, Any] = { |
| "task_type": task_type, |
| "difficulty": difficulty, |
| **kwargs, |
| } |
| if seed is not None: |
| reset_kwargs["seed"] = seed |
| if episode_id is not None: |
| reset_kwargs["episode_id"] = episode_id |
|
|
| return await super().reset(**reset_kwargs) |
|
|
| |
| |
| |
|
|
| def _step_payload(self, action: EpistemeAction) -> Dict[str, Any]: |
| """Convert an :class:`EpistemeAction` to the JSON payload |
| expected by the environment server's ``step`` endpoint. |
| """ |
| return action.model_dump() |
|
|
| def _parse_result( |
| self, payload: Dict[str, Any] |
| ) -> StepResult[EpistemeObservation]: |
| """Parse a server response into a typed :class:`StepResult`. |
| |
| The framework's ``serialize_observation`` places ``reward``, |
| ``done``, and ``metadata`` at the top level of ``payload`` |
| (outside the nested ``observation`` dict), so we source them |
| from there. |
| """ |
| obs_data = payload.get("observation", {}) |
|
|
| |
| |
| reward = payload.get("reward", 0.0) |
| done = payload.get("done", False) |
|
|
| observation = EpistemeObservation( |
| result=obs_data.get("result", ""), |
| reward=reward if reward is not None else 0.0, |
| done=done, |
| step_count=obs_data.get("step_count", 0), |
| steps_remaining=obs_data.get("steps_remaining", 0), |
| success=obs_data.get("success", True), |
| ) |
|
|
| return StepResult( |
| observation=observation, |
| reward=reward, |
| done=done, |
| ) |
|
|
| def _parse_state(self, payload: Dict[str, Any]) -> State: |
| """Parse a server state response into a :class:`State` object.""" |
| return State( |
| episode_id=payload.get("episode_id"), |
| step_count=payload.get("step_count", 0), |
| ) |
|
|