Spaces:
Sleeping
Sleeping
nadellaroshni commited on
Commit ·
8034313
1
Parent(s): 2d35d5a
changes
Browse files- env/models.py +11 -0
- env/task_catalog.py +55 -0
- openenv.yaml +22 -3
- server/app.py +9 -1
env/models.py
CHANGED
|
@@ -155,6 +155,7 @@ class StepResult(BaseModel):
|
|
| 155 |
|
| 156 |
class ResetRequest(BaseModel):
|
| 157 |
difficulty: Difficulty = Difficulty.MEDIUM
|
|
|
|
| 158 |
audio_path: Optional[str] = None
|
| 159 |
transcript_override: Optional[str] = None
|
| 160 |
|
|
@@ -201,3 +202,13 @@ class PlanResponse(BaseModel):
|
|
| 201 |
breakdown: Dict[str, float]
|
| 202 |
summary: str
|
| 203 |
final_board: str
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 155 |
|
| 156 |
class ResetRequest(BaseModel):
|
| 157 |
difficulty: Difficulty = Difficulty.MEDIUM
|
| 158 |
+
task: Optional[str] = None
|
| 159 |
audio_path: Optional[str] = None
|
| 160 |
transcript_override: Optional[str] = None
|
| 161 |
|
|
|
|
| 202 |
breakdown: Dict[str, float]
|
| 203 |
summary: str
|
| 204 |
final_board: str
|
| 205 |
+
|
| 206 |
+
|
| 207 |
+
class TaskDescriptor(BaseModel):
|
| 208 |
+
id: str
|
| 209 |
+
name: str
|
| 210 |
+
difficulty: Difficulty
|
| 211 |
+
objective: str
|
| 212 |
+
grader: str
|
| 213 |
+
score_range: str = "[0.0, 1.0]"
|
| 214 |
+
deterministic: bool = True
|
env/task_catalog.py
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Explicit task catalog for validator and API task discovery.
|
| 3 |
+
"""
|
| 4 |
+
|
| 5 |
+
from __future__ import annotations
|
| 6 |
+
|
| 7 |
+
from .models import Difficulty, TaskDescriptor
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
TASK_CATALOG: list[TaskDescriptor] = [
|
| 11 |
+
TaskDescriptor(
|
| 12 |
+
id="easy",
|
| 13 |
+
name="easy",
|
| 14 |
+
difficulty=Difficulty.EASY,
|
| 15 |
+
objective=(
|
| 16 |
+
"Create the best sprint plan from a static backlog with fixed team capacity "
|
| 17 |
+
"and fixed deadlines."
|
| 18 |
+
),
|
| 19 |
+
grader="env.graders.grade",
|
| 20 |
+
),
|
| 21 |
+
TaskDescriptor(
|
| 22 |
+
id="medium",
|
| 23 |
+
name="medium",
|
| 24 |
+
difficulty=Difficulty.MEDIUM,
|
| 25 |
+
objective=(
|
| 26 |
+
"Revise the sprint plan after one mid-sprint disruption such as urgent work "
|
| 27 |
+
"or a developer capacity loss."
|
| 28 |
+
),
|
| 29 |
+
grader="env.graders.grade",
|
| 30 |
+
),
|
| 31 |
+
TaskDescriptor(
|
| 32 |
+
id="hard",
|
| 33 |
+
name="hard",
|
| 34 |
+
difficulty=Difficulty.HARD,
|
| 35 |
+
objective=(
|
| 36 |
+
"Repeatedly re-plan under multiple disruptions including added work, "
|
| 37 |
+
"capacity changes, and dependency shifts while preserving feasibility."
|
| 38 |
+
),
|
| 39 |
+
grader="env.graders.grade",
|
| 40 |
+
),
|
| 41 |
+
]
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
def get_task_catalog() -> list[TaskDescriptor]:
|
| 45 |
+
return TASK_CATALOG
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
def resolve_task_name(name: str | None) -> Difficulty | None:
|
| 49 |
+
if not name:
|
| 50 |
+
return None
|
| 51 |
+
normalized = name.strip().lower()
|
| 52 |
+
for task in TASK_CATALOG:
|
| 53 |
+
if task.id == normalized or task.name == normalized:
|
| 54 |
+
return task.difficulty
|
| 55 |
+
return None
|
openenv.yaml
CHANGED
|
@@ -7,9 +7,27 @@ version: "1.0.0"
|
|
| 7 |
entrypoint: env.environment:SprintEnv
|
| 8 |
|
| 9 |
tasks:
|
| 10 |
-
- easy
|
| 11 |
-
|
| 12 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 13 |
|
| 14 |
environment:
|
| 15 |
max_steps: 20
|
|
@@ -37,6 +55,7 @@ dependencies:
|
|
| 37 |
server:
|
| 38 |
host: 0.0.0.0
|
| 39 |
port: 7860
|
|
|
|
| 40 |
endpoint_reset: /reset
|
| 41 |
endpoint_step: /step
|
| 42 |
endpoint_state: /state
|
|
|
|
| 7 |
entrypoint: env.environment:SprintEnv
|
| 8 |
|
| 9 |
tasks:
|
| 10 |
+
- id: easy
|
| 11 |
+
name: easy
|
| 12 |
+
difficulty: easy
|
| 13 |
+
objective: Create the best sprint plan from a static backlog with fixed capacity and deadlines.
|
| 14 |
+
grader: env.graders.grade
|
| 15 |
+
score_range: [0.0, 1.0]
|
| 16 |
+
deterministic: true
|
| 17 |
+
- id: medium
|
| 18 |
+
name: medium
|
| 19 |
+
difficulty: medium
|
| 20 |
+
objective: Revise the sprint plan once after a single mid-sprint disruption.
|
| 21 |
+
grader: env.graders.grade
|
| 22 |
+
score_range: [0.0, 1.0]
|
| 23 |
+
deterministic: true
|
| 24 |
+
- id: hard
|
| 25 |
+
name: hard
|
| 26 |
+
difficulty: hard
|
| 27 |
+
objective: Re-plan repeatedly under multiple disruptions while preserving feasibility and value.
|
| 28 |
+
grader: env.graders.grade
|
| 29 |
+
score_range: [0.0, 1.0]
|
| 30 |
+
deterministic: true
|
| 31 |
|
| 32 |
environment:
|
| 33 |
max_steps: 20
|
|
|
|
| 55 |
server:
|
| 56 |
host: 0.0.0.0
|
| 57 |
port: 7860
|
| 58 |
+
endpoint_tasks: /tasks
|
| 59 |
endpoint_reset: /reset
|
| 60 |
endpoint_step: /step
|
| 61 |
endpoint_state: /state
|
server/app.py
CHANGED
|
@@ -23,7 +23,9 @@ from env.models import (
|
|
| 23 |
ResetRequest,
|
| 24 |
StepRequest,
|
| 25 |
StepResult,
|
|
|
|
| 26 |
)
|
|
|
|
| 27 |
from planner import generate_plan
|
| 28 |
|
| 29 |
logger = logging.getLogger(__name__)
|
|
@@ -60,13 +62,19 @@ def health():
|
|
| 60 |
return {"status": "ok", "env_ready": env is not None}
|
| 61 |
|
| 62 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 63 |
@app.post("/reset", response_model=Observation)
|
| 64 |
def reset(request: ResetRequest = ResetRequest()):
|
| 65 |
global env
|
| 66 |
if env is None:
|
| 67 |
raise HTTPException(500, "Environment not initialised")
|
|
|
|
| 68 |
return env.reset(
|
| 69 |
-
difficulty=
|
| 70 |
audio_path=request.audio_path,
|
| 71 |
transcript_override=request.transcript_override,
|
| 72 |
)
|
|
|
|
| 23 |
ResetRequest,
|
| 24 |
StepRequest,
|
| 25 |
StepResult,
|
| 26 |
+
TaskDescriptor,
|
| 27 |
)
|
| 28 |
+
from env.task_catalog import get_task_catalog, resolve_task_name
|
| 29 |
from planner import generate_plan
|
| 30 |
|
| 31 |
logger = logging.getLogger(__name__)
|
|
|
|
| 62 |
return {"status": "ok", "env_ready": env is not None}
|
| 63 |
|
| 64 |
|
| 65 |
+
@app.get("/tasks", response_model=list[TaskDescriptor])
|
| 66 |
+
def list_tasks():
|
| 67 |
+
return get_task_catalog()
|
| 68 |
+
|
| 69 |
+
|
| 70 |
@app.post("/reset", response_model=Observation)
|
| 71 |
def reset(request: ResetRequest = ResetRequest()):
|
| 72 |
global env
|
| 73 |
if env is None:
|
| 74 |
raise HTTPException(500, "Environment not initialised")
|
| 75 |
+
requested_difficulty = resolve_task_name(request.task) or request.difficulty
|
| 76 |
return env.reset(
|
| 77 |
+
difficulty=requested_difficulty,
|
| 78 |
audio_path=request.audio_path,
|
| 79 |
transcript_override=request.transcript_override,
|
| 80 |
)
|