Spaces:
Sleeping
Sleeping
Commit ·
d755709
1
Parent(s): 991b1dd
Initial commit
Browse files- factory_env/env.py +93 -0
- factory_env/grader.py +3 -0
- factory_env/models.py +26 -0
- factory_env/tasks.py +17 -0
- inference.py +11 -0
- openenv.yaml +9 -0
- requirements.txt +3 -0
factory_env/env.py
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import random
|
| 2 |
+
from typing import List
|
| 3 |
+
from factory_env.models import Observation, Action, Machine, Job
|
| 4 |
+
|
| 5 |
+
class FactoryEnv:
|
| 6 |
+
def __init__(self, task="easy"):
|
| 7 |
+
self.task = task
|
| 8 |
+
self.time = 0
|
| 9 |
+
self.max_steps = 20
|
| 10 |
+
|
| 11 |
+
async def reset(self):
|
| 12 |
+
random.seed(42)
|
| 13 |
+
|
| 14 |
+
self.time = 0
|
| 15 |
+
|
| 16 |
+
self.machines = [
|
| 17 |
+
Machine(id="M1", status="idle"),
|
| 18 |
+
Machine(id="M2", status="idle"),
|
| 19 |
+
]
|
| 20 |
+
|
| 21 |
+
self.jobs = [
|
| 22 |
+
Job(id="J1", remaining_time=3, deadline=10),
|
| 23 |
+
Job(id="J2", remaining_time=2, deadline=8),
|
| 24 |
+
]
|
| 25 |
+
|
| 26 |
+
return self._get_result(0.0, False)
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
async def step(self, action: Action):
|
| 30 |
+
reward = 0.0
|
| 31 |
+
|
| 32 |
+
# Apply action
|
| 33 |
+
if action.action_type == "assign_job":
|
| 34 |
+
job = self._find_job(action.job_id)
|
| 35 |
+
machine = self._find_machine(action.machine_id)
|
| 36 |
+
|
| 37 |
+
if job and machine and machine.status == "idle":
|
| 38 |
+
job.assigned_machine = machine.id
|
| 39 |
+
machine.status = "busy"
|
| 40 |
+
machine.current_job = job.id
|
| 41 |
+
reward += 0.2
|
| 42 |
+
else:
|
| 43 |
+
reward -= 0.2 # invalid action
|
| 44 |
+
|
| 45 |
+
# Simulate time
|
| 46 |
+
self.time += 1
|
| 47 |
+
|
| 48 |
+
for machine in self.machines:
|
| 49 |
+
if machine.status == "busy":
|
| 50 |
+
job = self._find_job(machine.current_job)
|
| 51 |
+
job.remaining_time -= 1
|
| 52 |
+
|
| 53 |
+
if job.remaining_time <= 0:
|
| 54 |
+
reward += 1.0
|
| 55 |
+
self.jobs.remove(job)
|
| 56 |
+
machine.status = "idle"
|
| 57 |
+
machine.current_job = None
|
| 58 |
+
|
| 59 |
+
# Penalty for idle machines
|
| 60 |
+
idle_count = sum(1 for m in self.machines if m.status == "idle")
|
| 61 |
+
reward -= idle_count * 0.05
|
| 62 |
+
|
| 63 |
+
done = self.time >= self.max_steps or len(self.jobs) == 0
|
| 64 |
+
|
| 65 |
+
return self._get_result(reward, done)
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
def state(self):
|
| 69 |
+
return self._get_observation()
|
| 70 |
+
|
| 71 |
+
def _get_observation(self):
|
| 72 |
+
return Observation(
|
| 73 |
+
machines=self.machines,
|
| 74 |
+
pending_jobs=self.jobs,
|
| 75 |
+
time=self.time,
|
| 76 |
+
)
|
| 77 |
+
|
| 78 |
+
def _get_result(self, reward, done):
|
| 79 |
+
return type("Result", (), {
|
| 80 |
+
"observation": self._get_observation(),
|
| 81 |
+
"reward": reward,
|
| 82 |
+
"done": done
|
| 83 |
+
})
|
| 84 |
+
|
| 85 |
+
def _find_job(self, job_id):
|
| 86 |
+
return next((j for j in self.jobs if j.id == job_id), None)
|
| 87 |
+
|
| 88 |
+
def _find_machine(self, machine_id):
|
| 89 |
+
return next((m for m in self.machines if m.id == machine_id), None)
|
| 90 |
+
|
| 91 |
+
async def close(self):
|
| 92 |
+
pass
|
| 93 |
+
|
factory_env/grader.py
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
def compute_score(total_reward, max_possible=20):
|
| 2 |
+
score = total_reward / max_possible
|
| 3 |
+
return max(0.0, min(1.0, score))
|
factory_env/models.py
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from pydantic import BaseModel
|
| 2 |
+
from typing import List, Optional
|
| 3 |
+
|
| 4 |
+
class Machine(BaseModel):
|
| 5 |
+
id: str
|
| 6 |
+
status: str # idle, busy, broken
|
| 7 |
+
current_job: Optional[str] = None
|
| 8 |
+
|
| 9 |
+
class Job(BaseModel):
|
| 10 |
+
id: str
|
| 11 |
+
remaining_time: int
|
| 12 |
+
deadline: int
|
| 13 |
+
assigned_machine: Optional[str] = None
|
| 14 |
+
|
| 15 |
+
class Observation(BaseModel):
|
| 16 |
+
machines: List[Machine]
|
| 17 |
+
pending_jobs: List[Job]
|
| 18 |
+
time: int
|
| 19 |
+
|
| 20 |
+
class Action(BaseModel):
|
| 21 |
+
action_type: str # assign_job, wait
|
| 22 |
+
job_id: Optional[str] = None
|
| 23 |
+
machine_id: Optional[str] = None
|
| 24 |
+
|
| 25 |
+
class Reward(BaseModel):
|
| 26 |
+
value: float
|
factory_env/tasks.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
TASKS = {
|
| 2 |
+
"easy": {
|
| 3 |
+
"machines": 2,
|
| 4 |
+
"jobs": 2,
|
| 5 |
+
"failures": False,
|
| 6 |
+
},
|
| 7 |
+
"medium": {
|
| 8 |
+
"machines": 3,
|
| 9 |
+
"jobs": 5,
|
| 10 |
+
"failures": True,
|
| 11 |
+
},
|
| 12 |
+
"hard": {
|
| 13 |
+
"machines": 5,
|
| 14 |
+
"jobs": 10,
|
| 15 |
+
"failures": True,
|
| 16 |
+
},
|
| 17 |
+
}
|
inference.py
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
def parse_action(text):
|
| 2 |
+
parts = text.split()
|
| 3 |
+
|
| 4 |
+
if parts[0] == "assign_job":
|
| 5 |
+
return Action(
|
| 6 |
+
action_type="assign_job",
|
| 7 |
+
job_id=parts[1],
|
| 8 |
+
machine_id=parts[2],
|
| 9 |
+
)
|
| 10 |
+
|
| 11 |
+
return Action(action_type="wait")
|
openenv.yaml
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: factory_env
|
| 2 |
+
description: Smart factory scheduling environment
|
| 3 |
+
|
| 4 |
+
tasks:
|
| 5 |
+
- name: easy
|
| 6 |
+
- name: medium
|
| 7 |
+
- name: hard
|
| 8 |
+
|
| 9 |
+
entry_point: factory_env.env:FactoryEnv
|
requirements.txt
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
pydantic
|
| 2 |
+
openai
|
| 3 |
+
asyncio
|