File size: 1,976 Bytes
ea1db8d
 
 
5d342ad
 
 
 
 
 
 
ea1db8d
 
5d342ad
 
ea1db8d
 
 
 
 
5d342ad
ea1db8d
 
 
5d342ad
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
"""
Second Brain Environment — WebSocket Client
Connects to the running server and exposes reset()/step()/state().

FIX: The openenv HTTP layer deserializes the observation using the Pydantic
schema, but `reward` has a default of 0.0 — so if the server response omits
the field (or it gets stripped by the base class serializer) it silently
resets to 0. The fix: read `reward` from the raw response dict BEFORE
constructing the typed observation, then attach it as `result.reward` so
inference.py can read it independently of the observation object.
"""
from openenv.core.env_client import EnvClient
from models import SecondBrainAction, SecondBrainObservation, SecondBrainState
from types import SimpleNamespace


class SecondBrainEnv(EnvClient):
    action_type      = SecondBrainAction
    observation_type = SecondBrainObservation
    state_type       = SecondBrainState

    def __init__(self, base_url: str = "http://localhost:8000", task_name: str = "note_categorization"):
        super().__init__(base_url=base_url)
        self._task_name = task_name

    def _step_payload(self, action: SecondBrainAction) -> dict:
        return action.model_dump()

    def _parse_result(self, data: dict):
        obs_data = data.get("observation", {})

        # Pull reward out of the raw dict BEFORE Pydantic touches it.
        # The base EnvClient may not forward all fields — this is the only
        # reliable place to read it.
        reward = float(obs_data.get("reward", 0.0))

        obs  = self.observation_type(**obs_data)
        done = data.get("done", False)

        # Attach reward at the top level of the result so inference.py can
        # read result.reward without depending on obs.reward surviving intact.
        return SimpleNamespace(
            observation=obs,
            done=done,
            reward=reward,          # ← top-level, always correct
        )

    def _parse_state(self, data: dict):
        return self.state_type(**data)