Atharva1232 commited on
Commit
05403f5
Β·
verified Β·
1 Parent(s): 3de7f85

Upload folder using huggingface_hub

Browse files
Files changed (2) hide show
  1. server/app.py +22 -2
  2. server/static/index.html +370 -0
server/app.py CHANGED
@@ -28,6 +28,10 @@ Usage:
28
  python -m server.app
29
  """
30
 
 
 
 
 
31
  try:
32
  from openenv.core.env_server.http_server import create_app
33
  except Exception as e: # pragma: no cover
@@ -43,15 +47,31 @@ except ModuleNotFoundError:
43
  from server.Job_Scheduler_Env_environment import JobSchedulerEnvEnvironment
44
 
45
 
46
- # Create the app with web interface and README integration
47
  app = create_app(
48
  JobSchedulerEnvEnvironment,
49
  JobSchedulerEnvAction,
50
  JobSchedulerEnvObservation,
51
  env_name="Job_Scheduler_Env",
52
- max_concurrent_envs=1, # increase this number to allow more concurrent WebSocket sessions
53
  )
54
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55
 
56
  def main(host: str = "0.0.0.0", port: int = 8000):
57
  """
 
28
  python -m server.app
29
  """
30
 
31
+ from pathlib import Path
32
+
33
+ from fastapi.responses import HTMLResponse, RedirectResponse
34
+
35
  try:
36
  from openenv.core.env_server.http_server import create_app
37
  except Exception as e: # pragma: no cover
 
47
  from server.Job_Scheduler_Env_environment import JobSchedulerEnvEnvironment
48
 
49
 
 
50
  app = create_app(
51
  JobSchedulerEnvEnvironment,
52
  JobSchedulerEnvAction,
53
  JobSchedulerEnvObservation,
54
  env_name="Job_Scheduler_Env",
55
+ max_concurrent_envs=1,
56
  )
57
 
58
+ _UI_HTML = (Path(__file__).parent / "static" / "index.html").read_text()
59
+
60
+
61
+ @app.get("/ui", response_class=HTMLResponse, include_in_schema=False)
62
+ async def ui():
63
+ return _UI_HTML
64
+
65
+
66
+ @app.get("/", response_class=RedirectResponse, include_in_schema=False)
67
+ async def root():
68
+ return RedirectResponse(url="/ui")
69
+
70
+
71
+ @app.get("/web", response_class=RedirectResponse, include_in_schema=False)
72
+ async def web():
73
+ return RedirectResponse(url="/ui")
74
+
75
 
76
  def main(host: str = "0.0.0.0", port: int = 8000):
77
  """
server/static/index.html ADDED
@@ -0,0 +1,370 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Job Scheduler Environment</title>
7
+ <style>
8
+ *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
9
+
10
+ body {
11
+ font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
12
+ background: #0f172a;
13
+ color: #e2e8f0;
14
+ min-height: 100vh;
15
+ padding: 24px;
16
+ }
17
+
18
+ header {
19
+ display: flex;
20
+ justify-content: space-between;
21
+ align-items: center;
22
+ margin-bottom: 20px;
23
+ padding-bottom: 16px;
24
+ border-bottom: 1px solid #1e293b;
25
+ flex-wrap: wrap;
26
+ gap: 12px;
27
+ }
28
+
29
+ h1 { font-size: 1.4rem; font-weight: 700; color: #f8fafc; }
30
+
31
+ .badges { display: flex; gap: 8px; flex-wrap: wrap; }
32
+
33
+ .badge {
34
+ padding: 4px 10px;
35
+ border-radius: 9999px;
36
+ font-size: 0.75rem;
37
+ font-weight: 600;
38
+ }
39
+ .badge-blue { background: #1e3a8a; color: #93c5fd; }
40
+ .badge-green { background: #14532d; color: #86efac; }
41
+ .badge-red { background: #7f1d1d; color: #fca5a5; }
42
+ .badge-yellow { background: #78350f; color: #fde68a; }
43
+
44
+ #done-banner {
45
+ display: none;
46
+ border-radius: 8px;
47
+ padding: 10px 16px;
48
+ margin-bottom: 16px;
49
+ font-weight: 600;
50
+ text-align: center;
51
+ }
52
+ #done-banner.success { background: #14532d; border: 1px solid #22c55e; color: #86efac; }
53
+ #done-banner.fail { background: #7f1d1d; border: 1px solid #ef4444; color: #fca5a5; }
54
+
55
+ .desc {
56
+ background: #1e293b;
57
+ border: 1px solid #334155;
58
+ border-radius: 10px;
59
+ padding: 12px 16px;
60
+ margin-bottom: 16px;
61
+ font-size: 0.875rem;
62
+ color: #94a3b8;
63
+ font-style: italic;
64
+ min-height: 40px;
65
+ }
66
+
67
+ .controls {
68
+ display: flex;
69
+ gap: 10px;
70
+ margin-bottom: 20px;
71
+ flex-wrap: wrap;
72
+ align-items: center;
73
+ }
74
+
75
+ label { font-size: 0.8rem; color: #94a3b8; }
76
+
77
+ select, button {
78
+ padding: 7px 14px;
79
+ border-radius: 8px;
80
+ border: 1px solid #334155;
81
+ background: #1e293b;
82
+ color: #e2e8f0;
83
+ font-size: 0.875rem;
84
+ cursor: pointer;
85
+ transition: background 0.15s;
86
+ }
87
+ select:disabled, button:disabled { opacity: 0.4; cursor: not-allowed; }
88
+ select:not(:disabled):hover, button:not(:disabled):hover { background: #334155; }
89
+
90
+ button.primary { background: #1d4ed8; border-color: #1d4ed8; color: #fff; font-weight: 600; }
91
+ button.primary:not(:disabled):hover { background: #1e40af; }
92
+ button.reset { background: #b91c1c; border-color: #b91c1c; color: #fff; }
93
+ button.reset:hover { background: #991b1b; }
94
+
95
+ .assign-group {
96
+ display: flex;
97
+ align-items: center;
98
+ gap: 8px;
99
+ margin-left: auto;
100
+ flex-wrap: wrap;
101
+ }
102
+ .arrow { color: #475569; font-size: 1.1rem; }
103
+
104
+ .grid2 {
105
+ display: grid;
106
+ grid-template-columns: 1fr 1fr;
107
+ gap: 16px;
108
+ margin-bottom: 16px;
109
+ }
110
+ @media (max-width: 700px) { .grid2 { grid-template-columns: 1fr; } }
111
+
112
+ .card {
113
+ background: #1e293b;
114
+ border: 1px solid #334155;
115
+ border-radius: 12px;
116
+ padding: 16px;
117
+ }
118
+
119
+ .card h2 {
120
+ font-size: 0.7rem;
121
+ font-weight: 700;
122
+ color: #64748b;
123
+ text-transform: uppercase;
124
+ letter-spacing: 0.08em;
125
+ margin-bottom: 12px;
126
+ }
127
+
128
+ table { width: 100%; border-collapse: collapse; }
129
+ th {
130
+ text-align: left;
131
+ font-size: 0.7rem;
132
+ color: #475569;
133
+ text-transform: uppercase;
134
+ letter-spacing: 0.05em;
135
+ padding: 6px 4px;
136
+ border-bottom: 1px solid #334155;
137
+ }
138
+ td { padding: 7px 4px; font-size: 0.82rem; border-bottom: 1px solid #1e293b; }
139
+ tr:last-child td { border-bottom: none; }
140
+ code { font-family: monospace; background: #0f172a; padding: 2px 5px; border-radius: 4px; font-size: 0.8rem; }
141
+
142
+ .dot { display: inline-block; width: 7px; height: 7px; border-radius: 50%; margin-right: 5px; vertical-align: middle; }
143
+ .dot-green { background: #22c55e; }
144
+ .dot-yellow { background: #eab308; }
145
+ .dot-red { background: #ef4444; }
146
+ .dot-gray { background: #475569; }
147
+
148
+ .reward-value {
149
+ font-size: 2.2rem;
150
+ font-weight: 800;
151
+ line-height: 1;
152
+ margin-top: 4px;
153
+ }
154
+ .reward-value.pos { color: #22c55e; }
155
+ .reward-value.neg { color: #ef4444; }
156
+
157
+ .log {
158
+ height: 180px;
159
+ overflow-y: auto;
160
+ font-family: monospace;
161
+ font-size: 0.78rem;
162
+ }
163
+ .log-line { padding: 3px 0; border-bottom: 1px solid #1e293b; color: #64748b; }
164
+ .log-line.pos { color: #4ade80; }
165
+ .log-line.neg { color: #f87171; }
166
+ .log-line:last-child { border-bottom: none; }
167
+
168
+ .empty-msg { color: #475569; text-align: center; padding: 8px; font-size: 0.82rem; }
169
+ </style>
170
+ </head>
171
+ <body>
172
+
173
+ <header>
174
+ <h1>πŸ—“οΈ Job Scheduler Environment</h1>
175
+ <div class="badges">
176
+ <span id="badge-time" class="badge badge-blue">Time: β€”</span>
177
+ <span id="badge-step" class="badge badge-blue">Step: 0</span>
178
+ <span id="badge-state" class="badge badge-green">Ready</span>
179
+ </div>
180
+ </header>
181
+
182
+ <div id="done-banner"></div>
183
+
184
+ <div class="desc" id="llm-desc">Reset the environment to begin.</div>
185
+
186
+ <div class="controls">
187
+ <label>Level</label>
188
+ <select id="sel-level">
189
+ <option value="1">1 β€” 3 jobs Β· 3 machines</option>
190
+ <option value="2">2 β€” 5 jobs Β· 4 machines</option>
191
+ <option value="3">3 β€” 7 jobs Β· 5 machines</option>
192
+ </select>
193
+ <button class="reset" onclick="doReset()">β†Ί Reset</button>
194
+
195
+ <div class="assign-group">
196
+ <select id="sel-job" disabled><option>β€” job β€”</option></select>
197
+ <span class="arrow">β†’</span>
198
+ <select id="sel-machine" disabled><option>β€” machine β€”</option></select>
199
+ <button class="primary" id="btn-assign" disabled onclick="doAssign()">Assign</button>
200
+ </div>
201
+ </div>
202
+
203
+ <div class="grid2">
204
+ <div class="card">
205
+ <h2>Jobs</h2>
206
+ <table>
207
+ <thead><tr><th>ID</th><th>Duration</th><th>Arrival</th><th>Deadline</th><th>Status</th></tr></thead>
208
+ <tbody id="tbl-jobs"><tr><td colspan="5" class="empty-msg">No data</td></tr></tbody>
209
+ </table>
210
+ </div>
211
+ <div class="card">
212
+ <h2>Machines</h2>
213
+ <table>
214
+ <thead><tr><th>ID</th><th>Status</th><th>Running Job</th><th>Free At</th></tr></thead>
215
+ <tbody id="tbl-machines"><tr><td colspan="4" class="empty-msg">No data</td></tr></tbody>
216
+ </table>
217
+ </div>
218
+ </div>
219
+
220
+ <div class="grid2">
221
+ <div class="card">
222
+ <h2>Total Reward</h2>
223
+ <div class="reward-value pos" id="total-reward">0.00</div>
224
+ </div>
225
+ <div class="card">
226
+ <h2>Action Log</h2>
227
+ <div class="log" id="log"></div>
228
+ </div>
229
+ </div>
230
+
231
+ <script>
232
+ let totalReward = 0;
233
+ let stepCount = 0;
234
+ let isDone = false;
235
+
236
+ async function doReset() {
237
+ const level = parseInt(document.getElementById('sel-level').value);
238
+ try {
239
+ const res = await fetch('/reset', {
240
+ method: 'POST',
241
+ headers: { 'Content-Type': 'application/json' },
242
+ body: JSON.stringify({ task_level: level }),
243
+ });
244
+ const data = await res.json();
245
+ totalReward = 0; stepCount = 0; isDone = false;
246
+ document.getElementById('log').innerHTML = '';
247
+ hideBanner();
248
+ updateUI(data.observation ?? data, null);
249
+ } catch (e) { addLog('Reset failed: ' + e.message, false); }
250
+ }
251
+
252
+ async function doAssign() {
253
+ if (isDone) return;
254
+ const jobId = document.getElementById('sel-job').value;
255
+ const machineId = document.getElementById('sel-machine').value;
256
+ const action = `(${jobId}, ${machineId})`;
257
+ try {
258
+ const res = await fetch('/step', {
259
+ method: 'POST',
260
+ headers: { 'Content-Type': 'application/json' },
261
+ body: JSON.stringify({ action }),
262
+ });
263
+ const data = await res.json();
264
+ const r = data.reward ?? 0;
265
+ totalReward += r;
266
+ stepCount++;
267
+ addLog(`Step ${stepCount} ${action} β†’ ${r >= 0 ? '+' : ''}${r.toFixed(1)}`, r >= 0);
268
+ updateUI(data.observation ?? data, r);
269
+ if (data.done) {
270
+ isDone = true;
271
+ showBanner(totalReward >= 0);
272
+ setControls(false);
273
+ }
274
+ } catch (e) { addLog('Step failed: ' + e.message, false); }
275
+ }
276
+
277
+ function updateUI(obs, lastReward) {
278
+ if (!obs) return;
279
+ const t = obs.current_time ?? 0;
280
+
281
+ document.getElementById('badge-time').textContent = `Time: ${t}`;
282
+ document.getElementById('badge-step').textContent = `Step: ${stepCount}`;
283
+ document.getElementById('badge-state').textContent = isDone ? 'Done' : 'Running';
284
+ document.getElementById('badge-state').className = 'badge ' + (isDone ? 'badge-red' : 'badge-green');
285
+ document.getElementById('llm-desc').textContent = obs.llm_description || '';
286
+
287
+ const rewardEl = document.getElementById('total-reward');
288
+ rewardEl.textContent = totalReward.toFixed(2);
289
+ rewardEl.className = 'reward-value ' + (totalReward >= 0 ? 'pos' : 'neg');
290
+
291
+ // Jobs table
292
+ const jobs = obs.job_info || [];
293
+ document.getElementById('tbl-jobs').innerHTML = jobs.length
294
+ ? jobs.map(j => {
295
+ let dot, label;
296
+ if (j.done) { dot = 'dot-gray'; label = 'Done'; }
297
+ else if (j.is_happening) { dot = 'dot-yellow'; label = 'Running'; }
298
+ else if (t > j.deadline) { dot = 'dot-red'; label = 'Missed'; }
299
+ else if (t < j.arrival) { dot = 'dot-gray'; label = 'Not yet'; }
300
+ else { dot = 'dot-green'; label = 'Pending'; }
301
+ return `<tr>
302
+ <td><code>${j.id}</code></td>
303
+ <td>${j.duration}</td>
304
+ <td>${j.arrival}</td>
305
+ <td>${j.deadline}</td>
306
+ <td><span class="dot ${dot}"></span>${label}</td>
307
+ </tr>`;
308
+ }).join('')
309
+ : '<tr><td colspan="5" class="empty-msg">No jobs</td></tr>';
310
+
311
+ // Machines table
312
+ const machines = obs.machine_info || [];
313
+ document.getElementById('tbl-machines').innerHTML = machines.length
314
+ ? machines.map(m => `<tr>
315
+ <td><code>${m.id}</code></td>
316
+ <td><span class="dot ${m.occupied ? 'dot-yellow' : 'dot-green'}"></span>${m.occupied ? 'Busy' : 'Free'}</td>
317
+ <td>${m.job_running ?? 'β€”'}</td>
318
+ <td>${m.occupied ? m.become_free_time : 'β€”'}</td>
319
+ </tr>`).join('')
320
+ : '<tr><td colspan="4" class="empty-msg">No machines</td></tr>';
321
+
322
+ // Populate assignment selectors
323
+ const assignable = jobs.filter(j => !j.done && !j.is_happening && t >= j.arrival);
324
+ const freeMachines = machines.filter(m => !m.occupied);
325
+
326
+ document.getElementById('sel-job').innerHTML = assignable.length
327
+ ? assignable.map(j => `<option value="${j.id}">Job ${j.id} (dur ${j.duration}, dl ${j.deadline})</option>`).join('')
328
+ : '<option value="">No assignable jobs</option>';
329
+
330
+ document.getElementById('sel-machine').innerHTML = freeMachines.length
331
+ ? freeMachines.map(m => `<option value="${m.id}">Machine ${m.id}</option>`).join('')
332
+ : '<option value="">No free machines</option>';
333
+
334
+ setControls(!isDone && assignable.length > 0 && freeMachines.length > 0);
335
+ }
336
+
337
+ function setControls(on) {
338
+ ['sel-job', 'sel-machine', 'btn-assign'].forEach(id => {
339
+ document.getElementById(id).disabled = !on;
340
+ });
341
+ }
342
+
343
+ function addLog(msg, positive) {
344
+ const log = document.getElementById('log');
345
+ const line = document.createElement('div');
346
+ line.className = 'log-line ' + (positive ? 'pos' : 'neg');
347
+ line.textContent = msg;
348
+ log.prepend(line);
349
+ }
350
+
351
+ function showBanner(success) {
352
+ const b = document.getElementById('done-banner');
353
+ b.className = success ? 'success' : 'fail';
354
+ b.textContent = success
355
+ ? `βœ“ Episode complete β€” total reward ${totalReward.toFixed(2)}`
356
+ : `βœ— Episode ended with reward ${totalReward.toFixed(2)} β€” click Reset to try again`;
357
+ b.style.display = 'block';
358
+ }
359
+
360
+ function hideBanner() {
361
+ const b = document.getElementById('done-banner');
362
+ b.style.display = 'none';
363
+ b.className = '';
364
+ }
365
+
366
+ // Auto-start
367
+ doReset();
368
+ </script>
369
+ </body>
370
+ </html>