jayesh20 commited on
Commit
4a810e9
·
verified ·
1 Parent(s): 34aae41

Upload folder using huggingface_hub

Browse files
Files changed (5) hide show
  1. README.md +145 -67
  2. inference.py +132 -51
  3. models.py +57 -10
  4. server/openenv_jayesh_environment.py +391 -113
  5. validate_final.txt +1 -0
README.md CHANGED
@@ -1,19 +1,21 @@
1
  ---
2
- title: OpenEnv Jayesh - Task Manager
3
- colorFrom: blue
4
- colorTo: green
5
  sdk: docker
6
  pinned: false
7
  app_port: 8000
8
  tags:
9
  - openenv
10
  - task-manager
 
 
11
  base_path: /web
12
  ---
13
 
14
- # OpenEnv Jayesh - Task Manager Environment
15
 
16
- An AI agent environment for managing tasks across three difficulty levels, built for the OpenEnv Hackathon Round 1.
17
 
18
  - HF Space: https://huggingface.co/spaces/jayesh20/openenv_jayesh
19
  - Python 3.10+
@@ -22,17 +24,19 @@ An AI agent environment for managing tasks across three difficulty levels, built
22
 
23
  ## What is this?
24
 
25
- A real-world Task Manager environment where an AI agent must add, prioritize, and complete tasks to earn rewards. The environment cycles through Easy, Medium, and Hard difficulty levels, each with stricter goals and richer reward signals.
 
 
26
 
27
  ---
28
 
29
  ## Difficulty Levels
30
 
31
- | Level | Goal |
32
- |--------|------|
33
- | Easy | Add 2 tasks and list them |
34
- | Medium | Add 3 tasks (mixed priorities), complete all High priority ones |
35
- | Hard | Add 4 tasks (at least 2 High priority), complete at least 2 High priority tasks |
36
 
37
  ---
38
 
@@ -40,54 +44,85 @@ A real-world Task Manager environment where an AI agent must add, prioritize, an
40
 
41
  ```python
42
  TaskManagerAction(
43
- command="add", # "add" | "complete" | "list"
44
- title="Fix bug", # required for add / complete
45
- priority="High", # "Low" | "Normal" | "High" (optional, default: Normal)
46
- deadline="2026-04-01" # optional
 
47
  )
48
  ```
49
 
 
 
 
 
 
 
 
50
  ---
51
 
52
  ## Observation Space
53
 
54
  ```python
55
  TaskManagerObservation(
56
- success=True, # whether the action succeeded
57
- message="Task added", # status message
58
- tasks=[...], # current task list
59
- reward=0.4, # partial progress score (0.0 to 1.0)
60
- done=False # True when episode goal is achieved
 
 
 
 
 
 
 
 
 
61
  )
62
  ```
63
 
 
 
 
 
 
 
 
 
 
 
 
64
  ---
65
 
66
  ## Reward Function
67
 
68
- ### Easy
69
  | Event | Reward |
70
  |-------|--------|
71
- | Add 1st task | +0.4 |
72
- | Add 2nd task | +0.4 |
73
- | Call list | +0.2 |
74
- | Goal complete | 1.0 |
75
 
76
- ### Medium
77
  | Event | Reward |
78
  |-------|--------|
79
- | Each task added (up to 3) | +0.2 each |
80
- | Adding a High priority task | +0.1 |
81
- | Completing High priority tasks | +0.3 (proportional) |
82
- | Goal complete | 1.0 |
 
83
 
84
- ### Hard
85
  | Event | Reward |
86
  |-------|--------|
87
- | Each task added (up to 4) | +0.1 each |
88
- | Each High priority task added (up to 2) | +0.1 each |
89
- | Each High priority task completed (up to 2) | +0.2 each |
90
- | Goal complete | 1.0 |
 
 
 
91
 
92
  ---
93
 
@@ -97,49 +132,93 @@ TaskManagerObservation(
97
  # Install dependencies
98
  uv sync
99
 
100
- # Run the server
101
  uvicorn server.app:app --host 127.0.0.1 --port 8000
102
 
103
- # In another terminal, run inference
104
  python inference.py
105
  ```
106
 
107
  ---
108
 
109
- ## Usage Example
110
 
 
111
  ```python
112
- from server.openenv_jayesh_environment import OpenenvJayeshEnvironment
113
- from models import TaskManagerAction
114
-
115
  env = OpenenvJayeshEnvironment()
 
116
 
117
- obs = env.reset()
118
- print(obs.message)
119
- # Task Manager started in Easy mode. Goal: Add 2 tasks and list them.
120
-
121
- obs = env.step(TaskManagerAction(command="add", title="Buy groceries", priority="Normal"))
122
- print(obs.reward) # 0.4
123
 
124
- obs = env.step(TaskManagerAction(command="add", title="Fix bug", priority="High"))
125
- print(obs.reward) # 0.8
126
 
127
  obs = env.step(TaskManagerAction(command="list"))
128
- print(obs.reward) # 1.0
129
- print(obs.done) # True
 
 
 
 
 
 
 
 
 
 
 
 
130
  ```
131
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
132
  ---
133
 
134
  ## API Endpoints
135
 
136
  | URL | Description |
137
  |-----|-------------|
138
- | http://127.0.0.1:8000/docs | Swagger UI - interactive API testing |
139
- | http://127.0.0.1:8000/health | Health check |
140
- | POST /reset | Start a new episode |
141
- | POST /step | Execute an action |
142
- | GET /state | Get current episode state |
143
 
144
  ---
145
 
@@ -147,21 +226,20 @@ print(obs.done) # True
147
 
148
  ```
149
  openenv_jayesh/
150
- ├── Dockerfile
151
- ├── openenv.yaml
152
- ├── pyproject.toml
153
- ├── models.py
154
- ├── client.py
155
- ├── inference.py
156
- └── server/
157
- ├── app.py
158
- ├── openenv_jayesh_environment.py
159
- └── requirements.txt
160
  ```
161
 
162
  ---
163
 
164
- ## Deploy to Hugging Face Spaces
165
 
166
  ```bash
167
  openenv push --repo-id jayesh20/openenv_jayesh
 
1
  ---
2
+ title: OpenEnv Jayesh - Smart Personal Task Manager
3
+ colorFrom: indigo
4
+ colorTo: purple
5
  sdk: docker
6
  pinned: false
7
  app_port: 8000
8
  tags:
9
  - openenv
10
  - task-manager
11
+ - ai-agent
12
+ - planning
13
  base_path: /web
14
  ---
15
 
16
+ # Smart Personal Task Manager - OpenEnv Jayesh
17
 
18
+ > An AI agent environment for managing tasks with priorities, deadlines, and dependencies -- built for the **OpenEnv Hackathon Round 1**.
19
 
20
  - HF Space: https://huggingface.co/spaces/jayesh20/openenv_jayesh
21
  - Python 3.10+
 
24
 
25
  ## What is this?
26
 
27
+ A **real-world Task Manager environment** where an AI agent must add, prioritize, and complete tasks while respecting deadlines and dependency constraints. The environment cycles through three meaningfully distinct difficulty levels -- each demanding progressively more sophisticated planning.
28
+
29
+ This environment targets real-world utility: the kind of task scheduling problems that users, productivity apps, and organizational tools deal with every day.
30
 
31
  ---
32
 
33
  ## Difficulty Levels
34
 
35
+ | Level | Goal | Key Constraints |
36
+ |-------|------|-----------------|
37
+ | **Easy** | Add 2-3 tasks, then `list` them | None -- basic task CRUD |
38
+ | **Medium** | Add 4 tasks with priorities & deadlines; complete all High-priority before deadline | Deadline enforcement, priority management |
39
+ | **Hard** | Add 5 tasks with priorities, deadlines, AND dependencies; complete in valid topological order | Dependency ordering + deadline enforcement + penalty accumulation |
40
 
41
  ---
42
 
 
44
 
45
  ```python
46
  TaskManagerAction(
47
+ command = "add", # "add" | "complete" | "list"
48
+ title = "Fix critical bug", # required for add / complete
49
+ priority = "High", # "Low" | "Normal" | "High" (default: "Normal")
50
+ deadline = "2026-04-15", # ISO-8601 date (optional; relevant in Medium & Hard)
51
+ depends_on= ["Reproduce bug"] # list of prerequisite task titles (Hard only)
52
  )
53
  ```
54
 
55
+ ### Commands
56
+ | Command | Description |
57
+ |---------|-------------|
58
+ | `add` | Create a new task. Returns error if title already exists or dependency is unresolved. |
59
+ | `complete` | Mark a task as done. Checks deadline & dependency constraints and applies penalties. |
60
+ | `list` | Display all current tasks with status, priority, deadline, and dependency info. |
61
+
62
  ---
63
 
64
  ## Observation Space
65
 
66
  ```python
67
  TaskManagerObservation(
68
+ success = True, # whether the last action succeeded
69
+ message = "Task 'Fix bug' added", # status message or error description
70
+ tasks = [...], # full task list snapshot
71
+ violations = [...], # list of rule violations this episode
72
+ reward = 0.45, # cumulative partial reward (0.0-1.0)
73
+ done = False, # True when episode goal is achieved
74
+ metadata = {
75
+ "difficulty": "Hard",
76
+ "step": 7,
77
+ "tasks_added": 5,
78
+ "tasks_completed": 3,
79
+ "deadline_misses": 0,
80
+ "dependency_violations": 0
81
+ }
82
  )
83
  ```
84
 
85
+ ### Task Object Fields
86
+ | Field | Type | Description |
87
+ |-------|------|-------------|
88
+ | `title` | str | Task name |
89
+ | `priority` | str | `"Low"` / `"Normal"` / `"High"` |
90
+ | `deadline` | str | ISO-8601 date or `"none"` |
91
+ | `depends_on` | list[str] | Prerequisite task titles |
92
+ | `completed` | bool | Whether the task is done |
93
+ | `deadline_missed` | bool | True if completed after deadline |
94
+ | `dependency_violation` | bool | True if completed before all prerequisites |
95
+
96
  ---
97
 
98
  ## Reward Function
99
 
100
+ ### Easy Mode
101
  | Event | Reward |
102
  |-------|--------|
103
+ | Each task added (up to 3) | +0.15 |
104
+ | Calling `list` | +0.20 |
105
+ | **Goal: >=2 tasks added + list called** | **1.0** |
 
106
 
107
+ ### Medium Mode
108
  | Event | Reward |
109
  |-------|--------|
110
+ | Each task added (up to 4) | +0.15 |
111
+ | Each task with explicit non-Normal priority | +0.10 |
112
+ | Each High-priority task completed **on time** | +0.20 |
113
+ | Deadline missed | **-0.25** |
114
+ | **Goal: 4 tasks, >=2 High, all High completed on time** | **1.0** |
115
 
116
+ ### Hard Mode
117
  | Event | Reward |
118
  |-------|--------|
119
+ | Each task added (up to 5) | +0.15 |
120
+ | Each task with non-Normal priority | +0.10 |
121
+ | Each task completed without any violation | +0.25 |
122
+ | Perfect run bonus (all done, zero violations) | **+0.10** |
123
+ | Dependency violation | **-0.30** |
124
+ | Deadline missed | **-0.25** |
125
+ | **Goal: 5 tasks, >=2 High, all completed, zero violations** | **1.0** |
126
 
127
  ---
128
 
 
132
  # Install dependencies
133
  uv sync
134
 
135
+ # Start the server
136
  uvicorn server.app:app --host 127.0.0.1 --port 8000
137
 
138
+ # In another terminal, run the inference demo
139
  python inference.py
140
  ```
141
 
142
  ---
143
 
144
+ ## Usage Examples
145
 
146
+ ### Easy Mode
147
  ```python
 
 
 
148
  env = OpenenvJayeshEnvironment()
149
+ obs = env.reset() # cycles to Easy
150
 
151
+ env.step(TaskManagerAction(command="add", title="Buy groceries", priority="Normal"))
152
+ # reward: 0.15
 
 
 
 
153
 
154
+ env.step(TaskManagerAction(command="add", title="Call dentist", priority="Low"))
155
+ # reward: 0.30
156
 
157
  obs = env.step(TaskManagerAction(command="list"))
158
+ # reward: 1.0, done: True
159
+ ```
160
+
161
+ ### Medium Mode
162
+ ```python
163
+ obs = env.reset() # cycles to Medium
164
+
165
+ env.step(TaskManagerAction(command="add", title="Fix critical bug", priority="High", deadline="2026-04-15"))
166
+ env.step(TaskManagerAction(command="add", title="Deploy hotfix", priority="High", deadline="2026-04-16"))
167
+ env.step(TaskManagerAction(command="add", title="Write release notes", priority="Normal", deadline="2026-04-22"))
168
+ env.step(TaskManagerAction(command="add", title="Team prep", priority="Low"))
169
+
170
+ env.step(TaskManagerAction(command="complete", title="Fix critical bug")) # +0.20 on-time
171
+ obs = env.step(TaskManagerAction(command="complete", title="Deploy hotfix")) # +0.20 -> done=True, reward=1.0
172
  ```
173
 
174
+ ### Hard Mode (with dependencies)
175
+ ```python
176
+ obs = env.reset() # cycles to Hard
177
+
178
+ env.step(TaskManagerAction(command="add", title="Reproduce bug", priority="High", deadline="2026-04-15"))
179
+ env.step(TaskManagerAction(command="add", title="Write tests", priority="Normal", deadline="2026-04-16"))
180
+ env.step(TaskManagerAction(command="add", title="Write fix", priority="High", deadline="2026-04-18",
181
+ depends_on=["Reproduce bug"]))
182
+ env.step(TaskManagerAction(command="add", title="Code review", priority="Normal", deadline="2026-04-20",
183
+ depends_on=["Write fix", "Write tests"]))
184
+ env.step(TaskManagerAction(command="add", title="Deploy", priority="Low", deadline="2026-04-22",
185
+ depends_on=["Code review"]))
186
+
187
+ # Complete in valid topological order
188
+ env.step(TaskManagerAction(command="complete", title="Reproduce bug")) # no deps
189
+ env.step(TaskManagerAction(command="complete", title="Write tests")) # no deps
190
+ env.step(TaskManagerAction(command="complete", title="Write fix")) # dep met
191
+ env.step(TaskManagerAction(command="complete", title="Code review")) # deps met
192
+ obs = env.step(TaskManagerAction(command="complete", title="Deploy")) # done=True, reward=1.0 + bonus
193
+ ```
194
+
195
+ ---
196
+
197
+ ## Environment Design Rationale
198
+
199
+ ### Why these three levels?
200
+ - **Easy** establishes baseline task CRUD competency -- can the agent perform basic operations?
201
+ - **Medium** adds time pressure and priority trade-offs -- a realistic proxy for real project management.
202
+ - **Hard** requires multi-step planning with constraint satisfaction -- approximates real dependency scheduling (e.g., CI/CD pipelines, project Gantt charts).
203
+
204
+ ### Why partial rewards?
205
+ Smooth, dense reward signals (+0.15 per task, +0.10 per priority, etc.) enable reinforcement learning agents to make meaningful progress even without solving the full episode. This is superior to sparse reward environments where only terminal success counts.
206
+
207
+ ### Why penalties?
208
+ - Deadline misses (-0.25) discourage agents from completing tasks arbitrarily late.
209
+ - Dependency violations (-0.30) teach agents that **order matters** -- a fundamental property of real-world task graphs.
210
+
211
  ---
212
 
213
  ## API Endpoints
214
 
215
  | URL | Description |
216
  |-----|-------------|
217
+ | `GET /health` | Health check |
218
+ | `POST /reset` | Start a new episode |
219
+ | `POST /step` | Execute an action |
220
+ | `GET /state` | Current episode metadata |
221
+ | `GET /docs` | Interactive Swagger UI |
222
 
223
  ---
224
 
 
226
 
227
  ```
228
  openenv_jayesh/
229
+ +-- Dockerfile
230
+ +-- openenv.yaml
231
+ +-- pyproject.toml
232
+ +-- models.py <- Action + Observation types
233
+ +-- client.py <- HTTP client helper
234
+ +-- inference.py <- End-to-end demo (all 3 levels)
235
+ +-- server/
236
+ +-- app.py <- FastAPI app entry point
237
+ +-- openenv_jayesh_environment.py <- Core environment logic
 
238
  ```
239
 
240
  ---
241
 
242
+ ## Deploy
243
 
244
  ```bash
245
  openenv push --repo-id jayesh20/openenv_jayesh
inference.py CHANGED
@@ -1,66 +1,147 @@
 
 
 
 
 
 
1
  import sys
2
  import os
3
- sys.path.append(os.path.dirname(os.path.abspath(__file__)))
 
4
 
5
  from client import OpenenvJayeshEnv
6
  from models import TaskManagerAction
7
 
8
- def run_easy_task(client):
9
- print("\n=== Scenario: Easy ===")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
  res = client.reset()
11
- default_msg = res.observation.message if hasattr(res.observation, 'message') else "No message"
12
- print(f"Goal: {default_msg}")
13
-
14
- res = client.step(TaskManagerAction(command="add", title="Buy groceries", priority="Normal"))
15
- print(f"Action 'add 1': {res.observation.message} | Score: {res.reward}")
16
- res = client.step(TaskManagerAction(command="add", title="Do laundry", priority="Low"))
17
- print(f"Action 'add 2': {res.observation.message} | Score: {res.reward}")
18
- res = client.step(TaskManagerAction(command="list"))
19
- print(f"Action 'list': {res.observation.message} | Score: {res.reward} | Done: {res.done}")
20
-
21
- def run_medium_task(client):
22
- print("\n=== Scenario: Medium ===")
 
 
 
 
 
 
 
 
23
  res = client.reset()
24
- default_msg = res.observation.message if hasattr(res.observation, 'message') else "No message"
25
- print(f"Goal: {default_msg}")
26
-
27
- res = client.step(TaskManagerAction(command="add", title="Write code", priority="Normal"))
28
- print(f"Action 'add Normal': {res.observation.message} | Score: {res.reward}")
29
- res = client.step(TaskManagerAction(command="add", title="Review PR", priority="High"))
30
- print(f"Action 'add High': {res.observation.message} | Score: {res.reward}")
31
- res = client.step(TaskManagerAction(command="add", title="Check emails", priority="Low"))
32
- print(f"Action 'add Low': {res.observation.message} | Score: {res.reward}")
33
-
34
- res = client.step(TaskManagerAction(command="complete", title="Review PR"))
35
- print(f"Action 'complete High': {res.observation.message} | Score: {res.reward} | Done: {res.done}")
36
-
37
- def run_hard_task(client):
38
- print("\n=== Scenario: Hard ===")
 
 
 
 
 
 
 
 
 
 
 
 
39
  res = client.reset()
40
- default_msg = res.observation.message if hasattr(res.observation, 'message') else "No message"
41
- print(f"Goal: {default_msg}")
42
-
43
- res = client.step(TaskManagerAction(command="add", title="Fix prod bug", priority="High"))
44
- print(f"Action 'add High 1': {res.observation.message} | Score: {res.reward}")
45
- res = client.step(TaskManagerAction(command="add", title="Write incident report", priority="High"))
46
- print(f"Action 'add High 2': {res.observation.message} | Score: {res.reward}")
47
- res = client.step(TaskManagerAction(command="add", title="Refactor", priority="Normal"))
48
- print(f"Action 'add Normal': {res.observation.message} | Score: {res.reward}")
49
- res = client.step(TaskManagerAction(command="add", title="Update docs", priority="Low"))
50
- print(f"Action 'add Low': {res.observation.message} | Score: {res.reward}")
51
-
52
- res = client.step(TaskManagerAction(command="complete", title="Fix prod bug"))
53
- print(f"Action 'complete High 1': {res.observation.message} | Score: {res.reward}")
54
- res = client.step(TaskManagerAction(command="complete", title="Write incident report"))
55
- print(f"Action 'complete High 2': {res.observation.message} | Score: {res.reward} | Done: {res.done}")
56
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
 
58
  if __name__ == "__main__":
59
- print("Connecting to OpenEnv Task Manager Server on http://127.0.0.1:8000...")
 
 
 
 
60
  try:
61
  with OpenenvJayeshEnv(base_url="http://127.0.0.1:8000").sync() as client:
62
- run_easy_task(client)
63
- run_medium_task(client)
64
- run_hard_task(client)
 
65
  except Exception as e:
66
- print(f"Error connecting to server. Is it running? {e}")
 
 
1
+ """
2
+ inference.py – Demonstration of the Smart Personal Task Manager OpenEnv.
3
+
4
+ Runs all three difficulty scenarios and prints step-by-step reward signals.
5
+ """
6
+
7
  import sys
8
  import os
9
+
10
+ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
11
 
12
  from client import OpenenvJayeshEnv
13
  from models import TaskManagerAction
14
 
15
+ # ---------------------------------------------------------------------------
16
+ # Helpers
17
+ # ---------------------------------------------------------------------------
18
+
19
+ def _step(client, action: TaskManagerAction, label: str):
20
+ res = client.step(action)
21
+ obs = res.observation
22
+ done_tag = " [DONE]" if res.done else ""
23
+ violation_tag = ""
24
+ if hasattr(obs, "violations") and obs.violations:
25
+ violation_tag = f"\n Violations: {obs.violations[-1]}"
26
+ print(
27
+ f" {label:<40} | Score: {res.reward:.3f}{done_tag}"
28
+ f"\n -> {obs.message}{violation_tag}"
29
+ )
30
+ return res
31
+
32
+
33
+ # ---------------------------------------------------------------------------
34
+ # Easy scenario
35
+ # ---------------------------------------------------------------------------
36
+
37
+ def run_easy(client):
38
+ print("\n" + "=" * 70)
39
+ print("SCENARIO 1 – EASY MODE")
40
+ print("=" * 70)
41
  res = client.reset()
42
+ print(f"\nGoal:\n{res.observation.message}\n")
43
+
44
+ today = "2026-04-15"
45
+
46
+ _step(client, TaskManagerAction(command="add", title="Buy groceries", priority="Normal", deadline=today), "add 'Buy groceries' Normal")
47
+ _step(client, TaskManagerAction(command="add", title="Call the dentist", priority="Low"), "add 'Call the dentist' Low")
48
+ _step(client, TaskManagerAction(command="add", title="Review meeting notes", priority="Normal"), "add 'Review meeting notes'")
49
+ _step(client, TaskManagerAction(command="list"), "list (triggers goal completion)")
50
+
51
+ print()
52
+
53
+
54
+ # ---------------------------------------------------------------------------
55
+ # Medium scenario
56
+ # ---------------------------------------------------------------------------
57
+
58
+ def run_medium(client):
59
+ print("\n" + "=" * 70)
60
+ print("SCENARIO 2 – MEDIUM MODE")
61
+ print("=" * 70)
62
  res = client.reset()
63
+ print(f"\nGoal:\n{res.observation.message}\n")
64
+
65
+ # Use future deadlines so completions are on-time
66
+ today = "2026-04-15"
67
+ tomorrow = "2026-04-16"
68
+ next_week = "2026-04-22"
69
+
70
+ _step(client, TaskManagerAction(command="add", title="Fix critical bug", priority="High", deadline=today), "add 'Fix critical bug' High")
71
+ _step(client, TaskManagerAction(command="add", title="Deploy hotfix", priority="High", deadline=tomorrow), "add 'Deploy hotfix' High")
72
+ _step(client, TaskManagerAction(command="add", title="Write release notes", priority="Normal", deadline=next_week), "add 'Write release notes' Normal")
73
+ _step(client, TaskManagerAction(command="add", title="Team standup prep", priority="Low"), "add 'Team standup prep' Low")
74
+
75
+ # Complete High-priority tasks on time
76
+ _step(client, TaskManagerAction(command="complete", title="Fix critical bug"), "complete 'Fix critical bug' [High, on-time]")
77
+ _step(client, TaskManagerAction(command="complete", title="Deploy hotfix"), "complete 'Deploy hotfix' [High, on-time → GOAL]")
78
+
79
+ print()
80
+
81
+
82
+ # ---------------------------------------------------------------------------
83
+ # Hard scenario
84
+ # ---------------------------------------------------------------------------
85
+
86
+ def run_hard(client):
87
+ print("\n" + "=" * 70)
88
+ print("SCENARIO 3 – HARD MODE (dependencies + deadlines)")
89
+ print("=" * 70)
90
  res = client.reset()
91
+ print(f"\nGoal:\n{res.observation.message}\n")
92
+
93
+ t1 = "2026-04-15"
94
+ t2 = "2026-04-16"
95
+ t3 = "2026-04-18"
96
+ t4 = "2026-04-20"
97
+ t5 = "2026-04-22"
 
 
 
 
 
 
 
 
 
98
 
99
+ # Build dependency graph:
100
+ # Reproduce bug ──► Write fix ──► Code review ──► Deploy
101
+ # Write tests ──► Code review
102
+ _step(client, TaskManagerAction(command="add", title="Reproduce bug", priority="High", deadline=t1), "add 'Reproduce bug' High")
103
+ _step(client, TaskManagerAction(command="add", title="Write tests", priority="Normal", deadline=t2), "add 'Write tests' Normal")
104
+ _step(client, TaskManagerAction(command="add", title="Write fix", priority="High", deadline=t3, depends_on=["Reproduce bug"]), "add 'Write fix' High (depends: Reproduce bug)")
105
+ _step(client, TaskManagerAction(command="add", title="Code review", priority="Normal", deadline=t4, depends_on=["Write fix", "Write tests"]), "add 'Code review' Normal (depends: Write fix, Write tests)")
106
+ _step(client, TaskManagerAction(command="add", title="Deploy to production", priority="Low", deadline=t5, depends_on=["Code review"]), "add 'Deploy to prod' Low (depends: Code review)")
107
+
108
+ print()
109
+ print(" -- Completing in CORRECT topological order –-")
110
+ _step(client, TaskManagerAction(command="complete", title="Reproduce bug"), "complete 'Reproduce bug' [no deps, on-time]")
111
+ _step(client, TaskManagerAction(command="complete", title="Write tests"), "complete 'Write tests' [no deps, on-time]")
112
+ _step(client, TaskManagerAction(command="complete", title="Write fix"), "complete 'Write fix' [dep met, on-time]")
113
+ _step(client, TaskManagerAction(command="complete", title="Code review"), "complete 'Code review' [deps met, on-time]")
114
+ _step(client, TaskManagerAction(command="complete", title="Deploy to production"), "complete 'Deploy to prod' [all deps met → GOAL]")
115
+
116
+ print()
117
+ print(" -- Violations demo (new reset, same Hard mode) --")
118
+ res = client.reset() # same Hard cycle
119
+ print(f" (Re-reset into: {res.observation.message.splitlines()[0]})")
120
+
121
+ _step(client, TaskManagerAction(command="add", title="Task A", priority="High", deadline=t1), "add 'Task A' High")
122
+ _step(client, TaskManagerAction(command="add", title="Task B", priority="Normal", deadline=t2, depends_on=["Task A"]), "add 'Task B' Normal (depends: Task A)")
123
+ res = _step(client, TaskManagerAction(command="complete", title="Task B"), "WRONG: complete 'Task B' before 'Task A'")
124
+ print(f" Score after violation: {res.reward:.3f}")
125
+
126
+ print()
127
+
128
+
129
+ # ---------------------------------------------------------------------------
130
+ # Entry point
131
+ # ---------------------------------------------------------------------------
132
 
133
  if __name__ == "__main__":
134
+ print("=" * 70)
135
+ print("Smart Personal Task Manager – OpenEnv Inference Demo")
136
+ print("Connecting to http://127.0.0.1:8000 ...")
137
+ print("=" * 70)
138
+
139
  try:
140
  with OpenenvJayeshEnv(base_url="http://127.0.0.1:8000").sync() as client:
141
+ run_easy(client)
142
+ run_medium(client)
143
+ run_hard(client)
144
+ print("\nAll scenarios completed successfully.")
145
  except Exception as e:
146
+ print(f"\nError: Could not connect to server. Is it running?\n {e}")
147
+ sys.exit(1)
models.py CHANGED
@@ -2,22 +2,69 @@
2
  # All rights reserved.
3
 
4
  """
5
- Data models for the Openenv Jayesh Task Manager Environment.
 
 
 
 
 
6
  """
7
 
8
  from typing import Optional, List, Dict, Any
9
  from openenv.core.env_server.types import Action, Observation
10
  from pydantic import Field
11
 
 
12
  class TaskManagerAction(Action):
13
- """Action for the Task Manager."""
14
- command: str = Field(..., description="The command to execute: 'add', 'complete', 'list'")
15
- title: Optional[str] = Field(default=None, description="Task title (for 'add' or 'complete')")
16
- priority: Optional[str] = Field(default="Normal", description="Task priority (for 'add')")
17
- deadline: Optional[str] = Field(default=None, description="Task deadline (for 'add')")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
 
19
  class TaskManagerObservation(Observation):
20
- """Observation from the Task Manager."""
21
- success: bool = Field(default=True, description="Whether the action succeeded")
22
- message: str = Field(default="", description="System message or error")
23
- tasks: List[Dict[str, Any]] = Field(default_factory=list, description="List of tasks in the system")
 
 
 
 
 
 
 
 
 
 
 
 
 
2
  # All rights reserved.
3
 
4
  """
5
+ Data models for the Smart Personal Task Manager OpenEnv Environment.
6
+
7
+ Supports three difficulty levels:
8
+ Easy – add tasks & list them
9
+ Medium – priorities + deadlines; complete High-priority tasks before deadline
10
+ Hard – priorities + deadlines + dependencies; complete in valid topological order
11
  """
12
 
13
  from typing import Optional, List, Dict, Any
14
  from openenv.core.env_server.types import Action, Observation
15
  from pydantic import Field
16
 
17
+
18
  class TaskManagerAction(Action):
19
+ """Action for the Smart Personal Task Manager."""
20
+
21
+ command: str = Field(
22
+ ...,
23
+ description=(
24
+ "Command to execute. One of: 'add', 'complete', 'list'. "
25
+ "'add' – create a new task. "
26
+ "'complete' – mark an existing task as done. "
27
+ "'list' – display all current tasks."
28
+ ),
29
+ )
30
+ title: Optional[str] = Field(
31
+ default=None,
32
+ description="Human-readable task title. Required for 'add' and 'complete'.",
33
+ )
34
+ priority: Optional[str] = Field(
35
+ default="Normal",
36
+ description="Task priority. One of: 'Low', 'Normal', 'High'. Used for 'add'.",
37
+ )
38
+ deadline: Optional[str] = Field(
39
+ default=None,
40
+ description=(
41
+ "Optional ISO-8601 date string (YYYY-MM-DD) indicating when the task "
42
+ "must be completed. Used for 'add'. Relevant in Medium and Hard modes."
43
+ ),
44
+ )
45
+ depends_on: Optional[List[str]] = Field(
46
+ default=None,
47
+ description=(
48
+ "List of task titles that must be completed BEFORE this task can be "
49
+ "completed. Used for 'add'. Relevant in Hard mode only."
50
+ ),
51
+ )
52
+
53
 
54
  class TaskManagerObservation(Observation):
55
+ """Observation returned after each step in the Task Manager."""
56
+
57
+ success: bool = Field(default=True, description="Whether the last action succeeded.")
58
+ message: str = Field(default="", description="System status message or error description.")
59
+ tasks: List[Dict[str, Any]] = Field(
60
+ default_factory=list,
61
+ description=(
62
+ "Snapshot of all tasks. Each entry contains: "
63
+ "title, priority, deadline, depends_on, completed, "
64
+ "deadline_missed (bool), dependency_violation (bool)."
65
+ ),
66
+ )
67
+ violations: List[str] = Field(
68
+ default_factory=list,
69
+ description="List of rule violations encountered this episode (deadline misses, dependency order errors).",
70
+ )
server/openenv_jayesh_environment.py CHANGED
@@ -2,11 +2,36 @@
2
  # All rights reserved.
3
 
4
  """
5
- Openenv Jayesh Environment Implementation.
6
- A Simple Task Manager Environment.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
  """
8
 
9
- from typing import Optional, Any
 
 
 
10
  from uuid import uuid4
11
 
12
  from openenv.core.env_server.interfaces import Environment
@@ -18,25 +43,82 @@ except (ModuleNotFoundError, ImportError):
18
  from models import TaskManagerAction, TaskManagerObservation
19
 
20
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
  class OpenenvJayeshEnvironment(Environment):
22
- """A Task Manager environment with 3 difficulty modes."""
 
 
 
 
 
 
23
 
24
  SUPPORTS_CONCURRENT_SESSIONS: bool = True
25
 
26
- def __init__(self):
 
 
 
 
27
  super().__init__()
28
  self._state = State(episode_id=str(uuid4()), step_count=0)
29
  self._reset_count = 0
30
  self._scenarios = ["Easy", "Medium", "Hard"]
31
-
32
- # Internal state
33
- self.tasks = []
34
- self.difficulty = "Easy"
35
- self.goal_completed = False
36
- self.tasks_added = 0
37
- self.high_priority_added = 0
38
- self.high_priority_completed = 0
39
- self.list_called = False
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40
 
41
  def reset(
42
  self,
@@ -44,141 +126,337 @@ class OpenenvJayeshEnvironment(Environment):
44
  episode_id: Optional[str] = None,
45
  **kwargs: Any,
46
  ) -> TaskManagerObservation:
47
- """Reset the environment."""
48
  self._state = State(episode_id=episode_id or str(uuid4()), step_count=0)
49
-
 
50
  idx = self._reset_count % len(self._scenarios)
51
  self.difficulty = self._scenarios[idx]
52
  self._reset_count += 1
53
-
54
- self.tasks = []
55
- self.goal_completed = False
56
- self.tasks_added = 0
57
- self.high_priority_added = 0
58
- self.high_priority_completed = 0
59
- self.list_called = False
60
-
61
- message = f"Task Manager started in {self.difficulty} mode. "
62
  if self.difficulty == "Easy":
63
- message += "Goal: Add 2 tasks and list them."
 
 
 
 
 
 
 
64
  elif self.difficulty == "Medium":
65
- message += "Goal: Add 3 tasks (mixed priorities) and complete all High priority ones."
66
- elif self.difficulty == "Hard":
67
- message += "Goal: Add 4 tasks (at least 2 High) and complete at least 2 High priority tasks."
68
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
69
  return TaskManagerObservation(
70
  success=True,
71
- message=message,
72
- tasks=self.tasks,
 
73
  done=False,
74
- reward=0.0
75
  )
76
 
 
 
 
 
77
  def step(
78
  self,
79
  action: TaskManagerAction,
80
  timeout_s: Optional[float] = None,
81
  **kwargs: Any,
82
- ) -> TaskManagerObservation: # type: ignore[override]
83
- """Execute a step."""
84
  self._state.step_count += 1
85
-
86
- cmd = action.command.lower()
87
  success = True
88
  message = ""
89
-
90
  if cmd == "add":
91
- if not action.title:
92
- success = False
93
- message = "Title is required to add a task."
94
- else:
95
- prio = action.priority or "Normal"
96
- new_task = {
97
- "title": action.title,
98
- "priority": prio,
99
- "deadline": action.deadline or "N/A",
100
- "completed": False
101
- }
102
- self.tasks.append(new_task)
103
- self.tasks_added += 1
104
- if prio == "High":
105
- self.high_priority_added += 1
106
- message = f"Task '{action.title}' added successfully. (Priority: {prio})"
107
  elif cmd == "complete":
108
- if not action.title:
109
- success = False
110
- message = "Title is required to complete a task."
111
- else:
112
- found = False
113
- for t in self.tasks:
114
- if t["title"] == action.title:
115
- if not t["completed"]:
116
- t["completed"] = True
117
- if t["priority"] == "High":
118
- self.high_priority_completed += 1
119
- found = True
120
- message = f"Task '{action.title}' marked as completed."
121
- break
122
- if not found:
123
- success = False
124
- message = f"Task '{action.title}' not found."
125
  elif cmd == "list":
126
  self.list_called = True
127
- message = f"Listed {len(self.tasks)} current tasks."
128
  else:
129
  success = False
130
- message = f"Unknown command: '{cmd}'"
131
-
132
  reward = self._calculate_reward()
133
- done = self.goal_completed
134
-
135
  return TaskManagerObservation(
136
  success=success,
137
  message=message,
138
- tasks=self.tasks,
139
- done=done,
 
140
  reward=reward,
141
- metadata={"difficulty": self.difficulty, "step": self._state.step_count}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
142
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
143
 
144
  def _calculate_reward(self) -> float:
145
- reward = 0.0
146
  self.goal_completed = False
147
-
 
148
  if self.difficulty == "Easy":
149
- # Add 2 tasks (0.4 each) + list them (0.2)
150
- reward += min(0.8, self.tasks_added * 0.4)
151
- if self.list_called:
152
- reward += 0.2
153
-
154
- if self.tasks_added >= 2 and self.list_called:
155
- reward = 1.0
156
- self.goal_completed = True
157
-
158
  elif self.difficulty == "Medium":
159
- # Add 3 tasks (0.2 each) + Add High (0.1) + Complete High (0.3 ratio)
160
- reward += min(0.6, self.tasks_added * 0.2)
161
- if self.high_priority_added >= 1:
162
- reward += 0.1
163
- completion_ratio = self.high_priority_completed / self.high_priority_added
164
- reward += min(0.3, completion_ratio * 0.3)
165
-
166
- if self.tasks_added >= 3 and self.high_priority_added >= 1:
167
- if self.high_priority_completed >= self.high_priority_added:
168
- reward = 1.0
169
- self.goal_completed = True
170
-
171
- elif self.difficulty == "Hard":
172
- # Add 4 tasks (0.1 each) + Add 2 High (0.1 each) + Complete 2 High (0.2 each)
173
- reward += min(0.4, self.tasks_added * 0.1)
174
- reward += min(0.2, self.high_priority_added * 0.1)
175
- reward += min(0.4, self.high_priority_completed * 0.2)
176
-
177
- if self.tasks_added >= 4 and self.high_priority_added >= 2 and self.high_priority_completed >= 2:
178
- reward = 1.0
179
- self.goal_completed = True
180
-
181
- return round(min(1.0, reward), 2)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
182
 
183
  @property
184
  def state(self) -> State:
 
2
  # All rights reserved.
3
 
4
  """
5
+ Smart Personal Task Manager – OpenEnv Environment Implementation.
6
+
7
+ Three difficulty tiers:
8
+
9
+ Easy (reset 0, 3, 6 …)
10
+ Goal: Add 2-3 tasks of any priority, then call 'list'.
11
+ Reward: +0.15 per task added (up to 3), +0.20 for listing. Full 1.0 on completion.
12
+
13
+ Medium (reset 1, 4, 7 …)
14
+ Goal: Add 4 tasks with mixed priorities AND deadlines.
15
+ Complete ALL High-priority tasks before their deadlines.
16
+ Reward: +0.15 per task added (up to 4), +0.10 per correct priority label,
17
+ +0.20 per High-priority task completed on time.
18
+ −0.25 penalty per deadline miss.
19
+ Full 1.0 on completion.
20
+
21
+ Hard (reset 2, 5, 8 …)
22
+ Goal: Add 5 tasks with priorities, deadlines, AND dependencies.
23
+ Complete tasks in valid dependency order; respect all deadlines.
24
+ Reward: +0.15 per task added (up to 5), +0.10 per correct priority,
25
+ +0.25 per task completed without violation.
26
+ Bonus +0.10 for achieving perfect (optimal) topological ordering.
27
+ −0.30 per dependency violation, −0.25 per deadline miss.
28
+ Full 1.0 on perfect completion.
29
  """
30
 
31
+ from __future__ import annotations
32
+
33
+ from datetime import date
34
+ from typing import Any, Dict, List, Optional
35
  from uuid import uuid4
36
 
37
  from openenv.core.env_server.interfaces import Environment
 
43
  from models import TaskManagerAction, TaskManagerObservation
44
 
45
 
46
+ # ---------------------------------------------------------------------------
47
+ # Helpers
48
+ # ---------------------------------------------------------------------------
49
+
50
+ VALID_PRIORITIES = {"Low", "Normal", "High"}
51
+ PRIORITY_RANK = {"Low": 0, "Normal": 1, "High": 2}
52
+
53
+
54
+ def _parse_date(dt_str: Optional[str]) -> Optional[date]:
55
+ """Parse an ISO-8601 date string; return None on failure."""
56
+ if not dt_str:
57
+ return None
58
+ try:
59
+ return date.fromisoformat(dt_str)
60
+ except ValueError:
61
+ return None
62
+
63
+
64
+ def _today() -> date:
65
+ return date.today()
66
+
67
+
68
+ # ---------------------------------------------------------------------------
69
+ # Environment
70
+ # ---------------------------------------------------------------------------
71
+
72
  class OpenenvJayeshEnvironment(Environment):
73
+ """
74
+ Smart Personal Task Manager with three distinct difficulty levels.
75
+
76
+ Easy → add tasks + list
77
+ Medium → deadlines + priority management
78
+ Hard → deadlines + priority + dependency ordering
79
+ """
80
 
81
  SUPPORTS_CONCURRENT_SESSIONS: bool = True
82
 
83
+ # ------------------------------------------------------------------
84
+ # Lifecycle
85
+ # ------------------------------------------------------------------
86
+
87
+ def __init__(self) -> None:
88
  super().__init__()
89
  self._state = State(episode_id=str(uuid4()), step_count=0)
90
  self._reset_count = 0
91
  self._scenarios = ["Easy", "Medium", "Hard"]
92
+ self._init_episode_state()
93
+
94
+ def _init_episode_state(self) -> None:
95
+ """Zero all mutable per-episode state."""
96
+ self.tasks: List[Dict[str, Any]] = [] # ordered list of task dicts
97
+ self.difficulty: str = "Easy"
98
+ self.goal_completed: bool = False
99
+
100
+ # Counters
101
+ self.tasks_added: int = 0
102
+ self.tasks_completed: int = 0
103
+ self.high_tasks_added: int = 0
104
+ self.high_tasks_completed_on_time: int = 0
105
+ self.deadline_misses: int = 0
106
+ self.dependency_violations: int = 0
107
+ self.completion_order: List[str] = [] # titles in completion order
108
+
109
+ # Episode-level flag
110
+ self.list_called: bool = False
111
+
112
+ # Violations log (for observation)
113
+ self.violations: List[str] = []
114
+
115
+ # Target counts per difficulty
116
+ self.target_tasks: int = 2
117
+ self.target_high: int = 0
118
+
119
+ # ------------------------------------------------------------------
120
+ # reset
121
+ # ------------------------------------------------------------------
122
 
123
  def reset(
124
  self,
 
126
  episode_id: Optional[str] = None,
127
  **kwargs: Any,
128
  ) -> TaskManagerObservation:
 
129
  self._state = State(episode_id=episode_id or str(uuid4()), step_count=0)
130
+ self._init_episode_state()
131
+
132
  idx = self._reset_count % len(self._scenarios)
133
  self.difficulty = self._scenarios[idx]
134
  self._reset_count += 1
135
+
 
 
 
 
 
 
 
 
136
  if self.difficulty == "Easy":
137
+ self.target_tasks = 2
138
+ self.target_high = 0
139
+ msg = (
140
+ "=== EASY MODE ===\n"
141
+ "Goal: Add 2-3 tasks of any priority, then call 'list' to review them.\n"
142
+ "Rewards: +0.15 per task added (max 3), +0.20 for calling list.\n"
143
+ "Complete the goal to receive full reward (1.0)."
144
+ )
145
  elif self.difficulty == "Medium":
146
+ self.target_tasks = 4
147
+ self.target_high = 2
148
+ msg = (
149
+ "=== MEDIUM MODE ===\n"
150
+ "Goal: Add 4 tasks with priorities AND deadlines.\n"
151
+ " Complete ALL High-priority tasks before their deadlines.\n"
152
+ "Tips: Include at least 2 High-priority tasks.\n"
153
+ " Use deadline='YYYY-MM-DD' (today or future date for on-time credit).\n"
154
+ "Rewards: +0.15/task added, +0.10/correct priority, +0.20/High completed on time.\n"
155
+ "Penalty: -0.25 per deadline miss."
156
+ )
157
+ else: # Hard
158
+ self.target_tasks = 5
159
+ self.target_high = 2
160
+ msg = (
161
+ "=== HARD MODE ===\n"
162
+ "Goal: Add 5 tasks with priorities, deadlines, AND dependencies.\n"
163
+ " Complete tasks in valid dependency order (dependencies first).\n"
164
+ " Respect all deadlines.\n"
165
+ "Tips: Use depends_on=['Task Title'] when adding a dependent task.\n"
166
+ " Complete prerequisite tasks before their dependents.\n"
167
+ "Rewards: +0.15/task, +0.10/priority, +0.25/completion without violation.\n"
168
+ " +0.10 bonus for perfect topological ordering.\n"
169
+ "Penalty: -0.30/dependency violation, -0.25/deadline miss."
170
+ )
171
+
172
  return TaskManagerObservation(
173
  success=True,
174
+ message=msg,
175
+ tasks=[],
176
+ violations=[],
177
  done=False,
178
+ reward=0.0,
179
  )
180
 
181
+ # ------------------------------------------------------------------
182
+ # step
183
+ # ------------------------------------------------------------------
184
+
185
  def step(
186
  self,
187
  action: TaskManagerAction,
188
  timeout_s: Optional[float] = None,
189
  **kwargs: Any,
190
+ ) -> TaskManagerObservation:
 
191
  self._state.step_count += 1
192
+ cmd = (action.command or "").strip().lower()
 
193
  success = True
194
  message = ""
195
+
196
  if cmd == "add":
197
+ success, message = self._handle_add(action)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
198
  elif cmd == "complete":
199
+ success, message = self._handle_complete(action)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
200
  elif cmd == "list":
201
  self.list_called = True
202
+ message = self._format_task_list()
203
  else:
204
  success = False
205
+ message = f"Unknown command '{cmd}'. Valid commands: 'add', 'complete', 'list'."
206
+
207
  reward = self._calculate_reward()
208
+
 
209
  return TaskManagerObservation(
210
  success=success,
211
  message=message,
212
+ tasks=list(self.tasks),
213
+ violations=list(self.violations),
214
+ done=self.goal_completed,
215
  reward=reward,
216
+ metadata={
217
+ "difficulty": self.difficulty,
218
+ "step": self._state.step_count,
219
+ "tasks_added": self.tasks_added,
220
+ "tasks_completed": self.tasks_completed,
221
+ "deadline_misses": self.deadline_misses,
222
+ "dependency_violations": self.dependency_violations,
223
+ },
224
+ )
225
+
226
+ # ------------------------------------------------------------------
227
+ # Command handlers
228
+ # ------------------------------------------------------------------
229
+
230
+ def _handle_add(self, action: TaskManagerAction):
231
+ if not action.title or not action.title.strip():
232
+ return False, "Error: 'title' is required for the 'add' command."
233
+
234
+ title = action.title.strip()
235
+
236
+ # Duplicate check
237
+ if any(t["title"] == title for t in self.tasks):
238
+ return False, f"Error: A task titled '{title}' already exists."
239
+
240
+ priority = (action.priority or "Normal").strip()
241
+ if priority not in VALID_PRIORITIES:
242
+ priority = "Normal"
243
+
244
+ deadline_str = action.deadline
245
+ deadline_obj = _parse_date(deadline_str)
246
+
247
+ depends_on: List[str] = []
248
+ if action.depends_on:
249
+ for dep in action.depends_on:
250
+ dep = dep.strip()
251
+ if dep and any(t["title"] == dep for t in self.tasks):
252
+ depends_on.append(dep)
253
+ elif dep:
254
+ return (
255
+ False,
256
+ f"Error: Dependency '{dep}' does not exist yet. "
257
+ "Add prerequisite tasks first.",
258
+ )
259
+
260
+ task: Dict[str, Any] = {
261
+ "title": title,
262
+ "priority": priority,
263
+ "deadline": deadline_str or "none",
264
+ "depends_on": depends_on,
265
+ "completed": False,
266
+ "deadline_missed": False,
267
+ "dependency_violation": False,
268
+ "added_step": self._state.step_count,
269
+ }
270
+ self.tasks.append(task)
271
+ self.tasks_added += 1
272
+ if priority == "High":
273
+ self.high_tasks_added += 1
274
+
275
+ msg = (
276
+ f"Task added: '{title}' | Priority: {priority}"
277
+ + (f" | Deadline: {deadline_str}" if deadline_str else "")
278
+ + (f" | Depends on: {depends_on}" if depends_on else "")
279
  )
280
+ return True, msg
281
+
282
+ def _handle_complete(self, action: TaskManagerAction):
283
+ if not action.title or not action.title.strip():
284
+ return False, "Error: 'title' is required for the 'complete' command."
285
+
286
+ title = action.title.strip()
287
+ task = next((t for t in self.tasks if t["title"] == title), None)
288
+
289
+ if task is None:
290
+ return False, f"Error: Task '{title}' not found."
291
+ if task["completed"]:
292
+ return False, f"Task '{title}' is already completed."
293
+
294
+ # ---- Dependency check ----
295
+ dep_violation = False
296
+ unmet = [
297
+ dep for dep in task["depends_on"]
298
+ if not any(t["title"] == dep and t["completed"] for t in self.tasks)
299
+ ]
300
+ if unmet:
301
+ dep_violation = True
302
+ task["dependency_violation"] = True
303
+ self.dependency_violations += 1
304
+ violation_msg = (
305
+ f"DEPENDENCY VIOLATION: Completed '{title}' before prerequisites: {unmet}. "
306
+ "Penalty applied."
307
+ )
308
+ self.violations.append(violation_msg)
309
+
310
+ # ---- Deadline check ----
311
+ deadline_missed = False
312
+ dl = _parse_date(task["deadline"])
313
+ if dl is not None and _today() > dl:
314
+ deadline_missed = True
315
+ task["deadline_missed"] = True
316
+ self.deadline_misses += 1
317
+ violation_msg = (
318
+ f"DEADLINE MISSED: '{title}' was due {task['deadline']} "
319
+ f"but completed on {_today().isoformat()}. Penalty applied."
320
+ )
321
+ self.violations.append(violation_msg)
322
+
323
+ # ---- Mark completed ----
324
+ task["completed"] = True
325
+ self.tasks_completed += 1
326
+ self.completion_order.append(title)
327
+
328
+ if task["priority"] == "High" and not deadline_missed:
329
+ self.high_tasks_completed_on_time += 1
330
+
331
+ msg_parts = [f"Task '{title}' marked complete."]
332
+ if dep_violation:
333
+ msg_parts.append("(!) Dependency violation penalty applied.")
334
+ if deadline_missed:
335
+ msg_parts.append("(!) Deadline miss penalty applied.")
336
+ if not dep_violation and not deadline_missed:
337
+ msg_parts.append("Clean completion — no penalties.")
338
+
339
+ return True, " ".join(msg_parts)
340
+
341
+ # ------------------------------------------------------------------
342
+ # Reward calculation
343
+ # ------------------------------------------------------------------
344
 
345
  def _calculate_reward(self) -> float:
 
346
  self.goal_completed = False
347
+ reward = 0.0
348
+
349
  if self.difficulty == "Easy":
350
+ reward = self._reward_easy()
 
 
 
 
 
 
 
 
351
  elif self.difficulty == "Medium":
352
+ reward = self._reward_medium()
353
+ else:
354
+ reward = self._reward_hard()
355
+
356
+ return round(min(1.0, max(0.0, reward)), 3)
357
+
358
+ def _reward_easy(self) -> float:
359
+ r = 0.0
360
+ # +0.15 per task added, up to 3 tasks
361
+ r += min(3, self.tasks_added) * 0.15
362
+ # +0.20 for calling list
363
+ if self.list_called:
364
+ r += 0.20
365
+ # Goal: ≥2 tasks added + list called
366
+ if self.tasks_added >= 2 and self.list_called:
367
+ r = 1.0
368
+ self.goal_completed = True
369
+ return r
370
+
371
+ def _reward_medium(self) -> float:
372
+ r = 0.0
373
+ # +0.15 per task added, up to 4
374
+ r += min(4, self.tasks_added) * 0.15
375
+ # +0.10 per task that has an explicit priority label (not default "Normal" by omission)
376
+ explicit_priority_tasks = sum(
377
+ 1 for t in self.tasks
378
+ if t["priority"] != "Normal" or t.get("priority_explicit", False)
379
+ )
380
+ r += min(4, explicit_priority_tasks) * 0.10
381
+ # +0.20 per High-priority task completed on time
382
+ r += self.high_tasks_completed_on_time * 0.20
383
+ # Penalties
384
+ r -= self.deadline_misses * 0.25
385
+ # Goal: ≥4 tasks, ≥2 High added, ALL High completed on time, no deadline misses
386
+ high_tasks = [t for t in self.tasks if t["priority"] == "High"]
387
+ all_high_done = all(t["completed"] and not t["deadline_missed"] for t in high_tasks)
388
+ if (
389
+ self.tasks_added >= 4
390
+ and self.high_tasks_added >= 2
391
+ and all_high_done
392
+ and self.deadline_misses == 0
393
+ ):
394
+ r = 1.0
395
+ self.goal_completed = True
396
+ return r
397
+
398
+ def _reward_hard(self) -> float:
399
+ r = 0.0
400
+ # +0.15 per task added, up to 5
401
+ r += min(5, self.tasks_added) * 0.15
402
+ # +0.10 per task with non-Normal or explicit priority
403
+ explicit_priority_tasks = sum(
404
+ 1 for t in self.tasks if t["priority"] != "Normal"
405
+ )
406
+ r += min(5, explicit_priority_tasks) * 0.10
407
+ # +0.25 per task completed without any violation
408
+ clean_completions = sum(
409
+ 1 for t in self.tasks
410
+ if t["completed"] and not t["deadline_missed"] and not t["dependency_violation"]
411
+ )
412
+ r += clean_completions * 0.25
413
+ # Penalty
414
+ r -= self.dependency_violations * 0.30
415
+ r -= self.deadline_misses * 0.25
416
+ # Bonus: optimal ordering (no violations at all + all done)
417
+ all_done = all(t["completed"] for t in self.tasks)
418
+ if all_done and self.dependency_violations == 0 and self.deadline_misses == 0:
419
+ r += 0.10 # perfect-run bonus
420
+ # Goal: ≥5 tasks, ≥2 High, all completed, zero violations
421
+ high_tasks = [t for t in self.tasks if t["priority"] == "High"]
422
+ if (
423
+ self.tasks_added >= 5
424
+ and self.high_tasks_added >= 2
425
+ and len(self.tasks) == self.tasks_completed
426
+ and self.dependency_violations == 0
427
+ and self.deadline_misses == 0
428
+ ):
429
+ r = 1.0
430
+ self.goal_completed = True
431
+ return r
432
+
433
+ # ------------------------------------------------------------------
434
+ # Helpers
435
+ # ------------------------------------------------------------------
436
+
437
+ def _format_task_list(self) -> str:
438
+ if not self.tasks:
439
+ return "No tasks in the system."
440
+ lines = [f"Current tasks ({len(self.tasks)} total):"]
441
+ for i, t in enumerate(self.tasks, 1):
442
+ status = "DONE" if t["completed"] else "PENDING"
443
+ flags = []
444
+ if t.get("deadline_missed"):
445
+ flags.append("LATE")
446
+ if t.get("dependency_violation"):
447
+ flags.append("DEP-VIOLATION")
448
+ flag_str = f" [{', '.join(flags)}]" if flags else ""
449
+ dep_str = f" | Deps: {t['depends_on']}" if t["depends_on"] else ""
450
+ dl_str = f" | Due: {t['deadline']}" if t["deadline"] != "none" else ""
451
+ lines.append(
452
+ f" {i}. [{status}]{flag_str} {t['title']} "
453
+ f"(Priority: {t['priority']}{dl_str}{dep_str})"
454
+ )
455
+ return "\n".join(lines)
456
+
457
+ # ------------------------------------------------------------------
458
+ # State property
459
+ # ------------------------------------------------------------------
460
 
461
  @property
462
  def state(self) -> State:
validate_final.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ [OK] openenv_jayesh: Ready for multi-mode deployment