File size: 4,298 Bytes
77da5ce | 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 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 | # lifestack_env.py β Environment Reference
`core/lifestack_env.py` β The main OpenEnv-compatible RL environment for LifeStack.
---
## Overview
`LifeStackEnv` wraps the full simulation: metric cascades, world events, partial
observability, route execution, milestone tracking, and reward calculation.
Key classes in this file:
| Class | Role |
|---|---|
| `LifeStackAction` | Pydantic action schema (metric_changes, resource_cost, action_type, β¦) |
| `LifeStackObservation` | Pydantic observation schema (metrics, resources, step, done, reward, metadata) |
| `LifeStackState` | Internal state (current_metrics, budget, task, world_state, hidden_state, β¦) |
| `PartialObsFilter` | Converts full world state into the agent's partial observation |
| `WorldEngine` | Fires deterministic/probabilistic ExoEvents each step |
| `LifeStackEnv` | The environment itself β inherits from OpenEnv `Environment` |
---
## API
### `LifeStackEnv.__init__(seed, task, max_steps=30)`
```python
env = LifeStackEnv()
env = LifeStackEnv(seed=42, max_steps=50)
```
### `LifeStackEnv.reset(...) -> LifeStackObservation`
```python
obs = env.reset(task=my_task, episode_id="ep_001")
```
Parameters:
- `task` β a `Task` object (from `core/task.py`). Defaults to `FlightCrisisTask()`.
- `seed` β optional int for reproducibility.
- `conflict` β legacy `ConflictEvent` for metric disruption on reset.
- `budget` β dict with `time`, `money`, `energy` overrides.
- `person` β optional `SimPerson` for personality-driven drift.
### `LifeStackEnv.step(action) -> LifeStackObservation`
```python
obs = env.step(LifeStackAction(action_type="execute", target="rebook_premium"))
```
Supported `action_type` values:
| Type | Effect |
|---|---|
| `inspect` | Reveals a hidden-state key into the observation |
| `execute` | Attempts to activate a Route by `target` (route id) |
| `wait` | Passes the step; triggers stress penalty after 4 consecutive waits |
| `rollback` | Reverts metrics/budget to the previous step (one-time per episode) |
| `plan` / `communicate` / `spend` / `delegate` | Apply `metric_changes` and `resource_cost` |
### `LifeStackEnv.render()`
Prints a colour-coded terminal summary of the current state and task progress.
---
## PartialObsFilter
```python
PartialObsFilter.filter(task, revealed_keys) -> dict
```
- Base: `task.visible_world` (always visible).
- Keys in `revealed_keys` that exist in `task.mutable_world` β added as-is.
- Keys in `revealed_keys` that exist in `task.hidden_state` β wrapped as
`{"value": <val>, "source": "inspect"}` to signal the agent they came from inspect.
---
## Observation `metadata` fields
```python
obs.metadata = {
"world_state": dict, # partial view after filter
"goal": str,
"active_route": str | None,
"milestones": list[str],
"events": list[str],
"success": bool,
"failure": bool,
"failure_reason": str,
"routes_remaining": int,
"breakdown": dict, # reward component breakdown
"info": list[str], # step-level diagnostic messages
}
```
Key `info` message prefixes:
| Prefix | Meaning |
|---|---|
| `INSPECT_REVEALED:` | Key added to inspected list |
| `INSPECT_REVEALED_HIDDEN:` | Key was in `hidden_state` β value included |
| `INSPECT_REDUNDANT:` | Key already revealed, no-op |
| `ROUTE_SUCCESS:` | Route executed and consequences applied |
| `ROUTE_BLOCKED:` | Route was closed by a prior ExoEvent |
| `PRECONDITIONS_FAILED:` | Route preconditions not met |
| `MILESTONE_UNLOCKED:` | A milestone condition was met |
| `EVENT_FIRED:` | An ExoEvent triggered this step |
| `WAIT_CAP_EXCEEDED:` | 4+ consecutive waits β stress penalty applied |
---
## End Conditions
| Condition | `done` | `success` | `failure` |
|---|---|---|---|
| `step_count >= max_steps` | β
| depends | β |
| All `success_conditions` met | β
| β
| β |
| `failure_condition` met | β
| β | β
|
| Any metric hits 0 | β
| β | β
|
---
## Change Log
| Date | Change |
|---|---|
| 2026-04-23 | `PartialObsFilter.filter()` now reads `mutable_world` + `hidden_state` directly from `Task`; removed `world` param; hidden keys wrapped with `source: inspect`; `INSPECT_REVEALED_HIDDEN` info message added |
|