TwoQuarks commited on
Commit
2078a86
·
verified ·
1 Parent(s): de2f4d6

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +64 -73
app.py CHANGED
@@ -20,28 +20,23 @@ DOWN_RESULTS = DOWN_ROOT / "results"
20
  STRANGE_RESULTS = STRANGE_ROOT / "results"
21
 
22
  STYLE_PATH = ROOT / "style.css"
 
23
 
24
  # ============================================================
25
- # CSS + JS (canvas + menu)
26
  # ============================================================
27
 
28
- CSS = STYLE_PATH.read_text(encoding="utf-8") if STYLE_PATH.exists() else ""
29
-
30
- # JS: robust init (Gradio can run JS before HTML exists)
31
  JS = r"""
32
  () => {
33
- // already running animation? bail
34
  if (window.__tq_starfield_running) return;
35
 
36
  function initOnceDomReady() {
37
  const canvas = document.getElementById("quantumField");
38
  const btn = document.getElementById("tqMenuBtn");
39
  const menu = document.getElementById("tqMenu");
40
-
41
- // DOM not ready yet -> retry shortly
42
  if (!canvas) return false;
43
 
44
- // ---- Menu toggle (idempotent) ----
45
  if (btn && menu && !btn.__tq_bound) {
46
  btn.__tq_bound = true;
47
  btn.addEventListener("click", () => menu.classList.toggle("open"));
@@ -52,17 +47,26 @@ JS = r"""
52
  });
53
  }
54
 
55
- // ---- Starfield canvas ----
56
  const ctx = canvas.getContext("2d");
57
  if (!ctx) return true;
58
 
59
  let w = 0, h = 0;
 
60
  const stars = [];
61
  const N = 170;
62
 
63
  function resize() {
64
- w = canvas.width = window.innerWidth;
65
- h = canvas.height = window.innerHeight;
 
 
 
 
 
 
 
 
 
66
  }
67
 
68
  function seed() {
@@ -89,7 +93,6 @@ JS = r"""
89
 
90
  ctx.globalAlpha = 0.9;
91
  ctx.fillStyle = "rgba(255,255,255,0.75)";
92
-
93
  for (const s of stars) {
94
  s.y += s.v * (1.0 + (1.0 - s.z));
95
  if (s.y > h + 10) { s.y = -10; s.x = Math.random() * w; }
@@ -112,11 +115,9 @@ JS = r"""
112
 
113
  window.__tq_starfield_running = true;
114
  tick();
115
-
116
  return true;
117
  }
118
 
119
- // Try a few times to catch Gradio timing
120
  let tries = 0;
121
  const maxTries = 30;
122
  const timer = setInterval(() => {
@@ -143,22 +144,31 @@ def _clean_pngs(folder: Path):
143
  def _collect_pngs(folder: Path):
144
  if not folder.exists():
145
  return []
146
- # Return absolute paths so Gradio can resolve correctly
147
  return sorted(str(p.resolve()) for p in folder.rglob("*.png"))
148
 
149
 
150
- def _run_capture(cmd, cwd: Path, env: dict):
151
  """
152
  Run a subprocess and capture stdout/stderr.
153
- If it fails, raise a RuntimeError containing the logs so Gradio shows it.
154
  """
155
- result = subprocess.run(
156
- cmd,
157
- cwd=str(cwd),
158
- env=env,
159
- capture_output=True,
160
- text=True,
161
- )
 
 
 
 
 
 
 
 
 
 
162
  if result.returncode != 0:
163
  raise RuntimeError(
164
  "Subprocess failed\n"
@@ -167,6 +177,7 @@ def _run_capture(cmd, cwd: Path, env: dict):
167
  f"STDOUT:\n{result.stdout}\n"
168
  f"STDERR:\n{result.stderr}"
169
  )
 
170
  return result.stdout, result.stderr
171
 
172
 
@@ -177,11 +188,10 @@ def _run_capture(cmd, cwd: Path, env: dict):
177
  def run_down_all(episodes: int):
178
  run_all = DOWN_ROOT / "run_all.py"
179
  if not run_all.exists():
180
- raise RuntimeError(f"{run_all} not found (check folder name: must be 'Down/')")
181
 
182
  DOWN_GRAPHICS.mkdir(parents=True, exist_ok=True)
183
  DOWN_RESULTS.mkdir(parents=True, exist_ok=True)
184
-
185
  _clean_pngs(DOWN_GRAPHICS)
186
 
187
  env = os.environ.copy()
@@ -189,8 +199,7 @@ def run_down_all(episodes: int):
189
  env["TWOQUARKS_GRAPHICS_DIR"] = str(DOWN_GRAPHICS)
190
 
191
  cmd = [sys.executable, str(run_all), "--episodes", str(int(episodes))]
192
-
193
- _run_capture(cmd, cwd=DOWN_ROOT, env=env)
194
 
195
  images = _collect_pngs(DOWN_GRAPHICS)
196
  meta = {
@@ -213,11 +222,10 @@ def run_down_all(episodes: int):
213
  def run_strange_all():
214
  run_all = STRANGE_ROOT / "run_all.py"
215
  if not run_all.exists():
216
- raise RuntimeError(f"{run_all} not found (check folder name: must be 'Strange/')")
217
 
218
  STRANGE_GRAPHICS.mkdir(parents=True, exist_ok=True)
219
  STRANGE_RESULTS.mkdir(parents=True, exist_ok=True)
220
-
221
  _clean_pngs(STRANGE_GRAPHICS)
222
 
223
  env = os.environ.copy()
@@ -225,8 +233,7 @@ def run_strange_all():
225
  env["TWOQUARKS_GRAPHICS_DIR"] = str(STRANGE_GRAPHICS)
226
 
227
  cmd = [sys.executable, str(run_all)]
228
-
229
- _run_capture(cmd, cwd=STRANGE_ROOT, env=env)
230
 
231
  images = _collect_pngs(STRANGE_GRAPHICS)
232
  meta = {
@@ -246,7 +253,6 @@ def run_strange_all():
246
  def build_ui():
247
  with gr.Blocks(title="TwoQuarks — QuarksLab", css=CSS, js=JS) as demo:
248
 
249
- # Inject DOM for canvas + nav
250
  gr.HTML(
251
  """
252
  <canvas id="quantumField"></canvas>
@@ -258,9 +264,7 @@ def build_ui():
258
  <a href="#strange">Strange</a>
259
  </div>
260
  <div class="spacer"></div>
261
- <div class="menu-btn" id="tqMenuBtn" aria-label="menu">
262
- <span></span>
263
- </div>
264
  <div class="menu" id="tqMenu">
265
  <a href="#down">DOWN / AntiDown</a>
266
  <div class="sep"></div>
@@ -271,14 +275,13 @@ def build_ui():
271
  """
272
  )
273
 
274
- # App wrapper (needed for z-index + padding-top in your CSS)
275
  with gr.Column(elem_id="tqApp"):
276
 
277
  gr.Markdown(
278
  """
279
  <div class="tq-hero">
280
  <div class="brand">TwoQuarks • QuarksLab</div>
281
- <div class="desc"><b>Run Models.</b> Real artifacts. Real Models.</div>
282
  </div>
283
  """,
284
  elem_id="top",
@@ -288,31 +291,25 @@ def build_ui():
288
  # DOWN
289
  # ----------------------------
290
  gr.HTML('<div id="down"></div>')
291
-
292
  gr.Markdown("## DOWN / AntiDown")
293
  gr.Markdown(
294
- """
295
- The Down / AntiDown models are reinforcement learning agents
296
- designed to expose and analyze failure modes under deceptive reward conditions.
297
- """
298
- )
299
-
300
- down_eps = gr.Slider(
301
- minimum=50,
302
- maximum=500,
303
- step=50,
304
- value=200,
305
- label="Episodes per phase",
306
  )
307
 
 
308
  run_down_btn = gr.Button("Run DOWN Model", elem_id="btn_down", elem_classes=["tq-primary"])
309
- down_meta = gr.JSON(label="Run status")
310
 
311
- # Force 1 column; carousel is handled by CSS
 
 
 
 
 
312
  down_gallery = gr.Gallery(
313
  label="Generated plots",
314
- columns=1,
315
- height="auto",
 
316
  elem_id="down_gallery",
317
  )
318
 
@@ -320,6 +317,7 @@ designed to expose and analyze failure modes under deceptive reward conditions.
320
  fn=run_down_all,
321
  inputs=[down_eps],
322
  outputs=[down_meta, down_gallery],
 
323
  )
324
 
325
  gr.Markdown("---")
@@ -328,22 +326,20 @@ designed to expose and analyze failure modes under deceptive reward conditions.
328
  # STRANGE
329
  # ----------------------------
330
  gr.HTML('<div id="strange"></div>')
331
-
332
  gr.Markdown("## STRANGE / AntiStrange")
333
  gr.Markdown(
334
- """
335
- Strange / AntiStrange are reinforcement learning agents
336
- designed to study failure under hidden regime shifts and phase transitions.
337
- """
338
  )
339
 
340
  run_strange_btn = gr.Button("Run STRANGE Model", elem_id="btn_strange", elem_classes=["tq-primary"])
341
- strange_meta = gr.JSON(label="Run status")
 
 
342
 
343
  strange_gallery = gr.Gallery(
344
- label="Generated plots",
345
- columns=1,
346
- height="auto",
347
  elem_id="strange_gallery",
348
  )
349
 
@@ -351,26 +347,21 @@ designed to study failure under hidden regime shifts and phase transitions.
351
  fn=run_strange_all,
352
  inputs=[],
353
  outputs=[strange_meta, strange_gallery],
 
354
  )
355
 
356
  gr.Markdown(
357
  """
358
  ---
359
- ### Notes:
360
- - This lab, its models & the TwoQuarks website,
361
- are developed end to end by Jaime Ledesma.
362
- - Gradio does not import or orchestrate internal model logic.
363
- - All visualizations are the result produced by the model.
364
  """
365
  )
366
 
367
  return demo
368
 
369
 
370
- # ============================================================
371
- # Launch
372
- # ============================================================
373
-
374
  demo = build_ui()
375
 
376
  if __name__ == "__main__":
 
20
  STRANGE_RESULTS = STRANGE_ROOT / "results"
21
 
22
  STYLE_PATH = ROOT / "style.css"
23
+ CSS = STYLE_PATH.read_text(encoding="utf-8") if STYLE_PATH.exists() else ""
24
 
25
  # ============================================================
26
+ # JS (canvas + menu) — patched for HiDPI
27
  # ============================================================
28
 
 
 
 
29
  JS = r"""
30
  () => {
 
31
  if (window.__tq_starfield_running) return;
32
 
33
  function initOnceDomReady() {
34
  const canvas = document.getElementById("quantumField");
35
  const btn = document.getElementById("tqMenuBtn");
36
  const menu = document.getElementById("tqMenu");
 
 
37
  if (!canvas) return false;
38
 
39
+ // Menu toggle (idempotent)
40
  if (btn && menu && !btn.__tq_bound) {
41
  btn.__tq_bound = true;
42
  btn.addEventListener("click", () => menu.classList.toggle("open"));
 
47
  });
48
  }
49
 
 
50
  const ctx = canvas.getContext("2d");
51
  if (!ctx) return true;
52
 
53
  let w = 0, h = 0;
54
+ let dpr = 1;
55
  const stars = [];
56
  const N = 170;
57
 
58
  function resize() {
59
+ dpr = Math.max(1, window.devicePixelRatio || 1);
60
+ // CSS size
61
+ canvas.style.width = window.innerWidth + "px";
62
+ canvas.style.height = window.innerHeight + "px";
63
+ // backing buffer
64
+ canvas.width = Math.floor(window.innerWidth * dpr);
65
+ canvas.height = Math.floor(window.innerHeight * dpr);
66
+ // draw in CSS pixels
67
+ ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
68
+ w = window.innerWidth;
69
+ h = window.innerHeight;
70
  }
71
 
72
  function seed() {
 
93
 
94
  ctx.globalAlpha = 0.9;
95
  ctx.fillStyle = "rgba(255,255,255,0.75)";
 
96
  for (const s of stars) {
97
  s.y += s.v * (1.0 + (1.0 - s.z));
98
  if (s.y > h + 10) { s.y = -10; s.x = Math.random() * w; }
 
115
 
116
  window.__tq_starfield_running = true;
117
  tick();
 
118
  return true;
119
  }
120
 
 
121
  let tries = 0;
122
  const maxTries = 30;
123
  const timer = setInterval(() => {
 
144
  def _collect_pngs(folder: Path):
145
  if not folder.exists():
146
  return []
 
147
  return sorted(str(p.resolve()) for p in folder.rglob("*.png"))
148
 
149
 
150
+ def _run_capture(cmd, cwd: Path, env: dict, timeout_s: int = 900):
151
  """
152
  Run a subprocess and capture stdout/stderr.
153
+ Timeout prevents Spaces from hanging forever.
154
  """
155
+ try:
156
+ result = subprocess.run(
157
+ cmd,
158
+ cwd=str(cwd),
159
+ env=env,
160
+ capture_output=True,
161
+ text=True,
162
+ timeout=timeout_s,
163
+ )
164
+ except subprocess.TimeoutExpired as e:
165
+ raise RuntimeError(
166
+ "Subprocess timed out\n"
167
+ f"CMD: {' '.join(map(str, cmd))}\n"
168
+ f"CWD: {cwd}\n"
169
+ f"TIMEOUT: {timeout_s}s\n"
170
+ ) from e
171
+
172
  if result.returncode != 0:
173
  raise RuntimeError(
174
  "Subprocess failed\n"
 
177
  f"STDOUT:\n{result.stdout}\n"
178
  f"STDERR:\n{result.stderr}"
179
  )
180
+
181
  return result.stdout, result.stderr
182
 
183
 
 
188
  def run_down_all(episodes: int):
189
  run_all = DOWN_ROOT / "run_all.py"
190
  if not run_all.exists():
191
+ raise RuntimeError(f"{run_all} not found (folder must be 'Down/')")
192
 
193
  DOWN_GRAPHICS.mkdir(parents=True, exist_ok=True)
194
  DOWN_RESULTS.mkdir(parents=True, exist_ok=True)
 
195
  _clean_pngs(DOWN_GRAPHICS)
196
 
197
  env = os.environ.copy()
 
199
  env["TWOQUARKS_GRAPHICS_DIR"] = str(DOWN_GRAPHICS)
200
 
201
  cmd = [sys.executable, str(run_all), "--episodes", str(int(episodes))]
202
+ _run_capture(cmd, cwd=DOWN_ROOT, env=env, timeout_s=1200)
 
203
 
204
  images = _collect_pngs(DOWN_GRAPHICS)
205
  meta = {
 
222
  def run_strange_all():
223
  run_all = STRANGE_ROOT / "run_all.py"
224
  if not run_all.exists():
225
+ raise RuntimeError(f"{run_all} not found (folder must be 'Strange/')")
226
 
227
  STRANGE_GRAPHICS.mkdir(parents=True, exist_ok=True)
228
  STRANGE_RESULTS.mkdir(parents=True, exist_ok=True)
 
229
  _clean_pngs(STRANGE_GRAPHICS)
230
 
231
  env = os.environ.copy()
 
233
  env["TWOQUARKS_GRAPHICS_DIR"] = str(STRANGE_GRAPHICS)
234
 
235
  cmd = [sys.executable, str(run_all)]
236
+ _run_capture(cmd, cwd=STRANGE_ROOT, env=env, timeout_s=1200)
 
237
 
238
  images = _collect_pngs(STRANGE_GRAPHICS)
239
  meta = {
 
253
  def build_ui():
254
  with gr.Blocks(title="TwoQuarks — QuarksLab", css=CSS, js=JS) as demo:
255
 
 
256
  gr.HTML(
257
  """
258
  <canvas id="quantumField"></canvas>
 
264
  <a href="#strange">Strange</a>
265
  </div>
266
  <div class="spacer"></div>
267
+ <div class="menu-btn" id="tqMenuBtn" aria-label="menu"><span></span></div>
 
 
268
  <div class="menu" id="tqMenu">
269
  <a href="#down">DOWN / AntiDown</a>
270
  <div class="sep"></div>
 
275
  """
276
  )
277
 
 
278
  with gr.Column(elem_id="tqApp"):
279
 
280
  gr.Markdown(
281
  """
282
  <div class="tq-hero">
283
  <div class="brand">TwoQuarks • QuarksLab</div>
284
+ <div class="desc"><b>Run Models.</b> Real artifacts. Real agents.</div>
285
  </div>
286
  """,
287
  elem_id="top",
 
291
  # DOWN
292
  # ----------------------------
293
  gr.HTML('<div id="down"></div>')
 
294
  gr.Markdown("## DOWN / AntiDown")
295
  gr.Markdown(
296
+ "RL agents designed to expose failure modes under **deceptive reward** conditions."
 
 
 
 
 
 
 
 
 
 
 
297
  )
298
 
299
+ # 🔥 Button visible, everything else folded
300
  run_down_btn = gr.Button("Run DOWN Model", elem_id="btn_down", elem_classes=["tq-primary"])
 
301
 
302
+ with gr.Accordion("Advanced parameters", open=False):
303
+ down_eps = gr.Slider(50, 500, step=50, value=200, label="Episodes per phase")
304
+
305
+ with gr.Accordion("Run status / logs", open=False):
306
+ down_meta = gr.JSON(label="Run status", elem_id="down_meta")
307
+
308
  down_gallery = gr.Gallery(
309
  label="Generated plots",
310
+ show_label=False,
311
+ columns=8, # wide -> we’ll style as strip
312
+ height=260, # stable tiles
313
  elem_id="down_gallery",
314
  )
315
 
 
317
  fn=run_down_all,
318
  inputs=[down_eps],
319
  outputs=[down_meta, down_gallery],
320
+ api_name=False,
321
  )
322
 
323
  gr.Markdown("---")
 
326
  # STRANGE
327
  # ----------------------------
328
  gr.HTML('<div id="strange"></div>')
 
329
  gr.Markdown("## STRANGE / AntiStrange")
330
  gr.Markdown(
331
+ "RL agents designed to study failure under **hidden regime shifts** and phase transitions."
 
 
 
332
  )
333
 
334
  run_strange_btn = gr.Button("Run STRANGE Model", elem_id="btn_strange", elem_classes=["tq-primary"])
335
+
336
+ with gr.Accordion("Run status / logs", open=False):
337
+ strange_meta = gr.JSON(label="Run status", elem_id="strange_meta")
338
 
339
  strange_gallery = gr.Gallery(
340
+ show_label=False,
341
+ columns=8,
342
+ height=260,
343
  elem_id="strange_gallery",
344
  )
345
 
 
347
  fn=run_strange_all,
348
  inputs=[],
349
  outputs=[strange_meta, strange_gallery],
350
+ api_name=False,
351
  )
352
 
353
  gr.Markdown(
354
  """
355
  ---
356
+ ### Notes
357
+ - This lab and the TwoQuarks website are developed end-to-end by Jaime Ledesma.
358
+ - Visualizations shown here are **direct artifacts** produced by the models.
 
 
359
  """
360
  )
361
 
362
  return demo
363
 
364
 
 
 
 
 
365
  demo = build_ui()
366
 
367
  if __name__ == "__main__":