AzraelH commited on
Commit
2dbf205
·
1 Parent(s): d57fa5d
Files changed (7) hide show
  1. README.md +10 -0
  2. benchmark_tasks.py +189 -0
  3. graders.py +39 -0
  4. openenv.yaml +76 -0
  5. server/app.py +30 -0
  6. server/engineer_manager_environment.py +38 -3
  7. tasks.py +68 -0
README.md CHANGED
@@ -43,3 +43,13 @@ openenv validate http://127.0.0.1:8000
43
  - `task_buffer`: pending tasks with estimated duration and hidden complexity
44
  - `flow_score`, `social_debt`, `calendar_churn`: core scoring metrics
45
  - `current_slot`, `current_time`, `recovery_state`, `mute_comms`: live execution state
 
 
 
 
 
 
 
 
 
 
 
43
  - `task_buffer`: pending tasks with estimated duration and hidden complexity
44
  - `flow_score`, `social_debt`, `calendar_churn`: core scoring metrics
45
  - `current_slot`, `current_time`, `recovery_state`, `mute_comms`: live execution state
46
+
47
+ ## Built-in benchmark tasks
48
+
49
+ Set `TASK_NAME` to select a deterministic scenario before reset. Available tasks:
50
+
51
+ - `quiet-morning`: high-noise start where muting comms early and protecting focus is rewarded
52
+ - `meeting-surgery`: fragmented calendar where selective meeting moves should improve flow
53
+ - `delivery-triage`: constrained delivery day with hidden task complexity and tighter tradeoffs
54
+
55
+ Each task has a grader in [benchmark_tasks.py](/C:/Users/arshi/OneDrive/Desktop/idk/engineer-manager/benchmark_tasks.py:1). The environment also exposes task metadata and the current grader score in `observation.metadata.episode_metrics.grader_score`.
benchmark_tasks.py ADDED
@@ -0,0 +1,189 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ from dataclasses import dataclass
5
+ from typing import Any, Callable
6
+
7
+ from focus_resource_env import DEEP_WORK, EMPTY, MEETING, FocusResourceEnv, Task
8
+
9
+
10
+ StepRecord = dict[str, Any]
11
+ TaskSetup = Callable[[FocusResourceEnv], None]
12
+ TaskGrader = Callable[[list[StepRecord]], float]
13
+
14
+
15
+ def _reset_state(env: FocusResourceEnv) -> None:
16
+ env.timeline[:] = EMPTY
17
+ env.meeting_meta = {}
18
+ env.task_buffer = []
19
+ env.current_slot = 0
20
+ env.current_work_streak_slots = 0
21
+ env.recovery_remaining = 0
22
+ env.mute_comms = False
23
+ env.social_debt = 0.0
24
+ env.calendar_churn = 0
25
+ env.flow_score = 0.0
26
+ env.last_executed_kind = EMPTY
27
+ env.interruptions = 0
28
+ env.invalid_actions = 0
29
+
30
+
31
+ def _set_meeting(
32
+ env: FocusResourceEnv,
33
+ *,
34
+ start: int,
35
+ length: int,
36
+ priority: int,
37
+ meeting_id: int,
38
+ ) -> None:
39
+ env._place_meeting(start, length, priority, meeting_id)
40
+
41
+
42
+ def _normalized_total_score(env: FocusResourceEnv) -> float:
43
+ max_score = max(1.0, (env.timeline_length * 0.5) ** 2)
44
+ return min(1.0, max(0.0, env._total_score() / max_score))
45
+
46
+
47
+ def setup_quiet_morning(env: FocusResourceEnv) -> None:
48
+ _reset_state(env)
49
+ env.distraction_risk = 0.65
50
+ env.task_buffer = [
51
+ Task(duration=2, hidden_complexity=1.0),
52
+ Task(duration=3, hidden_complexity=1.0),
53
+ Task(duration=2, hidden_complexity=1.25),
54
+ ]
55
+ _set_meeting(env, start=5, length=1, priority=4, meeting_id=1)
56
+ _set_meeting(env, start=7, length=1, priority=3, meeting_id=2)
57
+
58
+
59
+ def setup_meeting_surgery(env: FocusResourceEnv) -> None:
60
+ _reset_state(env)
61
+ env.distraction_risk = 0.10
62
+ env.task_buffer = [
63
+ Task(duration=2, hidden_complexity=1.0),
64
+ Task(duration=2, hidden_complexity=1.25),
65
+ Task(duration=1, hidden_complexity=1.0),
66
+ ]
67
+ _set_meeting(env, start=1, length=1, priority=2, meeting_id=1)
68
+ _set_meeting(env, start=3, length=1, priority=2, meeting_id=2)
69
+ _set_meeting(env, start=6, length=2, priority=8, meeting_id=3)
70
+
71
+
72
+ def setup_delivery_triage(env: FocusResourceEnv) -> None:
73
+ _reset_state(env)
74
+ env.distraction_risk = 0.25
75
+ env.task_buffer = [
76
+ Task(duration=3, hidden_complexity=1.5),
77
+ Task(duration=2, hidden_complexity=1.0),
78
+ Task(duration=1, hidden_complexity=1.0),
79
+ ]
80
+ _set_meeting(env, start=4, length=1, priority=9, meeting_id=1)
81
+ _set_meeting(env, start=8, length=2, priority=7, meeting_id=2)
82
+
83
+
84
+ def grade_quiet_morning(trajectory: list[StepRecord]) -> float:
85
+ if not trajectory:
86
+ return 0.0
87
+ first_action = int(trajectory[0]["action"]["operation"])
88
+ final = trajectory[-1]["observation"]
89
+ final_score = float(final["flow_score"])
90
+ transition_count = sum(1 for step in trajectory if step["info"]["transition_info"]["interrupted"])
91
+ scheduled = sum(1 for slot in final["timeline"] if int(slot) == DEEP_WORK)
92
+
93
+ score = 0.0
94
+ score += 0.25 if first_action == 3 else 0.0
95
+ score += min(0.45, final_score / 6.0)
96
+ score += 0.15 if transition_count == 0 else 0.0
97
+ score += min(0.15, scheduled / 6.0)
98
+ return min(1.0, round(score, 4))
99
+
100
+
101
+ def grade_meeting_surgery(trajectory: list[StepRecord]) -> float:
102
+ if not trajectory:
103
+ return 0.0
104
+ final = trajectory[-1]["observation"]
105
+ flow = float(final["flow_score"])
106
+ debt = float(final["social_debt"])
107
+ churn = int(final["calendar_churn"])
108
+ reschedules = sum(
109
+ 1
110
+ for step in trajectory
111
+ if step["info"].get("action_info", {}).get("status") == "meeting_rescheduled"
112
+ )
113
+
114
+ score = 0.0
115
+ score += min(0.40, flow / 5.0)
116
+ score += 0.20 if reschedules >= 1 else 0.0
117
+ score += 0.20 if 1 <= churn <= 2 else max(0.0, 0.20 - (0.10 * abs(churn - 1)))
118
+ score += max(0.0, 0.20 - (debt / 8.0))
119
+ return min(1.0, round(score, 4))
120
+
121
+
122
+ def grade_delivery_triage(trajectory: list[StepRecord]) -> float:
123
+ if not trajectory:
124
+ return 0.0
125
+ final = trajectory[-1]["observation"]
126
+ total = float(final["flow_score"]) - float(final["social_debt"]) - float(final["calendar_churn"])
127
+ invalid_actions = sum(
128
+ 1
129
+ for step in trajectory
130
+ if str(step["info"].get("action_info", {}).get("status", "")).startswith("invalid")
131
+ )
132
+ remaining_tasks = len(final["task_buffer"])
133
+ scheduled = sum(1 for slot in final["timeline"] if int(slot) == DEEP_WORK)
134
+
135
+ score = 0.0
136
+ score += min(0.45, max(0.0, total) / 6.0)
137
+ score += min(0.25, scheduled / 8.0)
138
+ score += 0.20 if remaining_tasks <= 1 else 0.10 if remaining_tasks == 2 else 0.0
139
+ score += max(0.0, 0.10 - (0.05 * invalid_actions))
140
+ return min(1.0, round(score, 4))
141
+
142
+
143
+ @dataclass(frozen=True)
144
+ class TaskSpec:
145
+ name: str
146
+ description: str
147
+ setup: TaskSetup
148
+ grader: TaskGrader
149
+
150
+
151
+ TASK_SPECS: dict[str, TaskSpec] = {
152
+ "quiet-morning": TaskSpec(
153
+ name="quiet-morning",
154
+ description="High-noise morning where the agent should mute comms early and protect an uninterrupted work block.",
155
+ setup=setup_quiet_morning,
156
+ grader=grade_quiet_morning,
157
+ ),
158
+ "meeting-surgery": TaskSpec(
159
+ name="meeting-surgery",
160
+ description="A fragmented calendar where the agent should improve flow with limited, selective meeting moves.",
161
+ setup=setup_meeting_surgery,
162
+ grader=grade_meeting_surgery,
163
+ ),
164
+ "delivery-triage": TaskSpec(
165
+ name="delivery-triage",
166
+ description="A constrained day with hidden task complexity where the agent must schedule useful work without spiraling debt.",
167
+ setup=setup_delivery_triage,
168
+ grader=grade_delivery_triage,
169
+ ),
170
+ }
171
+
172
+
173
+ DEFAULT_TASK_NAME = "quiet-morning"
174
+
175
+
176
+ def get_task_spec(task_name: str | None) -> TaskSpec:
177
+ normalized = (task_name or os.getenv("TASK_NAME") or DEFAULT_TASK_NAME).strip()
178
+ return TASK_SPECS.get(normalized, TASK_SPECS[DEFAULT_TASK_NAME])
179
+
180
+
181
+ def apply_task(env: FocusResourceEnv, task_name: str | None) -> TaskSpec:
182
+ spec = get_task_spec(task_name)
183
+ spec.setup(env)
184
+ return spec
185
+
186
+
187
+ def grade_trajectory(task_name: str, trajectory: list[StepRecord]) -> float:
188
+ spec = get_task_spec(task_name)
189
+ return spec.grader(trajectory)
graders.py ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+
5
+ from benchmark_tasks import grade_trajectory
6
+
7
+
8
+ def _coerce_trajectory(payload: Any) -> list[dict[str, Any]]:
9
+ if isinstance(payload, list):
10
+ return [dict(step) for step in payload]
11
+ if isinstance(payload, dict):
12
+ if isinstance(payload.get("trajectory"), list):
13
+ return [dict(step) for step in payload["trajectory"]]
14
+ if isinstance(payload.get("steps"), list):
15
+ return [dict(step) for step in payload["steps"]]
16
+ return []
17
+
18
+
19
+ def _grade(task_name: str, payload: Any) -> dict[str, Any]:
20
+ trajectory = _coerce_trajectory(payload)
21
+ score = grade_trajectory(task_name, trajectory)
22
+ return {
23
+ "task_name": task_name,
24
+ "score": score,
25
+ "passed": score > 0.0,
26
+ "reward": score,
27
+ }
28
+
29
+
30
+ def grade_task_0(payload: Any) -> dict[str, Any]:
31
+ return _grade("quiet-morning", payload)
32
+
33
+
34
+ def grade_task_1(payload: Any) -> dict[str, Any]:
35
+ return _grade("meeting-surgery", payload)
36
+
37
+
38
+ def grade_task_2(payload: Any) -> dict[str, Any]:
39
+ return _grade("delivery-triage", payload)
openenv.yaml CHANGED
@@ -4,3 +4,79 @@ type: space
4
  runtime: fastapi
5
  app: server.app:app
6
  port: 8000
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4
  runtime: fastapi
5
  app: server.app:app
6
  port: 8000
7
+ version: 0.1.0
8
+ entry_point: server.engineer_manager_environment:EngineerManagerEnvironment
9
+ api:
10
+ base_url: /
11
+ endpoints:
12
+ reset:
13
+ method: POST
14
+ path: /reset
15
+ step:
16
+ method: POST
17
+ path: /step
18
+ state:
19
+ method: GET
20
+ path: /state
21
+ tasks:
22
+ method: GET
23
+ path: /tasks
24
+ grader:
25
+ method: POST
26
+ path: /grader
27
+ tasks:
28
+ - id: engineer_manager_task_0
29
+ task_id: quiet-morning
30
+ name: quiet-morning
31
+ difficulty: easy
32
+ description: High-noise morning where the agent should mute comms early and protect an uninterrupted work block.
33
+ max_steps: 32
34
+ reset_params:
35
+ task_name: quiet-morning
36
+ action_schema:
37
+ target_slot: integer slot index within the workday
38
+ operation: 0=idle, 1=schedule work, 2=reschedule meeting, 3=toggle mute comms
39
+ grader: graders:grade_task_0
40
+ graders:
41
+ - graders:grade_task_0
42
+ reward_range:
43
+ - 0.0
44
+ - 1.0
45
+ - id: engineer_manager_task_1
46
+ task_id: meeting-surgery
47
+ name: meeting-surgery
48
+ difficulty: medium
49
+ description: Fragmented calendar where selective meeting moves should improve flow.
50
+ max_steps: 32
51
+ reset_params:
52
+ task_name: meeting-surgery
53
+ action_schema:
54
+ target_slot: integer slot index within the workday
55
+ operation: 0=idle, 1=schedule work, 2=reschedule meeting, 3=toggle mute comms
56
+ grader: graders:grade_task_1
57
+ graders:
58
+ - graders:grade_task_1
59
+ reward_range:
60
+ - 0.0
61
+ - 1.0
62
+ - id: engineer_manager_task_2
63
+ task_id: delivery-triage
64
+ name: delivery-triage
65
+ difficulty: hard
66
+ description: Constrained delivery day with hidden task complexity and tighter tradeoffs.
67
+ max_steps: 32
68
+ reset_params:
69
+ task_name: delivery-triage
70
+ action_schema:
71
+ target_slot: integer slot index within the workday
72
+ operation: 0=idle, 1=schedule work, 2=reschedule meeting, 3=toggle mute comms
73
+ grader: graders:grade_task_2
74
+ graders:
75
+ - graders:grade_task_2
76
+ reward_range:
77
+ - 0.0
78
+ - 1.0
79
+ graders:
80
+ - graders:grade_task_0
81
+ - graders:grade_task_1
82
+ - graders:grade_task_2
server/app.py CHANGED
@@ -7,6 +7,10 @@ from textwrap import dedent
7
  import uvicorn
8
  from fastapi.responses import HTMLResponse, JSONResponse, PlainTextResponse, RedirectResponse, Response
9
  from openenv.core.env_server.http_server import create_fastapi_app
 
 
 
 
10
 
11
  try:
12
  from ..models import EngineerManagerAction, EngineerManagerObservation
@@ -29,6 +33,11 @@ app = create_fastapi_app(
29
  max_concurrent_envs=2,
30
  )
31
 
 
 
 
 
 
32
  WEB_CSS = dedent(
33
  """\
34
  :root {
@@ -453,6 +462,27 @@ def manifest() -> JSONResponse:
453
  )
454
 
455
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
456
  def run(host: str = "0.0.0.0", port: int = 8000) -> None:
457
  """Run the OpenEnv HTTP server."""
458
  uvicorn.run(app, host=host, port=port)
 
7
  import uvicorn
8
  from fastapi.responses import HTMLResponse, JSONResponse, PlainTextResponse, RedirectResponse, Response
9
  from openenv.core.env_server.http_server import create_fastapi_app
10
+ from pydantic import BaseModel
11
+
12
+ from graders import grade_task_0, grade_task_1, grade_task_2
13
+ from tasks import TASKS
14
 
15
  try:
16
  from ..models import EngineerManagerAction, EngineerManagerObservation
 
33
  max_concurrent_envs=2,
34
  )
35
 
36
+
37
+ class GraderRequest(BaseModel):
38
+ task_id: str
39
+ trajectory: list[dict]
40
+
41
  WEB_CSS = dedent(
42
  """\
43
  :root {
 
462
  )
463
 
464
 
465
+ @app.get("/tasks", include_in_schema=False)
466
+ def tasks() -> JSONResponse:
467
+ return JSONResponse({"tasks": TASKS})
468
+
469
+
470
+ @app.post("/grader", include_in_schema=False)
471
+ def grader(request: GraderRequest) -> JSONResponse:
472
+ graders = {
473
+ "quiet-morning": grade_task_0,
474
+ "meeting-surgery": grade_task_1,
475
+ "delivery-triage": grade_task_2,
476
+ }
477
+ grader_fn = graders.get(request.task_id)
478
+ if grader_fn is None:
479
+ return JSONResponse(
480
+ {"error": f"Unknown task_id: {request.task_id}", "score": 0.0, "passed": False},
481
+ status_code=400,
482
+ )
483
+ return JSONResponse(grader_fn({"trajectory": request.trajectory}))
484
+
485
+
486
  def run(host: str = "0.0.0.0", port: int = 8000) -> None:
487
  """Run the OpenEnv HTTP server."""
488
  uvicorn.run(app, host=host, port=port)
server/engineer_manager_environment.py CHANGED
@@ -3,10 +3,12 @@
3
  from __future__ import annotations
4
 
5
  from uuid import uuid4
 
6
 
7
  from openenv.core.env_server.interfaces import Environment, EnvironmentMetadata
8
  from openenv.core.env_server.types import State
9
 
 
10
  from focus_resource_env import FocusResourceEnv
11
 
12
  try:
@@ -28,14 +30,17 @@ class EngineerManagerEnvironment(
28
  end_hour: str = "17:00",
29
  distraction_risk: float = 0.15,
30
  seed: int | None = 7,
 
31
  ) -> None:
32
  super().__init__()
33
  self._start_hour = start_hour
34
  self._end_hour = end_hour
35
  self._distraction_risk = distraction_risk
36
  self._seed = seed
 
37
  self._step_count = 0
38
  self._episode_id = str(uuid4())
 
39
  self._env = FocusResourceEnv(
40
  start_hour=start_hour,
41
  end_hour=end_hour,
@@ -47,18 +52,23 @@ class EngineerManagerEnvironment(
47
  self,
48
  seed: int | None = None,
49
  episode_id: str | None = None,
 
50
  **_: object,
51
  ) -> EngineerManagerObservation:
52
  self._seed = self._seed if seed is None else seed
 
53
  self._episode_id = episode_id or str(uuid4())
54
  self._step_count = 0
 
55
  self._env = FocusResourceEnv(
56
  start_hour=self._start_hour,
57
  end_hour=self._end_hour,
58
  distraction_risk=self._distraction_risk,
59
  seed=self._seed,
60
  )
61
- return self._to_observation(self._env.reset(), reward=0.0, done=False)
 
 
62
 
63
  def step(
64
  self,
@@ -71,6 +81,15 @@ class EngineerManagerEnvironment(
71
  (action.target_slot, action.operation)
72
  )
73
  self._step_count += 1
 
 
 
 
 
 
 
 
 
74
  return self._to_observation(observation, reward=reward, done=done, info=info)
75
 
76
  @property
@@ -87,7 +106,8 @@ class EngineerManagerEnvironment(
87
  name="Engineer Manager",
88
  description=(
89
  "Manage a workday by scheduling deep work, rescheduling meetings, "
90
- "and controlling communication noise."
 
91
  ),
92
  version="0.1.0",
93
  )
@@ -103,5 +123,20 @@ class EngineerManagerEnvironment(
103
  payload = dict(observation)
104
  payload["reward"] = reward
105
  payload["done"] = done
106
- payload["metadata"] = info or {}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
107
  return EngineerManagerObservation.model_validate(payload)
 
3
  from __future__ import annotations
4
 
5
  from uuid import uuid4
6
+ import os
7
 
8
  from openenv.core.env_server.interfaces import Environment, EnvironmentMetadata
9
  from openenv.core.env_server.types import State
10
 
11
+ from benchmark_tasks import TASK_SPECS, apply_task, grade_trajectory
12
  from focus_resource_env import FocusResourceEnv
13
 
14
  try:
 
30
  end_hour: str = "17:00",
31
  distraction_risk: float = 0.15,
32
  seed: int | None = 7,
33
+ task_name: str | None = None,
34
  ) -> None:
35
  super().__init__()
36
  self._start_hour = start_hour
37
  self._end_hour = end_hour
38
  self._distraction_risk = distraction_risk
39
  self._seed = seed
40
+ self._task_name = task_name or os.getenv("TASK_NAME")
41
  self._step_count = 0
42
  self._episode_id = str(uuid4())
43
+ self._trajectory: list[dict[str, object]] = []
44
  self._env = FocusResourceEnv(
45
  start_hour=start_hour,
46
  end_hour=end_hour,
 
52
  self,
53
  seed: int | None = None,
54
  episode_id: str | None = None,
55
+ task_name: str | None = None,
56
  **_: object,
57
  ) -> EngineerManagerObservation:
58
  self._seed = self._seed if seed is None else seed
59
+ self._task_name = task_name or self._task_name or os.getenv("TASK_NAME")
60
  self._episode_id = episode_id or str(uuid4())
61
  self._step_count = 0
62
+ self._trajectory = []
63
  self._env = FocusResourceEnv(
64
  start_hour=self._start_hour,
65
  end_hour=self._end_hour,
66
  distraction_risk=self._distraction_risk,
67
  seed=self._seed,
68
  )
69
+ self._env.reset()
70
+ apply_task(self._env, self._task_name)
71
+ return self._to_observation(self._env._observation(), reward=0.0, done=False)
72
 
73
  def step(
74
  self,
 
81
  (action.target_slot, action.operation)
82
  )
83
  self._step_count += 1
84
+ self._trajectory.append(
85
+ {
86
+ "action": {"target_slot": int(action.target_slot), "operation": int(action.operation)},
87
+ "observation": observation,
88
+ "reward": float(reward),
89
+ "done": bool(done),
90
+ "info": info,
91
+ }
92
+ )
93
  return self._to_observation(observation, reward=reward, done=done, info=info)
94
 
95
  @property
 
106
  name="Engineer Manager",
107
  description=(
108
  "Manage a workday by scheduling deep work, rescheduling meetings, "
109
+ "and controlling communication noise. "
110
+ f"Available tasks: {', '.join(sorted(TASK_SPECS))}."
111
  ),
112
  version="0.1.0",
113
  )
 
123
  payload = dict(observation)
124
  payload["reward"] = reward
125
  payload["done"] = done
126
+ metadata = dict(info or {})
127
+ metadata["task_name"] = self._task_name
128
+ metadata["episode_metrics"] = {
129
+ "interruptions": int(self._env.interruptions),
130
+ "invalid_actions": int(self._env.invalid_actions),
131
+ "remaining_tasks": len(self._env.task_buffer),
132
+ "scheduled_work_slots": sum(1 for slot in self._env.timeline if int(slot) == 1),
133
+ "successful_reschedules": sum(
134
+ 1
135
+ for step in self._trajectory
136
+ if step["info"].get("action_info", {}).get("status") == "meeting_rescheduled"
137
+ ),
138
+ "total_score": float(self._env._total_score()),
139
+ "grader_score": grade_trajectory(self._task_name or "", self._trajectory) if self._trajectory else 0.0,
140
+ }
141
+ payload["metadata"] = metadata
142
  return EngineerManagerObservation.model_validate(payload)
tasks.py ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from benchmark_tasks import TASK_SPECS
4
+
5
+
6
+ TASKS = [
7
+ {
8
+ "id": "engineer_manager_task_0",
9
+ "task_id": "quiet-morning",
10
+ "name": "quiet-morning",
11
+ "difficulty": "easy",
12
+ "description": TASK_SPECS["quiet-morning"].description,
13
+ "max_steps": 32,
14
+ "reset_params": {"task_name": "quiet-morning"},
15
+ "action_schema": {
16
+ "target_slot": "integer slot index within the workday",
17
+ "operation": "0=idle, 1=schedule work, 2=reschedule meeting, 3=toggle mute comms",
18
+ },
19
+ "grader": "graders:grade_task_0",
20
+ "graders": ["graders:grade_task_0"],
21
+ "reward_range": [0.0, 1.0],
22
+ },
23
+ {
24
+ "id": "engineer_manager_task_1",
25
+ "task_id": "meeting-surgery",
26
+ "name": "meeting-surgery",
27
+ "difficulty": "medium",
28
+ "description": TASK_SPECS["meeting-surgery"].description,
29
+ "max_steps": 32,
30
+ "reset_params": {"task_name": "meeting-surgery"},
31
+ "action_schema": {
32
+ "target_slot": "integer slot index within the workday",
33
+ "operation": "0=idle, 1=schedule work, 2=reschedule meeting, 3=toggle mute comms",
34
+ },
35
+ "grader": "graders:grade_task_1",
36
+ "graders": ["graders:grade_task_1"],
37
+ "reward_range": [0.0, 1.0],
38
+ },
39
+ {
40
+ "id": "engineer_manager_task_2",
41
+ "task_id": "delivery-triage",
42
+ "name": "delivery-triage",
43
+ "difficulty": "hard",
44
+ "description": TASK_SPECS["delivery-triage"].description,
45
+ "max_steps": 32,
46
+ "reset_params": {"task_name": "delivery-triage"},
47
+ "action_schema": {
48
+ "target_slot": "integer slot index within the workday",
49
+ "operation": "0=idle, 1=schedule work, 2=reschedule meeting, 3=toggle mute comms",
50
+ },
51
+ "grader": "graders:grade_task_2",
52
+ "graders": ["graders:grade_task_2"],
53
+ "reward_range": [0.0, 1.0],
54
+ },
55
+ ]
56
+
57
+
58
+ TASK_ID_TO_INDEX = {task["task_id"]: index for index, task in enumerate(TASKS)}
59
+
60
+
61
+ TASK_GRADER_PAIRS = [
62
+ ("engineer_manager_task_0", "graders:grade_task_0"),
63
+ ("engineer_manager_task_1", "graders:grade_task_1"),
64
+ ("engineer_manager_task_2", "graders:grade_task_2"),
65
+ ]
66
+
67
+
68
+ __all__ = ["TASKS", "TASK_ID_TO_INDEX", "TASK_GRADER_PAIRS"]