TwoQuarks commited on
Commit
aedff98
·
verified ·
1 Parent(s): 612a416

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +361 -226
app.py CHANGED
@@ -1,226 +1,361 @@
1
- import os
2
- import sys
3
- import subprocess
4
- from pathlib import Path
5
- import gradio as gr
6
-
7
- # ============================================================
8
- # Paths (Linux is case-sensitive!)
9
- # ============================================================
10
-
11
- ROOT = Path(__file__).resolve().parent
12
-
13
- DOWN_ROOT = ROOT / "Down"
14
- STRANGE_ROOT = ROOT / "Strange"
15
-
16
- DOWN_GRAPHICS = DOWN_ROOT / "graphics"
17
- STRANGE_GRAPHICS = STRANGE_ROOT / "graphics"
18
-
19
- DOWN_RESULTS = DOWN_ROOT / "results"
20
- STRANGE_RESULTS = STRANGE_ROOT / "results"
21
-
22
-
23
- # ============================================================
24
- # Helpers
25
- # ============================================================
26
-
27
- def _clean_pngs(folder: Path):
28
- folder.mkdir(parents=True, exist_ok=True)
29
- for f in folder.rglob("*.png"):
30
- try:
31
- f.unlink()
32
- except Exception:
33
- pass
34
-
35
-
36
- def _collect_pngs(folder: Path):
37
- if not folder.exists():
38
- return []
39
- return sorted(str(p) for p in folder.rglob("*.png"))
40
-
41
-
42
- def _run_capture(cmd, cwd: Path, env: dict):
43
- """
44
- Run a subprocess and capture stdout/stderr.
45
- If it fails, raise a RuntimeError containing the logs so Gradio shows it.
46
- """
47
- result = subprocess.run(
48
- cmd,
49
- cwd=str(cwd),
50
- env=env,
51
- capture_output=True,
52
- text=True,
53
- )
54
- if result.returncode != 0:
55
- raise RuntimeError(
56
- "Subprocess failed\n"
57
- f"CMD: {' '.join(map(str, cmd))}\n"
58
- f"CWD: {cwd}\n\n"
59
- f"STDOUT:\n{result.stdout}\n"
60
- f"STDERR:\n{result.stderr}"
61
- )
62
- return result.stdout, result.stderr
63
-
64
-
65
- # ============================================================
66
- # DOWN (Unified: Down + AntiDown)
67
- # ============================================================
68
-
69
- def run_down_all(episodes: int):
70
- run_all = DOWN_ROOT / "run_all.py"
71
- if not run_all.exists():
72
- raise RuntimeError(f"{run_all} not found (check folder name: must be 'Down/')")
73
-
74
- DOWN_GRAPHICS.mkdir(parents=True, exist_ok=True)
75
- DOWN_RESULTS.mkdir(parents=True, exist_ok=True)
76
-
77
- _clean_pngs(DOWN_GRAPHICS)
78
-
79
- env = os.environ.copy()
80
- env["TWOQUARKS_RESULTS_DIR"] = str(DOWN_RESULTS)
81
- env["TWOQUARKS_GRAPHICS_DIR"] = str(DOWN_GRAPHICS)
82
-
83
- cmd = [sys.executable, str(run_all), "--episodes", str(int(episodes))]
84
-
85
- # capture logs so the UI shows the real failure reason if any
86
- _run_capture(cmd, cwd=DOWN_ROOT, env=env)
87
-
88
- images = _collect_pngs(DOWN_GRAPHICS)
89
- meta = {
90
- "status": "completed",
91
- "pipeline": "DOWN + AntiDown",
92
- "episodes_per_phase": int(episodes),
93
- "graphics_count": len(images),
94
- "graphics_dir": str(DOWN_GRAPHICS),
95
- "results_dir": str(DOWN_RESULTS),
96
- # canonical CSV names the dual plot expects
97
- "csv_down": str(DOWN_RESULTS / "down_paradox_tabular_results.csv"),
98
- "csv_antidown": str(DOWN_RESULTS / "antidown_corrupted_valley_tabular.csv"),
99
- }
100
- return meta, images
101
-
102
-
103
- # ============================================================
104
- # STRANGE (Unified: Strange + AntiStrange)
105
- # ============================================================
106
-
107
- def run_strange_all():
108
- run_all = STRANGE_ROOT / "run_all.py"
109
- if not run_all.exists():
110
- raise RuntimeError(f"{run_all} not found (check folder name: must be 'Strange/')")
111
-
112
- STRANGE_GRAPHICS.mkdir(parents=True, exist_ok=True)
113
- STRANGE_RESULTS.mkdir(parents=True, exist_ok=True)
114
-
115
- _clean_pngs(STRANGE_GRAPHICS)
116
-
117
- env = os.environ.copy()
118
- env["TWOQUARKS_RESULTS_DIR"] = str(STRANGE_RESULTS)
119
- env["TWOQUARKS_GRAPHICS_DIR"] = str(STRANGE_GRAPHICS)
120
-
121
- cmd = [sys.executable, str(run_all)]
122
-
123
- _run_capture(cmd, cwd=STRANGE_ROOT, env=env)
124
-
125
- images = _collect_pngs(STRANGE_GRAPHICS)
126
- meta = {
127
- "status": "completed",
128
- "pipeline": "STRANGE + AntiStrange",
129
- "graphics_count": len(images),
130
- "graphics_dir": str(STRANGE_GRAPHICS),
131
- "results_dir": str(STRANGE_RESULTS),
132
- }
133
- return meta, images
134
-
135
-
136
- # ============================================================
137
- # UI
138
- # ============================================================
139
-
140
- def build_ui():
141
- with gr.Blocks(title="TwoQuarks — QuarksLab") as demo:
142
- gr.Markdown(
143
- """
144
- # TwoQuarks • QuarksLab
145
- **Run Models. Real artifacts. Real Models.**
146
- """
147
- )
148
-
149
- # ----------------------------
150
- # DOWN
151
- # ----------------------------
152
- gr.Markdown("## DOWN / AntiDown")
153
- gr.Markdown(
154
- """
155
- The Down / AntiDown models are reinforcement learning agents
156
- designed to expose and analyze failure modes under deceptive reward conditions.
157
- """
158
- )
159
-
160
- down_eps = gr.Slider(
161
- minimum=50,
162
- maximum=500,
163
- step=50,
164
- value=200,
165
- label="Episodes per phase",
166
- )
167
-
168
- run_down_btn = gr.Button("Run DOWN Model")
169
- down_meta = gr.JSON(label="Run status")
170
- down_gallery = gr.Gallery(label="Generated plots", columns=2, height="auto")
171
-
172
- run_down_btn.click(
173
- fn=run_down_all,
174
- inputs=[down_eps],
175
- outputs=[down_meta, down_gallery],
176
- )
177
-
178
- gr.Markdown("---")
179
-
180
- # ----------------------------
181
- # STRANGE
182
- # ----------------------------
183
- gr.Markdown("## STRANGE / AntiStrange")
184
- gr.Markdown(
185
- """
186
- Strange / AntiStrange are reinforcement learning agents
187
- designed to study failure under hidden regime shifts and phase transitions.
188
- """
189
- )
190
-
191
- run_strange_btn = gr.Button("Run STRANGE Model")
192
- strange_meta = gr.JSON(label="Run status")
193
- strange_gallery = gr.Gallery(label="Generated plots", columns=2, height="auto")
194
-
195
- run_strange_btn.click(
196
- fn=run_strange_all,
197
- inputs=[],
198
- outputs=[strange_meta, strange_gallery],
199
- )
200
-
201
- gr.Markdown(
202
- """
203
- ---
204
- ### Notes:
205
- - This lab, its models & the TwoQuarks website,
206
- are developed end to end by Jaime Ledesma.
207
- - Gradio does not import or orchestrate internal model logic.
208
- - All visualizations are the result produced by the model.
209
- """
210
- )
211
-
212
- return demo
213
-
214
-
215
- # ============================================================
216
- # Launch
217
- # ============================================================
218
-
219
- demo = build_ui()
220
-
221
- if __name__ == "__main__":
222
- demo.launch(
223
- server_name="0.0.0.0",
224
- server_port=int(os.getenv("PORT", "7860")),
225
- show_api=False,
226
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* ===== TwoQuarks skin for Gradio ===== */
2
+ *{margin:0;padding:0;box-sizing:border-box}
3
+
4
+ :root{
5
+ --bg:#000;
6
+ --fg:#fff;
7
+ --muted:rgba(255,255,255,.55);
8
+
9
+ --card:rgba(255,255,255,.04);
10
+ --card2:rgba(255,255,255,.08);
11
+ --stroke:rgba(255,255,255,.10);
12
+
13
+ /* glass needs to be visible but not milky */
14
+ --glass:rgba(0,0,0,.32);
15
+
16
+ --accent:#8cb4ff;
17
+ --accentGlow: rgba(140,180,255,.20);
18
+
19
+ --rSm:10px;
20
+ --rMd:12px;
21
+ --rLg:14px;
22
+ }
23
+
24
+ html,body{height:100%}
25
+ body, .gradio-container{
26
+ font-family: Inter, ui-sans-serif, system-ui, Arial;
27
+ background:var(--bg) !important;
28
+ color:var(--fg) !important;
29
+ overflow-x:hidden;
30
+ }
31
+
32
+ /* Starfield canvas (keep it inside stacking context) */
33
+ #quantumField{
34
+ position:fixed; inset:0;
35
+ width:100vw; height:100vh;
36
+ z-index:0;
37
+ background:#000;
38
+ pointer-events:none;
39
+ opacity:.70;
40
+ }
41
+
42
+ /* Nav */
43
+ #tqNav{
44
+ position: fixed;
45
+ top: 0; left: 0; right: 0;
46
+ z-index: 999;
47
+ padding: 12px 22px;
48
+ background: linear-gradient(180deg, rgba(0,0,0,.55), rgba(0,0,0,.22));
49
+ backdrop-filter: blur(12px);
50
+ border-bottom: 1px solid rgba(255,255,255,.06);
51
+ }
52
+
53
+ #tqNav .nav-inner{
54
+ max-width: 980px;
55
+ margin: 0 auto;
56
+ display:flex;
57
+ align-items:center;
58
+ gap: 12px;
59
+ }
60
+
61
+ #tqNav .brand{
62
+ font-weight: 700;
63
+ font-size: 1.05rem;
64
+ letter-spacing: .22px;
65
+ opacity: .92;
66
+ }
67
+
68
+ #tqNav .nav-links{
69
+ display:flex;
70
+ gap:10px;
71
+ margin-left: 14px;
72
+ flex-wrap: wrap;
73
+ }
74
+
75
+ #tqNav .nav-links a{
76
+ text-decoration:none;
77
+ color: var(--fg);
78
+ padding: 8px 12px;
79
+ border: 1px solid rgba(255,255,255,.12);
80
+ border-radius: 999px;
81
+ font-size: .9rem;
82
+ opacity: .78;
83
+ transition: .15s ease;
84
+ white-space: nowrap;
85
+ background: rgba(255,255,255,.02);
86
+ }
87
+
88
+ #tqNav .nav-links a:hover{
89
+ opacity: 1;
90
+ background: rgba(255,255,255,.06);
91
+ border-color: rgba(255,255,255,.18);
92
+ }
93
+
94
+ #tqNav .spacer{ flex:1; }
95
+
96
+ /* Hamburger button */
97
+ #tqNav .menu-btn{
98
+ width: 34px;
99
+ height: 34px;
100
+ display:flex;
101
+ align-items:center;
102
+ justify-content:center;
103
+ border: 1px solid rgba(255,255,255,.14);
104
+ border-radius: 12px;
105
+ cursor: pointer;
106
+ background: rgba(255,255,255,.03);
107
+ transition: .15s ease;
108
+ }
109
+ #tqNav .menu-btn:hover{
110
+ background: rgba(255,255,255,.06);
111
+ border-color: rgba(255,255,255,.20);
112
+ }
113
+
114
+ #tqNav .menu-btn span{
115
+ display:block;
116
+ width:16px;
117
+ height:2px;
118
+ background: var(--fg);
119
+ position:relative;
120
+ opacity:.9;
121
+ }
122
+
123
+ #tqNav .menu-btn span::before,
124
+ #tqNav .menu-btn span::after{
125
+ content:"";
126
+ position:absolute;
127
+ left:0;
128
+ width:16px;
129
+ height:2px;
130
+ background: var(--fg);
131
+ opacity:.9;
132
+ }
133
+
134
+ #tqNav .menu-btn span::before{ top:-6px; }
135
+ #tqNav .menu-btn span::after{ top: 6px; }
136
+
137
+ #tqNav .menu{
138
+ position:absolute;
139
+ top: 60px;
140
+ right: 24px;
141
+ min-width: 220px;
142
+ padding: 10px;
143
+ border-radius: var(--rLg);
144
+ background: var(--glass);
145
+ backdrop-filter: blur(14px);
146
+ border: 1px solid rgba(255,255,255,.10);
147
+ box-shadow: 0 0 28px rgba(0,0,0,.6);
148
+ display:none;
149
+ }
150
+
151
+ #tqNav .menu.open{ display:block; }
152
+
153
+ #tqNav .menu a{
154
+ display:block;
155
+ padding: 10px 10px;
156
+ border-radius: 10px;
157
+ text-decoration:none;
158
+ color: var(--fg);
159
+ opacity: .88;
160
+ }
161
+
162
+ #tqNav .menu a:hover{
163
+ background: rgba(255,255,255,.06);
164
+ opacity: 1;
165
+ }
166
+
167
+ #tqNav .menu .sep{
168
+ height:1px;
169
+ background: rgba(255,255,255,.10);
170
+ margin: 8px 2px;
171
+ }
172
+
173
+ /* Make app sit above canvas and below fixed nav */
174
+ #tqApp{
175
+ position:relative;
176
+ z-index:1;
177
+ padding-top: 84px;
178
+ }
179
+
180
+ /* Hero / header panel (glass) */
181
+ .tq-hero{
182
+ margin: 10px 0 14px 0;
183
+ padding: 14px 18px;
184
+ background: var(--glass);
185
+ backdrop-filter: blur(12px);
186
+ border: 1px solid rgba(255,255,255,.06);
187
+ border-radius: var(--rMd);
188
+ box-shadow: 0 12px 34px rgba(0,0,0,.45);
189
+ }
190
+
191
+ .tq-hero .brand{
192
+ font-weight:700;
193
+ letter-spacing:.22px;
194
+ font-size:1.08rem;
195
+ opacity:.92;
196
+ }
197
+
198
+ .tq-hero .desc{
199
+ margin-top:8px;
200
+ color: var(--muted);
201
+ font-size: .98rem;
202
+ line-height: 1.45;
203
+ }
204
+
205
+ /* Cards / panels */
206
+ .gradio-container .gr-panel,
207
+ .gradio-container .block,
208
+ .gradio-container .wrap{
209
+ border:1px solid var(--stroke) !important;
210
+ background: var(--card) !important;
211
+ backdrop-filter: blur(10px) !important;
212
+ border-radius: var(--rMd) !important;
213
+ }
214
+
215
+ /* Hover effect */
216
+ .gradio-container .gr-panel:hover{
217
+ background: var(--card2) !important;
218
+ border-color: rgba(255,255,255,.14) !important;
219
+ }
220
+
221
+ /* Tabs (pill vibe) */
222
+ .gradio-container .gr-tab-nav{
223
+ background: transparent !important;
224
+ border: none !important;
225
+ gap: 10px !important;
226
+ }
227
+
228
+ .gradio-container .gr-tab-nav button{
229
+ border:1px solid rgba(255,255,255,.12) !important;
230
+ border-radius:999px !important;
231
+ padding:8px 12px !important;
232
+ color: var(--fg) !important;
233
+ opacity:.78 !important;
234
+ background: rgba(255,255,255,.02) !important;
235
+ transition:.18s ease !important;
236
+ }
237
+
238
+ .gradio-container .gr-tab-nav button:hover{
239
+ opacity:1 !important;
240
+ background: rgba(255,255,255,.06) !important;
241
+ border-color: rgba(255,255,255,.18) !important;
242
+ }
243
+
244
+ .gradio-container .gr-tab-nav button.selected{
245
+ opacity: 1 !important;
246
+ border-color: rgba(140,180,255,.55) !important;
247
+ box-shadow:
248
+ 0 0 0 1px rgba(140,180,255,.18) inset,
249
+ 0 0 20px var(--accentGlow) !important;
250
+ }
251
+
252
+ /* Inputs */
253
+ .gradio-container input,
254
+ .gradio-container textarea{
255
+ background: rgba(255,255,255,.03) !important;
256
+ border:1px solid rgba(255,255,255,.12) !important;
257
+ color: var(--fg) !important;
258
+ border-radius: var(--rMd) !important;
259
+ }
260
+
261
+ /* Buttons (base) */
262
+ .gradio-container button,
263
+ .gradio-container .gr-button{
264
+ color: var(--fg) !important;
265
+ border:1px solid rgba(255,255,255,.12) !important;
266
+ background: rgba(255,255,255,.04) !important;
267
+ border-radius:999px !important;
268
+ padding:10px 14px !important;
269
+ opacity:.86 !important;
270
+ transition:.15s ease !important;
271
+ }
272
+
273
+ /* Buttons: make them feel like buttons, not banners */
274
+ .gradio-container .gr-button,
275
+ .gradio-container button{
276
+ width:auto !important;
277
+ min-width: 160px;
278
+ max-width: 280px;
279
+ display:inline-flex !important;
280
+ align-items:center !important;
281
+ justify-content:center !important;
282
+ font-weight: 650;
283
+ letter-spacing: .2px;
284
+ }
285
+
286
+ .gradio-container button:hover,
287
+ .gradio-container .gr-button:hover{
288
+ opacity:1 !important;
289
+ background: rgba(255,255,255,.08) !important;
290
+ border-color: rgba(140,180,255,.35) !important;
291
+ box-shadow: 0 0 18px var(--accentGlow);
292
+ }
293
+
294
+ /* Gallery base */
295
+ .gradio-container .gallery{
296
+ border-radius: var(--rMd) !important;
297
+ }
298
+
299
+ /* ===== Gallery → horizontal carousel (needs elem_id in app.py) ===== */
300
+
301
+ /* Grid row becomes horizontal strip */
302
+ #down_gallery .grid,
303
+ #strange_gallery .grid{
304
+ display:flex !important;
305
+ flex-wrap:nowrap !important;
306
+ gap: 14px !important;
307
+
308
+ overflow-x:auto !important;
309
+ overflow-y:hidden !important;
310
+
311
+ padding: 12px !important;
312
+ scroll-snap-type: x mandatory;
313
+ -webkit-overflow-scrolling: touch;
314
+ }
315
+
316
+ /* Each slide */
317
+ #down_gallery .grid > figure,
318
+ #strange_gallery .grid > figure{
319
+ flex: 0 0 auto !important;
320
+ width: 220px !important;
321
+ scroll-snap-align: start;
322
+
323
+ background: rgba(255,255,255,.03);
324
+ border: 1px solid rgba(255,255,255,.08);
325
+ border-radius: 14px;
326
+ padding: 8px;
327
+ }
328
+
329
+ /* Thumbnail size */
330
+ #down_gallery img,
331
+ #strange_gallery img{
332
+ width: 100% !important;
333
+ height: 130px !important;
334
+ object-fit: contain !important; /* use cover if you prefer */
335
+ border-radius: 10px !important;
336
+ }
337
+
338
+ /* Hide captions/filenames */
339
+ #down_gallery figcaption,
340
+ #strange_gallery figcaption,
341
+ #down_gallery .caption,
342
+ #strange_gallery .caption{
343
+ display:none !important;
344
+ }
345
+
346
+ /* Sleek scrollbar */
347
+ #down_gallery .grid::-webkit-scrollbar,
348
+ #strange_gallery .grid::-webkit-scrollbar{
349
+ height: 6px;
350
+ }
351
+ #down_gallery .grid::-webkit-scrollbar-thumb,
352
+ #strange_gallery .grid::-webkit-scrollbar-thumb{
353
+ background: rgba(255,255,255,.14);
354
+ border-radius: 999px;
355
+ }
356
+
357
+ /* Range accent */
358
+ input[type="range"]{ accent-color: var(--accent); }
359
+
360
+ /* Optional: hide Gradio footer */
361
+ footer{ display:none !important; }