seawolf2357 commited on
Commit
3bcab20
·
verified ·
1 Parent(s): 9209608

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +505 -824
app.py CHANGED
@@ -1,840 +1,521 @@
1
  """
2
- MARL Model-Agnostic Runtime Middleware for LLMs
 
 
 
 
 
 
 
 
3
  """
4
- print("=" * 50)
5
- print(" MARL Starting...")
6
- print("=" * 50)
7
-
8
- import os, sys, time, traceback
9
-
10
- # ── Path setup ──
11
- APP_DIR = os.path.dirname(os.path.abspath(__file__))
12
- sys.path.insert(0, APP_DIR)
13
- print(f" APP_DIR: {APP_DIR}")
14
- print(f" CWD: {os.getcwd()}")
15
- print(f" Files: {os.listdir(APP_DIR)}")
16
-
17
- # ── marl package check ──
18
- pkg_dir = os.path.join(APP_DIR, "marl")
19
- if os.path.isdir(pkg_dir):
20
- print(f" marl/: {os.listdir(pkg_dir)}")
21
- else:
22
- print(f" ⚠️ marl/ directory NOT FOUND at {pkg_dir}")
23
- # also check cwd
24
- cwd_pkg = os.path.join(os.getcwd(), "marl")
25
- if os.path.isdir(cwd_pkg):
26
- print(f" Found at CWD: {cwd_pkg}")
27
- sys.path.insert(0, os.getcwd())
28
-
29
- # ── dependency imports ──
30
- try:
31
- import html as html_mod
32
- print(" ✅ html")
33
- except Exception as e:
34
- print(f" ❌ html: {e}")
35
-
36
- try:
37
- import requests
38
- print(f" ✅ requests")
39
- except Exception as e:
40
- print(f" ❌ requests: {e}")
41
-
42
- try:
43
- import gradio as gr
44
- print(f" ✅ gradio {gr.__version__}")
45
- except Exception as e:
46
- print(f" ❌ gradio: {e}")
47
- sys.exit(1)
48
-
49
- # ── marl import ──
50
- try:
51
- from marl import Marl, MarlConfig, MarlResult
52
- print(" ✅ marl imported")
53
- MARL_OK = True
54
- except Exception as e:
55
- print(f" ❌ marl import failed: {e}")
56
- traceback.print_exc()
57
- MARL_OK = False
58
-
59
- # ── load index.html ──
60
- INDEX_HTML = ""
61
- for p in [os.path.join(APP_DIR, "index.html"), "index.html",
62
- "/app/index.html", os.path.join(os.getcwd(), "index.html")]:
63
- try:
64
- with open(p, "r", encoding="utf-8") as f:
65
- INDEX_HTML = f.read()
66
- print(f" ✅ index.html from {p}")
67
- break
68
- except:
69
- continue
70
- if not INDEX_HTML:
71
- print(" ⚠️ index.html not found, inline fallback")
72
- INDEX_HTML = """<style>
73
- .gradio-container{max-width:100%!important;width:100%!important;padding:0 24px!important}
74
- header{display:none!important}
75
- </style>
76
- <div style="text-align:center;padding:28px 0 16px">
77
- <h1 style="margin:0;font-size:2.2em;font-weight:800;color:#6366f1">MARL</h1>
78
- <p style="color:#475569;font-size:13px;margin:6px 0">Model-Agnostic Runtime Middleware for LLMs</p>
79
- <p style="color:#94a3b8;font-size:11px">Intelligence Amplification · Hallucination Reduction · Zero-Change Middleware</p>
80
- <div style="display:flex;justify-content:center;gap:4px;margin:12px 0;font-size:9px;font-family:monospace">
81
- <span style="background:rgba(13,148,136,.08);color:#0d9488;padding:4px 10px;border-radius:10px">🔍 S1 Hypothesis</span>
82
- <span style="color:#ccc">→</span>
83
- <span style="background:rgba(99,102,241,.08);color:#6366f1;padding:4px 10px;border-radius:10px">⚡ S2 Solver</span>
84
- <span style="color:#ccc">→</span>
85
- <span style="background:rgba(217,119,6,.08);color:#d97706;padding:4px 10px;border-radius:10px">🛡 S3 Auditor</span>
86
- <span style="color:#ccc">→</span>
87
- <span style="background:rgba(225,29,72,.08);color:#e11d48;padding:4px 10px;border-radius:10px">🎯 S4 Verifier</span>
88
- <span style="color:#ccc">→</span>
89
- <span style="background:rgba(139,92,246,.08);color:#8b5cf6;padding:4px 10px;border-radius:10px">🧠 S5 Refiner</span>
90
- </div>
91
- </div>"""
92
-
93
- print("=" * 50)
94
-
95
- # ════════════════════════════════════════════════════════════════
96
- # Pipeline / Model config
97
- # ════════════════════════════════════════════════════════════════
98
-
99
- import re
100
- import random
101
 
102
- STAGES = {
103
- "S1_Hypothesis": {"label":"S1 · Hypothesis Generator","tag":"Divergent Search","icon":"🔍","color":"#0d9488"},
104
- "S2_Solver": {"label":"S2 · Primary Solver","tag":"Forward Pass","icon":"⚡","color":"#6366f1"},
105
- "S3_Auditor": {"label":"S3 · Consistency Auditor","tag":"Validation Gate","icon":"🛡️","color":"#d97706"},
106
- "S4_Verifier": {"label":"S4 · Adversarial Verifier","tag":"Error Detection","icon":"🎯","color":"#e11d48"},
107
- "S5_Refiner": {"label":"S5 · Metacognitive Refiner","tag":"Self-Correction","icon":"🧠","color":"#8b5cf6"},
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
108
  }
109
- STAGE_ORDER = ["S1_Hypothesis","S2_Solver","S3_Auditor","S4_Verifier","S5_Refiner"]
110
-
111
- # ════════════════════════════════════════════════════════════════
112
- # Showcase Examples — displayed during pipeline wait
113
- # ════════════════════════════════════════════════════════════════
114
-
115
- SHOWCASE = [
116
- {"cat":"🎯 Trap Question","q":"Is 0.9999... less than 1?",
117
- "raw":"It approaches 1 infinitely but is less than 1.",
118
- "tag":"S1 trap detection → S4 error confirmed",
119
- "marl":"Mathematically 0.999... = 1 (proven via geometric series + algebraic proof)"},
120
- {"cat":"🎯 Trap Question","q":"Can the Great Wall be seen from space?",
121
- "raw":"It is the only man-made structure visible from space with the naked eye.",
122
- "tag":"S4 detects 'NASA officially denied this'",
123
- "marl":"At 5-8m wide, invisible even from low orbit. Urban legend originating from 18th-century British satire."},
124
- {"cat":"🎯 Trap Question","q":"Was Napoleon short?",
125
- "raw":"He was very short at about 157cm.",
126
- "tag":"S4 detects 'French inch vs British inch confusion'",
127
- "marl":"Actually ~169cm, taller than avg French male (165cm). Product of British propaganda."},
128
- {"cat":"🔬 Precision Question","q":"Does water boil at 100°C?",
129
- "raw":"Yes, water boils at 100°C.",
130
- "tag":"S3 detects 'atmospheric pressure not specified'",
131
- "marl":"100°C at 1 atm. On Mt. Everest summit, water boils at ~70°C."},
132
- {"cat":"💀 Overconfidence","q":"Is Vitamin C effective for preventing colds?",
133
- "raw":"It strengthens immunity and effectively prevents colds. (85%)",
134
- "tag":"S4 detects 'conflicts with Cochrane meta-analysis'",
135
- "marl":"Minimal prevention for general population (8%↓). Only significant for high-intensity athletes (50%↓)."},
136
- {"cat":"💀 Overconfidence","q":"Will quantum computing break all encryption?",
137
- "raw":"When quantum computers become practical, all encryption will be broken.",
138
- "tag":"S4 detects 'symmetric vs asymmetric key distinction missing'",
139
- "marl":"RSA/ECC vulnerable, but AES-256 remains safe. NIST post-quantum standards already published."},
140
- {"cat":"💀 Hard Question","q":"Is GPT-5 an AGI?",
141
- "raw":"It surpasses humans on most benchmarks, so it is close to AGI.",
142
- "tag":"S1 catches 'benchmark ≠ general intelligence' trap",
143
- "marl":"Self-correction ability (ER=0.302) still low. 'Knowing a lot' and 'knowing what you don't know' are different dimensions."},
144
- {"cat":"🧠 Emergent Question","q":"Write a grant proposal leveraging a sports star's IP",
145
- "raw":"Just partner with the team and plan a branded product.",
146
- "tag":"S4 detects 'IP not secured = project termination risk'",
147
- "marl":"Plan A (license) + Plan C (alternative design) in parallel. Attach distributor LOI + demand survey evidence."},
148
- {"cat":"🧠 Emergent Question","q":"Calculate TAM·SAM·SOM for my startup",
149
- "raw":"TAM: $500B, SAM: $10B, SOM: $1M (no sources)",
150
- "tag":"S4 detects 'TAM→SAM→SOM logical disconnection'",
151
- "marl":"Cite IDC report + bottom-up calculation via segment×ARPU. Restructured for investor verifiability."},
152
- {"cat":"🧠 Emergent Question","q":"What is the fastest sort in Python?",
153
- "raw":"QuickSort is the fastest at O(n log n).",
154
- "tag":"S3 detects 'diverges from Python built-in implementation'",
155
- "marl":"sorted() uses TimSort (hybrid), empirically faster than pure QuickSort."},
156
- {"cat":"🔬 Precision Question","q":"What is the height of the Eiffel Tower?",
157
- "raw":"The Eiffel Tower is 324m tall.",
158
- "tag":"S4 detects 'antenna included/excluded not specified'",
159
- "marl":"Structure 300m + broadcast antenna 24m = 324m total. Distinction matters by context."},
160
- {"cat":"🔬 Precision Question","q":"How many light-minutes from Earth to the Sun?",
161
- "raw":"About 8 minutes.",
162
- "tag":"S3 detects 'elliptical orbit variation range missing'",
163
- "marl":"Average 8 min 20 sec. Ranges from 8:10 (perihelion) to 8:27 (aphelion)."},
164
- ]
165
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
166
  MODELS = {
167
- "OpenAI": {"env":"OPENAI_API_KEY","default":"gpt-5.4",
168
- "list":["gpt-5.4","gpt-5.4-pro","gpt-5.2","gpt-4o","gpt-4o-mini"]},
169
- "Anthropic": {"env":"ANTHROPIC_API_KEY","default":"claude-sonnet-4-6",
170
- "list":["claude-opus-4-6","claude-sonnet-4-6","claude-haiku-4-5-20251001"]},
171
- "Google Gemini": {"env":"GOOGLE_API_KEY","default":"gemini-2.5-pro",
172
- "list":["gemini-2.5-pro","gemini-2.5-flash"]},
173
- "DeepSeek": {"env":"DEEPSEEK_API_KEY","default":"deepseek-chat",
174
- "list":["deepseek-chat","deepseek-reasoner"]},
175
- "xAI (Grok)": {"env":"XAI_API_KEY","default":"grok-3-beta",
176
- "list":["grok-3-beta"]},
177
- "Ollama (Local)": {"env":"","default":"llama3.1",
178
- "list":["llama3.1","llama3.1:70b","qwen3.5:32b","deepseek-r1:8b","phi4:14b"]},
179
- "Custom (OpenAI-compatible)": {"env":"","default":"custom","list":["custom"]},
 
 
180
  }
181
- BACKEND_LIST = list(MODELS.keys())
182
-
183
- # ══════════════════════════════════════════════════════════════���═
184
- # MD → HTML
185
- # ════════════════════════════════════════════════════════════════
186
-
187
- def _esc(t):
188
- return html_mod.escape(str(t)) if t else ""
189
-
190
- def _hex_rgb(h):
191
- try: return f"{int(h[1:3],16)},{int(h[3:5],16)},{int(h[5:7],16)}"
192
- except: return "99,102,241"
193
-
194
- def _inline_fmt(text):
195
- t = text
196
- t = re.sub(r'\*\*(.+?)\*\*', r'<b>\1</b>', t)
197
- t = re.sub(r'\*(.+?)\*', r'<em>\1</em>', t)
198
- t = re.sub(r'(\d{1,3})%', r'<span style="font-family:monospace;font-weight:600;color:#6366f1">\1%</span>', t)
199
- return t
200
-
201
- def _md2html(text):
202
- if not text: return ""
203
- t = text
204
- code_blocks = {}
205
- def _save_code(m):
206
- k = f"__CODE_{len(code_blocks)}__"
207
- code = html_mod.escape(m.group(2))
208
- code_blocks[k] = f'<pre style="background:rgba(15,23,42,.04);border:1px solid #e2e5f0;border-radius:8px;padding:12px;overflow-x:auto;font-family:monospace;font-size:11px;line-height:1.6;margin:8px 0"><code>{code}</code></pre>'
209
- return k
210
- t = re.sub(r'```(\w*)\n(.*?)```', _save_code, t, flags=re.DOTALL)
211
- t = re.sub(r'`([^`]+)`', r'<code style="background:rgba(99,102,241,.08);padding:1px 5px;border-radius:4px;font-family:monospace;font-size:11px">\1</code>', t)
212
-
213
- lines = t.split('\n')
214
- result = []
215
- in_list = False
216
- for line in lines:
217
- s = line.strip()
218
- if s in code_blocks:
219
- if in_list: result.append('</ul>'); in_list = False
220
- result.append(code_blocks[s]); continue
221
- hm = re.match(r'^(#{1,4})\s+(.+)$', s)
222
- if hm:
223
- if in_list: result.append('</ul>'); in_list = False
224
- lvl = len(hm.group(1))
225
- sz = {1:'18px',2:'15px',3:'13px',4:'12px'}[lvl]
226
- result.append(f'<div style="font-size:{sz};font-weight:700;color:#1e293b;margin:14px 0 6px">{_inline_fmt(html_mod.escape(hm.group(2)))}</div>'); continue
227
- if re.match(r'^[-*_]{3,}\s*$', s):
228
- if in_list: result.append('</ul>'); in_list = False
229
- result.append('<hr style="border:none;border-top:1px solid #e2e5f0;margin:12px 0">'); continue
230
- lm = re.match(r'^[-*+]\s+(.+)$', s)
231
- if lm:
232
- if not in_list: result.append('<ul style="margin:6px 0;padding-left:20px">'); in_list = True
233
- result.append(f'<li style="margin:3px 0;line-height:1.7">{_inline_fmt(html_mod.escape(lm.group(1)))}</li>'); continue
234
- nm = re.match(r'^(\d+)[.)]\s+(.+)$', s)
235
- if nm:
236
- if in_list: result.append('</ul>'); in_list = False
237
- result.append(f'<div style="margin:3px 0;padding-left:8px;line-height:1.7"><span style="color:#6366f1;font-weight:600;font-family:monospace;font-size:11px">{nm.group(1)}.</span> {_inline_fmt(html_mod.escape(nm.group(2)))}</div>'); continue
238
- if in_list: result.append('</ul>'); in_list = False
239
- if not s: result.append('<div style="height:6px"></div>'); continue
240
- tm = re.match(r'^\[([A-Z_-]+(?:-\d+)?)\]\s*(.*)', s)
241
- if tm:
242
- tag = tm.group(1); rest = _inline_fmt(html_mod.escape(tm.group(2)))
243
- tc = '#6366f1'
244
- for px, cl in {'BACKTRACK':'#d97706','FIX':'#e11d48','APPLIED':'#16a34a','TRAP':'#e11d48','HALLUCINATION':'#e11d48','NO-FIXES':'#16a34a'}.items():
245
- if tag.startswith(px): tc = cl; break
246
- result.append(f'<div style="margin:6px 0;padding:6px 10px;background:rgba({_hex_rgb(tc)},.06);border-left:3px solid {tc};border-radius:0 6px 6px 0;font-size:12px;line-height:1.7"><span style="font-family:monospace;font-weight:700;color:{tc};font-size:11px">[{html_mod.escape(tag)}]</span> {rest}</div>'); continue
247
- result.append(f'<p style="margin:4px 0;line-height:1.8">{_inline_fmt(html_mod.escape(s))}</p>')
248
- if in_list: result.append('</ul>')
249
- return '\n'.join(result)
250
-
251
- # ════════════════════════════════════════════════════════════════
252
- # Answer Cleaner — strip system tags, confidence %, metadata
253
- # ════════════════════════════════════════════════════════════════
254
-
255
- def _clean_answer(text):
256
- """Strip reasoning artifacts from final answer for end-user display."""
257
- if not text:
258
- return ""
259
- t = text
260
-
261
- # Remove "--- Corrections ---" section and everything after
262
- t = re.split(r'\n-{2,}\s*Corrections\s*-{2,}', t, maxsplit=1)[0]
263
-
264
- lines = t.split('\n')
265
- clean = []
266
- for line in lines:
267
- s = line.strip()
268
- # Skip system tags: [FIX-n], [TRAP-CHECK], [HALLUCINATION], [APPLIED-n], [BACKTRACK-n], [NO-FIXES-NEEDED]
269
- if re.match(r'^\[(?:FIX-\d+|TRAP-CHECK|HALLUCINATION|APPLIED-\d+|BACKTRACK-\d+|NO-FIXES-NEEDED)\]', s):
270
- continue
271
- # Skip stage labels: "S1 · Hypothesis Generator", "S2 · Primary Solver" etc.
272
- if re.match(r'^S[1-5]\s*[·\-]', s):
273
- continue
274
- # Strip inline confidence: "(confidence: 85%)", "(90% confidence)", "Confidence: 85%"
275
- line = re.sub(r'\(?\s*[Cc]onfidence[:\s]*\d{1,3}%\s*\)?', '', line)
276
- line = re.sub(r'\(?\s*\d{1,3}%\s*confidence\s*\)?', '', line)
277
- # Strip standalone confidence lines: "Confidence: 85%" or "**Confidence:** 90%"
278
- if re.match(r'^\s*\*{0,2}[Cc]onfidence\*{0,2}\s*[:]\s*\d{1,3}%', line):
279
- continue
280
- # Strip "## Confidence Adjustments" section headers
281
- if re.match(r'^#{1,4}\s*(?:Confidence|Top-\d+\s+Uncertaint)', s):
282
- continue
283
- # Strip "★ MANDATORY SELF-CHECK" and similar framework directives
284
- if re.match(r'^★', s):
285
- continue
286
- clean.append(line)
287
-
288
- # Clean up excess blank lines
289
- result = '\n'.join(clean)
290
- result = re.sub(r'\n{3,}', '\n\n', result).strip()
291
- return result
292
-
293
-
294
- # ════════════════════════════════════════════════════════════════
295
- # HTML Renderers
296
- # ════════════════════════════════════════════════════════════════
297
-
298
- def _stage_html(name, content):
299
- s = STAGES.get(name, {})
300
- c = s.get("color","#6366f1")
301
- body = _md2html(content[:3000] if content else "(no output)")
302
- return f'<div style="border-left:3px solid {c};background:rgba({_hex_rgb(c)},.04);border-radius:0 10px 10px 0;padding:14px 16px;margin:8px 0"><div style="display:flex;align-items:center;gap:8px;margin-bottom:8px"><span style="font-size:1.1em">{s.get("icon","")}</span><span style="font-family:monospace;font-weight:700;font-size:12px;color:{c}">{s.get("label",name)}</span><span style="font-size:9px;color:#94a3b8;font-family:monospace;background:rgba(0,0,0,.04);padding:2px 8px;border-radius:10px">{s.get("tag","")}</span></div><div style="font-size:12px;line-height:1.7;color:#334155">{body}</div></div>'
303
-
304
- def _result_html(content, is_marl=False):
305
- """Render Raw LLM result (no cleaning needed)."""
306
- if not content:
307
- return '<div style="color:#94a3b8;padding:40px;text-align:center;font-size:12px">Waiting...</div>'
308
- badge = "MARL-Enhanced" if is_marl else "Raw LLM"
309
- bc = "#6366f1" if is_marl else "#64748b"
310
- bg = "rgba(99,102,241,.06)" if is_marl else "#f5f6fa"
311
- body = _md2html(content)
312
- return f'<div style="padding:16px"><div style="margin-bottom:12px"><span style="font-family:monospace;font-size:9px;font-weight:700;background:{bg};color:{bc};padding:3px 10px;border-radius:10px;text-transform:uppercase;letter-spacing:1px">{badge}</span></div><div style="font-size:13px;line-height:1.8;color:#1e293b;word-break:break-word">{body}</div></div>'
313
-
314
- def _marl_result_html(raw_answer, trace_dict):
315
- """Render MARL result: clean answer + embedded reasoning toggle with S1~S5 trace."""
316
- clean = _clean_answer(raw_answer)
317
- if not clean:
318
- return '<div style="color:#94a3b8;padding:40px;text-align:center;font-size:12px">Waiting...</div>'
319
-
320
- body = _md2html(clean)
321
-
322
- # Build S1~S5 trace HTML for toggle
323
- trace_parts = ""
324
- if trace_dict:
325
- trace_parts = ''.join(_stage_html(n, trace_dict[n]) for n in STAGE_ORDER if n in trace_dict)
326
-
327
- toggle_html = ""
328
- if trace_parts:
329
- toggle_html = f'''
330
- <details style="margin-top:16px;border:1px solid #e2e5f0;border-radius:10px;overflow:hidden">
331
- <summary style="cursor:pointer;padding:10px 16px;background:rgba(99,102,241,.03);font-family:monospace;font-size:11px;font-weight:600;color:#6366f1;user-select:none;display:flex;align-items:center;gap:6px">
332
- 🔍 View Reasoning Process — S1→S2→S3→S4→S5
333
- </summary>
334
- <div style="padding:12px 16px;border-top:1px solid #e2e5f0;background:#fafbfc">
335
- {trace_parts}
336
- </div>
337
- </details>'''
338
-
339
- return f'''<div style="padding:16px">
340
- <div style="margin-bottom:12px">
341
- <span style="font-family:monospace;font-size:9px;font-weight:700;background:rgba(99,102,241,.06);color:#6366f1;padding:3px 10px;border-radius:10px;text-transform:uppercase;letter-spacing:1px">MARL-Enhanced</span>
342
- </div>
343
- <div style="font-size:13px;line-height:1.8;color:#1e293b;word-break:break-word">{body}</div>
344
- {toggle_html}
345
- </div>'''
346
-
347
- def _trace_html(trace_dict):
348
- if not trace_dict:
349
- return '<div style="color:#94a3b8;padding:30px;text-align:center">Run MARL to see pipeline trace</div>'
350
- return ''.join(_stage_html(n, trace_dict[n]) for n in STAGE_ORDER if n in trace_dict)
351
-
352
- def _pipeline_anim(phase="marl", msg=""):
353
- """Animated pipeline + rotating showcase Before/After cards."""
354
- stages = [
355
- ("S1", "Hypothesis", "🔍", "#0d9488"),
356
- ("S2", "Solver", "⚡", "#6366f1"),
357
- ("S3", "Auditor", "🛡️", "#d97706"),
358
- ("S4", "Verifier", "🎯", "#e11d48"),
359
- ("S5", "Synthesizer", "🧠", "#8b5cf6"),
360
- ]
361
- if phase == "raw":
362
- return f'''<div style="padding:30px 20px;text-align:center">
363
- <div style="font-size:13px;color:#475569;margin-bottom:16px">{_esc(msg) if msg else "Generating Raw LLM response..."}</div>
364
- <div style="display:inline-block;width:200px;height:4px;background:#e2e5f0;border-radius:4px;overflow:hidden">
365
- <div style="width:40%;height:100%;background:linear-gradient(90deg,#6366f1,#0d9488);border-radius:4px;animation:rawPulse 1.5s ease-in-out infinite"></div>
366
- </div>
367
- <style>@keyframes rawPulse{{0%,100%{{width:30%}}50%{{width:70%}}}}</style>
368
- </div>'''
369
-
370
- # ── Stage pills with sequential glow ──
371
- pills = []
372
- arrows = []
373
- ns = len(stages)
374
- for i, (sid, name, icon, color) in enumerate(stages):
375
- delay = i * 1.8
376
- rgb = _hex_rgb(color)
377
- pills.append(f'''<div style="display:flex;flex-direction:column;align-items:center;gap:4px;animation:stageGlow {ns*1.8}s ease-in-out infinite;animation-delay:{delay}s;opacity:0.35">
378
- <div style="width:44px;height:44px;border-radius:12px;display:flex;align-items:center;justify-content:center;font-size:18px;background:rgba({rgb},.1);border:2px solid rgba({rgb},.2)">{icon}</div>
379
- <div style="font-family:monospace;font-size:10px;font-weight:700;color:{color}">{sid}</div>
380
- <div style="font-size:9px;color:#94a3b8;white-space:nowrap">{name}</div>
381
- </div>''')
382
- if i < ns - 1:
383
- arrows.append(f'<div style="color:#cbd5e1;font-size:14px;margin-top:-16px;animation:arrowPulse {ns*1.8}s ease-in-out infinite;animation-delay:{delay+0.9}s;opacity:0.3">→</div>')
384
-
385
- interleaved = []
386
- for i, pill in enumerate(pills):
387
- interleaved.append(pill)
388
- if i < len(arrows):
389
- interleaved.append(arrows[i])
390
- stage_html = "\n".join(interleaved)
391
- sub = _esc(msg) if msg else "Thinking, questioning, correcting, rewriting..."
392
-
393
- # ── Showcase cards — CSS-only rotation ──
394
- shuffled = list(SHOWCASE)
395
- random.shuffle(shuffled)
396
- nc = min(len(shuffled), 8) # show up to 8 cards
397
- dur_each = 5 # seconds per card
398
- total_dur = nc * dur_each
399
-
400
- cards_html = ""
401
- card_kf = ""
402
- for idx in range(nc):
403
- ex = shuffled[idx]
404
- pct_start = (idx / nc) * 100
405
- pct_show = pct_start + 2
406
- pct_hide = ((idx + 1) / nc) * 100 - 2
407
- pct_end = ((idx + 1) / nc) * 100
408
-
409
- cards_html += f'''<div class="sc-card sc-card-{idx}" style="position:absolute;inset:0;opacity:0;animation:sc{idx} {total_dur}s ease-in-out infinite">
410
- <div style="display:flex;align-items:center;gap:6px;margin-bottom:10px">
411
- <span style="font-size:9px;font-weight:700;color:#6366f1;background:rgba(99,102,241,.08);padding:2px 8px;border-radius:6px;font-family:monospace">{_esc(ex['cat'])}</span>
412
- </div>
413
- <div style="font-size:13px;font-weight:700;color:#1e293b;margin-bottom:12px;line-height:1.4">"{_esc(ex['q'])}"</div>
414
- <div style="display:flex;gap:10px">
415
- <div style="flex:1;padding:10px 12px;background:#fef2f2;border:1px solid #fecaca;border-radius:8px">
416
- <div style="font-size:9px;font-weight:700;color:#e11d48;margin-bottom:4px;font-family:monospace">❌ Non-MARL</div>
417
- <div style="font-size:11px;color:#7f1d1d;line-height:1.5">{_esc(ex['raw'])}</div>
418
- </div>
419
- <div style="flex:1;padding:10px 12px;background:#f0fdf4;border:1px solid #bbf7d0;border-radius:8px">
420
- <div style="font-size:9px;font-weight:700;color:#16a34a;margin-bottom:4px;font-family:monospace">✅ MARL</div>
421
- <div style="font-size:11px;color:#14532d;line-height:1.5">{_esc(ex['marl'])}</div>
422
- </div>
423
- </div>
424
- <div style="margin-top:8px;font-size:9px;color:#6366f1;font-family:monospace;font-weight:600">🔍 {_esc(ex['tag'])}</div>
425
- </div>\n'''
426
-
427
- card_kf += f"@keyframes sc{idx}{{" \
428
- f"0%,{pct_start:.1f}%{{opacity:0;transform:translateY(8px)}}" \
429
- f"{pct_show:.1f}%{{opacity:1;transform:translateY(0)}}" \
430
- f"{pct_hide:.1f}%{{opacity:1;transform:translateY(0)}}" \
431
- f"{pct_end:.1f}%,100%{{opacity:0;transform:translateY(-8px)}}}}\n"
432
-
433
- return f'''<div style="padding:24px 16px;text-align:center">
434
- <div style="display:flex;align-items:center;justify-content:center;gap:10px;flex-wrap:nowrap;margin-bottom:16px">
435
- {stage_html}
436
- </div>
437
- <div style="font-size:12px;color:#475569;margin-bottom:6px">{sub}</div>
438
- <div style="display:flex;gap:4px;justify-content:center;margin-bottom:20px">
439
- <div style="width:6px;height:6px;border-radius:50%;background:#6366f1;animation:dotBounce 1.4s ease-in-out infinite"></div>
440
- <div style="width:6px;height:6px;border-radius:50%;background:#0d9488;animation:dotBounce 1.4s ease-in-out .2s infinite"></div>
441
- <div style="width:6px;height:6px;border-radius:50%;background:#8b5cf6;animation:dotBounce 1.4s ease-in-out .4s infinite"></div>
442
- </div>
443
- <div style="text-align:left;max-width:560px;margin:0 auto">
444
- <div style="font-size:9px;font-weight:700;color:#94a3b8;text-transform:uppercase;letter-spacing:1.5px;margin-bottom:10px;font-family:monospace">💡 Real cases caught by MARL</div>
445
- <div style="position:relative;min-height:180px">
446
- {cards_html}
447
- </div>
448
- </div>
449
- <style>
450
- @keyframes stageGlow{{
451
- 0%,100%{{opacity:0.3;transform:scale(1)}}
452
- {100/ns:.0f}%{{opacity:1;transform:scale(1.1)}}
453
- {200/ns:.0f}%{{opacity:0.3;transform:scale(1)}}
454
- }}
455
- @keyframes arrowPulse{{
456
- 0%,100%{{opacity:0.2;color:#cbd5e1}}
457
- {100/ns:.0f}%{{opacity:1;color:#6366f1}}
458
- {200/ns:.0f}%{{opacity:0.2;color:#cbd5e1}}
459
- }}
460
- @keyframes dotBounce{{
461
- 0%,100%{{transform:translateY(0)}}
462
- 50%{{transform:translateY(-6px)}}
463
- }}
464
- {card_kf}
465
- </style>
466
- </div>'''
467
-
468
- def _status(state, msg, model, color):
469
- dot = "●" if state == "Running" else "✓"
470
- return f'<div style="display:flex;align-items:center;gap:10px;padding:10px 16px;background:rgba({_hex_rgb(color)},.06);border:1px solid rgba({_hex_rgb(color)},.15);border-radius:10px;font-family:monospace;font-size:11px"><span style="color:{color};font-weight:700">{dot} {state}</span><span style="color:#475569">{_esc(model)}</span><span style="color:#94a3b8">·</span><span style="color:#64748b">{_esc(msg)}</span></div>'
471
-
472
- # ════════════════════════════════════════════════════════════════
473
- # Build Marl
474
- # ════════════════════════════════════════════════════════════════
475
-
476
- def _on_backend(backend):
477
- reg = MODELS.get(backend, {})
478
- ml, dv, ek = reg.get("list",[]), reg.get("default",""), reg.get("env","")
479
- return gr.Dropdown(choices=ml, value=dv), gr.Textbox(placeholder=f"ENV: {ek}" if ek else "API Key")
480
-
481
- def _build(backend, api_key, model, base_url):
482
- if not MARL_OK:
483
- return None, "❌ marl package failed to load. Check Space logs."
484
- cfg = MarlConfig(include_trace=True, return_final_only=True)
485
- reg = MODELS.get(backend, {})
486
- model = model or reg.get("default","")
487
- ek = reg.get("env","")
488
- k = api_key or (os.getenv(ek,"") if ek else "")
489
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
490
  try:
491
- if backend == "OpenAI":
492
- if not k: return None, " OPENAI_API_KEY required"
493
- return Marl.from_openai(k, model, cfg), ""
494
- elif backend == "Anthropic":
495
- if not k: return None, "❌ ANTHROPIC_API_KEY required"
496
- return Marl.from_anthropic(k, model, cfg), "✅"
497
- elif backend == "Google Gemini":
498
- k = k or os.getenv("GEMINI_API_KEY","")
499
- if not k: return None, "❌ GOOGLE_API_KEY required"
500
- return Marl.from_openai_compatible("https://generativelanguage.googleapis.com/v1beta/openai", k, model, cfg), "✅"
501
- elif backend == "DeepSeek":
502
- if not k: return None, "❌ DEEPSEEK_API_KEY required"
503
- return Marl.from_openai_compatible("https://api.deepseek.com/v1", k, model, cfg), "✅"
504
- elif backend == "xAI (Grok)":
505
- if not k: return None, "❌ XAI_API_KEY required"
506
- return Marl.from_openai_compatible("https://api.x.ai/v1", k, model, cfg), "✅"
507
- elif backend == "Ollama (Local)":
508
- return Marl.from_ollama(model, base_url or "http://localhost:11434", cfg), "✅"
509
- elif backend == "Custom (OpenAI-compatible)":
510
- if not base_url: return None, "❌ Base URL required"
511
- return Marl.from_openai_compatible(base_url, api_key or "", model or "default", cfg), "✅"
512
  except Exception as e:
513
- return None, f"❌ Build error: {e}"
514
- return None, "❌ Unsupported"
515
 
516
- # ════════════════════════════════════════════════════════════════
517
- # A/B Test (Streaming)
518
- # ════════════════════════════════════════════════════════════════
519
 
520
- def run_ab_test(prompt, backend, api_key, model, base_url, budget, mode_sel, etype_sel):
521
- if not prompt.strip():
522
- yield ('<div style="color:#e11d48;padding:12px">❌ Enter a prompt</div>',"","","")
523
- return
524
- ml, st = _build(backend, api_key, model, base_url)
525
- if not ml:
526
- yield (f'<div style="color:#e11d48;padding:12px">{_esc(st)}</div>',"","","")
527
  return
528
- ml.config.budget_scale = float(budget)
529
- # Set mode
530
- _MODE_MAP = {"🔬 Insight": "insight", "🎨 Emergence": "emergence"}
531
- _ETYPE_MAP = {"🔧 Invent": "invent", "✨ Create": "create", "🍳 Recipe": "recipe", "💊 Pharma": "pharma", "🧬 Genomics": "genomics", "🧪 Chemistry": "chemistry", "🌍 Ecology": "ecology", "⚖️ Law": "law", "📄 Document": "document"}
532
- ml.config.mode = _MODE_MAP.get(mode_sel, "insight")
533
- ml.config.emergence_type = _ETYPE_MAP.get(etype_sel, "invent")
534
- mode_label = f"{mode_sel}" + (f" · {etype_sel}" if "Emergence" in mode_sel else "")
535
-
536
- # Show both animations simultaneously
537
- yield (_status("Running",f"{mode_label} · Running Raw LLM + MARL in parallel...",model,"#6366f1"),
538
- _pipeline_anim("raw", "Generating Raw LLM response..."),
539
- _pipeline_anim("marl", "Running MARL pipeline..."),
540
- "")
541
-
542
- # ── Parallel execution ──
543
- from concurrent.futures import ThreadPoolExecutor
544
  t0 = time.time()
545
- with ThreadPoolExecutor(max_workers=2) as pool:
546
- future_raw = pool.submit(ml.call_fn, prompt, "Answer thoroughly.", 4096, 0.6)
547
- future_marl = pool.submit(ml.run, prompt)
548
- raw = future_raw.result()
549
- r = future_marl.result()
550
- t_total = time.time() - t0
551
-
552
- yield (_status("Complete",f"Parallel complete {t_total:.1f}s · {len(r.fixes)} corrections",model,"#16a34a"),
553
- _result_html(raw,False), _marl_result_html(r.answer, r.trace), _trace_html(r.trace))
554
-
555
- def run_marl_only(prompt, backend, api_key, model, base_url, budget, mode_sel, etype_sel):
556
- if not prompt.strip():
557
- yield ('<div style="color:#e11d48;padding:12px">❌ Enter a prompt</div>',"","","")
558
- return
559
- ml, st = _build(backend, api_key, model, base_url)
560
- if not ml:
561
- yield (f'<div style="color:#e11d48;padding:12px">{_esc(st)}</div>',"","","")
562
- return
563
- ml.config.budget_scale = float(budget)
564
- _MODE_MAP = {"🔬 Insight": "insight", "🎨 Emergence": "emergence"}
565
- _ETYPE_MAP = {"🔧 Invent": "invent", "✨ Create": "create", "🍳 Recipe": "recipe", "💊 Pharma": "pharma", "🧬 Genomics": "genomics", "🧪 Chemistry": "chemistry", "🌍 Ecology": "ecology", "⚖️ Law": "law", "📄 Document": "document"}
566
- ml.config.mode = _MODE_MAP.get(mode_sel, "insight")
567
- ml.config.emergence_type = _ETYPE_MAP.get(etype_sel, "invent")
568
- mode_label = f"{mode_sel}" + (f" · {etype_sel}" if "Emergence" in mode_sel else "")
569
- yield (_status("Running",f"{mode_label} · MARL pipeline...",model,"#6366f1"),
570
- "",_pipeline_anim("marl", "Running MARL pipeline..."),"")
571
- t0=time.time(); r=ml.run(prompt); t_marl=time.time()-t0
572
- yield (_status("Complete",f"MARL {t_marl:.1f}s · {len(r.fixes)} corrections",model,"#16a34a"),
573
- "", _marl_result_html(r.answer, r.trace), _trace_html(r.trace))
574
-
575
- # ════════════════════════════════════════════════════════════════
576
- # Gradio App
577
- # ════════════════════════════════════════════════════════════════
578
-
579
- def create_app():
580
- init_m = MODELS["OpenAI"]["list"]
581
-
582
- with gr.Blocks(title="MARL Model-Agnostic Runtime Middleware") as app:
583
- gr.HTML(INDEX_HTML)
584
-
585
- with gr.Tabs():
586
- with gr.Tab(" Playground"):
587
- with gr.Row():
588
- backend = gr.Dropdown(label="Backend", choices=BACKEND_LIST, value="OpenAI", scale=2)
589
- api_key = gr.Textbox(label="API Key", type="password", placeholder="Enter your API key (required)",
590
- value=os.getenv("OPENAI_API_KEY",""), scale=3)
591
- with gr.Row():
592
- model = gr.Dropdown(label="Model", choices=init_m, value="gpt-5.4",
593
- allow_custom_value=True, scale=3)
594
- base_url = gr.Textbox(label="Base URL (Custom/Ollama)",
595
- placeholder="http://localhost:11434", scale=2)
596
- budget = gr.Slider(0.3, 3.0, value=1.0, step=0.1, label="Budget Scale", scale=1)
597
-
598
- with gr.Row():
599
- mode = gr.Radio(["🔬 Insight", "🎨 Emergence"],
600
- value="🔬 Insight", label="Mode", scale=2)
601
- etype = gr.Radio(["🔧 Invent", "✨ Create", "🍳 Recipe", "💊 Pharma", "🧬 Genomics", "🧪 Chemistry", "🌍 Ecology", "⚖️ Law", "📄 Document"],
602
- value="🔧 Invent", label="Emergence Engine", scale=2, visible=False)
603
-
604
- def _on_mode(m):
605
- return gr.Radio(visible="Emergence" in m)
606
- mode.change(fn=_on_mode, inputs=[mode], outputs=[etype])
607
-
608
- backend.change(fn=_on_backend, inputs=[backend], outputs=[model, api_key])
609
-
610
- prompt = gr.Textbox(label="Prompt", placeholder="Enter your question or task...", lines=3)
611
-
612
- EXAMPLES = [
613
- ("🔬", "Is 0.9999... less than 1? Prove your answer with two different mathematical approaches.",
614
- "🔬 Insight", "🔧 Invent"),
615
- ("🔬", "A startup claims their AI detects cancer with 99.9% accuracy from a selfie. As a medical advisor, evaluate this claim — what critical information is missing?",
616
- "🔬 Insight", "🔧 Invent"),
617
- ("🔧", "Invent a device that allows dementia patients to live safely at home alone. Fuse sensors, AI, and UX — under $50/month. Identify the top 3 failure modes.",
618
- "🎨 Emergence", "🔧 Invent"),
619
- ("🔧", "Design a building material that detects its own cracks and self-heals. What existing material science makes this feasible vs. science fiction?",
620
- "🎨 Emergence", "🔧 Invent"),
621
- ("✨", "Write a single movie logline that would make both A24 and Marvel want to bid. Explain why the concept bridges arthouse and blockbuster.",
622
- "🎨 Emergence", " Create"),
623
- ("✨", "A museum wants to create an exhibit where visitors experience 'the feeling of forgetting.' Design the concept — what do they see, hear, and feel?",
624
- "🎨 Emergence", " Create"),
625
- ("🍳", "Can you truly replicate Korean beef bulgogi taste using only plant-based ingredients? Analyze the Maillard reaction chemistry and propose the closest possible recipe.",
626
- "🎨 Emergence", "🍳 Recipe"),
627
- ("🍳", "A Michelin chef claims instant ramen can never be fine dining. Prove them wrong design one dish that could change their mind, with the chemistry behind each choice.",
628
- "🎨 Emergence", "🍳 Recipe"),
629
- ("📄", "Our company's turnover rate hit 30% this year. The CEO blames salary, but HR says it's culture. Analyze both hypotheses with data-driven counter-arguments.",
630
- "🎨 Emergence", "📄 Document"),
631
- ("📄", "Write a policy brief arguing BOTH sides of whether governments should ban deepfake technology. Which side has the stronger evidence?",
632
- "🎨 Emergence", "📄 Document"),
633
- ("💊", "Viagra was originally a heart drug. Identify ONE existing approved drug and build a rigorous case for repositioning it to treat Alzheimer's. Include mechanism, evidence gaps, and risks.",
634
- "🎨 Emergence", "💊 Pharma"),
635
- ("💊", "A pharma company claims their new Alzheimer's drug reverses cognitive decline by 40%. What hidden assumptions in their clinical trial design should an FDA reviewer challenge?",
636
- "🎨 Emergence", "💊 Pharma"),
637
- ("🧬", "BRCA-PARP synthetic lethality revolutionized cancer therapy. Propose ONE new synthetic lethality pair with biological rationale for why simultaneous inhibition would selectively kill cancer cells.",
638
- "🎨 Emergence", "🧬 Genomics"),
639
- ("🧬", "A preprint claims gut microbiome directly causes Parkinson's disease. Evaluate the causal claim — what would a definitive study need to prove this beyond correlation?",
640
- "🎨 Emergence", "🧬 Genomics"),
641
- ("🧪", "Is it physically possible to combine graphene-level strength with rubber-level flexibility in a single material? Analyze the trade-offs and propose the most feasible architecture.",
642
- "🎨 Emergence", "🧪 Chemistry"),
643
- ("🧪", "A startup claims they can convert spent lithium batteries into solid-state battery materials at 90% efficiency. What are the thermodynamic limits they're likely ignoring?",
644
- "🎨 Emergence", "🧪 Chemistry"),
645
- ("🌍", "An island nation is sinking due to climate change. They have $10M. Should they invest in sea walls, coral restoration, or relocation? Analyze the trade-offs with a 50-year horizon.",
646
- "🎨 Emergence", "🌍 Ecology"),
647
- ("🌍", "Invasive lionfish are destroying Caribbean reefs. Can this threat be turned into a profitable industry? Analyze the ecological risks of commercializing an invasive species.",
648
- "🎨 Emergence", "🌍 Ecology"),
649
- ("⚖️", "A self-driving car kills a pedestrian. Under EU law, the manufacturer is liable. Under US law, the software developer is. Under Korean law, it's unclear. Design a framework that resolves all three.",
650
- "🎨 Emergence", "⚖️ Law"),
651
- ("⚖️", "An AI generates a novel that becomes a bestseller. The AI was trained on copyrighted books. Who owns the copyright? Analyze under common law vs. civil law and propose a new doctrine.",
652
- "🎨 Emergence", "⚖️ Law"),
653
- ]
654
- gr.HTML('<div style="font-size:10px;font-weight:700;color:#8b90b0;text-transform:uppercase;letter-spacing:1.5px;font-family:var(--mono);margin:12px 0 6px">💡 EXAMPLES — click to auto-fill prompt & mode</div>')
655
- with gr.Row():
656
- ex_btns = []
657
- for i in range(5):
658
- icon, text = EXAMPLES[i][0], EXAMPLES[i][1]
659
- ex_btns.append(gr.Button(f"{icon} {text[:42]}...", size="sm", scale=1, min_width=60))
660
- with gr.Row():
661
- for i in range(5, 10):
662
- icon, text = EXAMPLES[i][0], EXAMPLES[i][1]
663
- ex_btns.append(gr.Button(f"{icon} {text[:42]}...", size="sm", scale=1, min_width=60))
664
- with gr.Row():
665
- for i in range(10, 15):
666
- icon, text = EXAMPLES[i][0], EXAMPLES[i][1]
667
- ex_btns.append(gr.Button(f"{icon} {text[:42]}...", size="sm", scale=1, min_width=60))
668
- with gr.Row():
669
- for i in range(15, 20):
670
- icon, text = EXAMPLES[i][0], EXAMPLES[i][1]
671
- ex_btns.append(gr.Button(f"{icon} {text[:42]}...", size="sm", scale=1, min_width=60))
672
-
673
- for i, btn in enumerate(ex_btns):
674
- _, ex_prompt, ex_mode, ex_etype = EXAMPLES[i]
675
- is_emergence = "Emergence" in ex_mode
676
- btn.click(fn=lambda p=ex_prompt, m=ex_mode, e=ex_etype, v=is_emergence: (p, m, gr.Radio(value=e, visible=v)),
677
- outputs=[prompt, mode, etype])
678
-
679
- with gr.Row():
680
- ab_btn = gr.Button("⚡ A/B Test · Raw LLM vs MARL", variant="primary", size="lg", scale=3)
681
- marl_btn = gr.Button("🧠 MARL Only", variant="secondary", size="lg", scale=2)
682
-
683
- status = gr.HTML()
684
-
685
- gr.HTML('<div style="display:flex;gap:12px;margin:12px 0"><div style="flex:1;text-align:center;font-family:JetBrains Mono,monospace;font-size:10px;font-weight:700;letter-spacing:1.5px;text-transform:uppercase;color:#64748b;padding:10px;background:#f5f6fa;border-radius:10px 10px 0 0;border:1px solid #e2e5f0;border-bottom:2px solid #e2e5f0">🤖 A · Raw LLM</div><div style="flex:1;text-align:center;font-family:JetBrains Mono,monospace;font-size:10px;font-weight:700;letter-spacing:1.5px;text-transform:uppercase;color:#6366f1;padding:10px;background:rgba(99,102,241,.04);border-radius:10px 10px 0 0;border:1px solid rgba(99,102,241,.12);border-bottom:2px solid #6366f1">🧠 B · MARL-Enhanced</div></div>')
686
-
687
- with gr.Row():
688
- raw_out = gr.HTML()
689
- marl_out = gr.HTML()
690
-
691
- with gr.Accordion("📊 Pipeline Trace — 5-Stage Agent Outputs", open=False):
692
- trace_out = gr.HTML()
693
-
694
- ins = [prompt, backend, api_key, model, base_url, budget, mode, etype]
695
- outs = [status, raw_out, marl_out, trace_out]
696
- ab_btn.click(fn=run_ab_test, inputs=ins, outputs=outs)
697
- marl_btn.click(fn=run_marl_only, inputs=ins, outputs=outs)
698
-
699
- with gr.Tab("📦 Integration Guide"):
700
- gr.HTML('''<div style="font-family:Sora,sans-serif;max-width:920px;margin:0 auto;padding:20px;color:#0f172a;line-height:1.7">
701
-
702
- <div style="display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-bottom:24px">
703
- <div style="background:var(--surface,#fff);border:1px solid #e2e5f0;border-radius:16px;padding:20px;box-shadow:0 1px 3px rgba(15,23,42,.04)">
704
- <div style="font-size:8px;font-family:JetBrains Mono,monospace;color:#6366f1;text-transform:uppercase;letter-spacing:2px;font-weight:700;margin-bottom:6px">Quick Start</div>
705
- <pre style="margin:0;font-family:JetBrains Mono,monospace;font-size:14px;color:#6366f1;background:#f5f6fa;padding:10px;border-radius:10px;border:1px solid #e2e5f0"><code>pip install marl-middleware</code></pre>
706
- <div style="margin-top:6px;font-size:9px;color:#94a3b8">Linux x86_64 / Python 3.12 · Other OS → Docker</div>
707
- </div>
708
- <div style="background:var(--surface,#fff);border:1px solid #e2e5f0;border-radius:16px;padding:20px;box-shadow:0 1px 3px rgba(15,23,42,.04)">
709
- <div style="font-size:8px;font-family:JetBrains Mono,monospace;color:#0d9488;text-transform:uppercase;letter-spacing:2px;font-weight:700;margin-bottom:6px">Docker (All Platforms)</div>
710
- <pre style="margin:0;font-family:JetBrains Mono,monospace;font-size:14px;color:#0d9488;background:#f5f6fa;padding:10px;border-radius:10px;border:1px solid #e2e5f0"><code>docker run -p 8080:8080 vidraft/marl</code></pre>
711
- <div style="margin-top:6px;font-size:9px;color:#94a3b8">Mac · Windows · Linux — works everywhere</div>
712
- </div>
713
- </div>
714
-
715
- <div style="background:var(--surface,#fff);border:1px solid #e2e5f0;border-radius:16px;padding:20px;margin-bottom:16px;box-shadow:0 1px 3px rgba(15,23,42,.04)">
716
- <div style="font-size:10px;font-family:JetBrains Mono,monospace;font-weight:700;color:#6366f1;text-transform:uppercase;letter-spacing:.8px;margin-bottom:10px">⚡ 1-LINE INTEGRATION</div>
717
- <p style="color:#475569;font-size:11px;margin-bottom:10px">Add <b>one line</b> to any OpenAI-compatible app:</p>
718
- <pre style="background:#f5f6fa;padding:14px;border-radius:10px;font-family:JetBrains Mono,monospace;font-size:11px;line-height:1.8;border:1px solid #e2e5f0;overflow-x:auto"><code><span style="color:#94a3b8"># Before</span>
719
- client = OpenAI(api_key=<span style="color:#d97706">"sk-..."</span>)
720
-
721
- <span style="color:#94a3b8"># After just add base_url</span>
722
- client = OpenAI(api_key=<span style="color:#d97706">"sk-..."</span>, <span style="color:#e11d48">base_url=</span><span style="color:#d97706">"http://localhost:8080/v1"</span>)</code></pre>
723
- </div>
724
-
725
- <div style="background:var(--surface,#fff);border:1px solid #e2e5f0;border-radius:16px;padding:20px;margin-bottom:16px;box-shadow:0 1px 3px rgba(15,23,42,.04)">
726
- <div style="font-size:10px;font-family:JetBrains Mono,monospace;font-weight:700;color:#6366f1;text-transform:uppercase;letter-spacing:.8px;margin-bottom:10px">🎨 9 EMERGENCE MODES</div>
727
- <p style="color:#475569;font-size:11px;margin-bottom:10px">Append <code style="background:rgba(99,102,241,.06);padding:2px 6px;border-radius:4px;color:#6366f1;font-family:JetBrains Mono,monospace;font-size:10px">::mode</code> to any model name:</p>
728
- <div style="overflow-x:auto">
729
- <table style="width:100%;border-collapse:separate;border-spacing:0;font-size:11px;border-radius:10px;overflow:hidden;border:1px solid #e2e5f0">
730
- <tr style="background:#f5f6fa"><th style="text-align:left;padding:8px 10px;font-family:JetBrains Mono,monospace;font-size:8px;color:#94a3b8;text-transform:uppercase;letter-spacing:.5px">model</th><th style="text-align:left;padding:8px 10px;font-family:JetBrains Mono,monospace;font-size:8px;color:#94a3b8;text-transform:uppercase;letter-spacing:.5px">Mode</th><th style="text-align:left;padding:8px 10px;font-family:JetBrains Mono,monospace;font-size:8px;color:#94a3b8;text-transform:uppercase;letter-spacing:.5px">Seeds</th></tr>
731
- <tr><td style="padding:7px 10px;border-top:1px solid #e2e5f0"><code style="color:#6366f1;font-weight:600;font-family:JetBrains Mono,monospace;font-size:10px">gpt-5.2</code></td><td style="padding:7px 10px;border-top:1px solid #e2e5f0">🔬 Insight (default)</td><td style="padding:7px 10px;border-top:1px solid #e2e5f0;color:#94a3b8;font-size:10px">Fact-check · Strategy</td></tr>
732
- <tr style="background:#fafbfe"><td style="padding:7px 10px;border-top:1px solid #e2e5f0"><code style="color:#6366f1;font-weight:600;font-family:JetBrains Mono,monospace;font-size:10px">::invent</code></td><td style="padding:7px 10px;border-top:1px solid #e2e5f0">🔧 Invent</td><td style="padding:7px 10px;border-top:1px solid #e2e5f0;color:#94a3b8;font-size:10px">4,318 tech items</td></tr>
733
- <tr><td style="padding:7px 10px;border-top:1px solid #e2e5f0"><code style="color:#6366f1;font-weight:600;font-family:JetBrains Mono,monospace;font-size:10px">::create</code></td><td style="padding:7px 10px;border-top:1px solid #e2e5f0">✨ Create</td><td style="padding:7px 10px;border-top:1px solid #e2e5f0;color:#94a3b8;font-size:10px">493 seeds (11 categories)</td></tr>
734
- <tr style="background:#fafbfe"><td style="padding:7px 10px;border-top:1px solid #e2e5f0"><code style="color:#6366f1;font-weight:600;font-family:JetBrains Mono,monospace;font-size:10px">::recipe</code></td><td style="padding:7px 10px;border-top:1px solid #e2e5f0">🍳 Recipe</td><td style="padding:7px 10px;border-top:1px solid #e2e5f0;color:#94a3b8;font-size:10px">131 methods · textures</td></tr>
735
- <tr><td style="padding:7px 10px;border-top:1px solid #e2e5f0"><code style="color:#6366f1;font-weight:600;font-family:JetBrains Mono,monospace;font-size:10px">::pharma</code></td><td style="padding:7px 10px;border-top:1px solid #e2e5f0">💊 Pharma</td><td style="padding:7px 10px;border-top:1px solid #e2e5f0;color:#94a3b8;font-size:10px">172 targets · mechanisms</td></tr>
736
- <tr style="background:#fafbfe"><td style="padding:7px 10px;border-top:1px solid #e2e5f0"><code style="color:#6366f1;font-weight:600;font-family:JetBrains Mono,monospace;font-size:10px">::genomics</code></td><td style="padding:7px 10px;border-top:1px solid #e2e5f0">🧬 Genomics</td><td style="padding:7px 10px;border-top:1px solid #e2e5f0;color:#94a3b8;font-size:10px">104 genes · pathways</td></tr>
737
- <tr><td style="padding:7px 10px;border-top:1px solid #e2e5f0"><code style="color:#6366f1;font-weight:600;font-family:JetBrains Mono,monospace;font-size:10px">::chemistry</code></td><td style="padding:7px 10px;border-top:1px solid #e2e5f0">🧪 Chemistry</td><td style="padding:7px 10px;border-top:1px solid #e2e5f0;color:#94a3b8;font-size:10px">135 elements · properties</td></tr>
738
- <tr style="background:#fafbfe"><td style="padding:7px 10px;border-top:1px solid #e2e5f0"><code style="color:#6366f1;font-weight:600;font-family:JetBrains Mono,monospace;font-size:10px">::ecology</code></td><td style="padding:7px 10px;border-top:1px solid #e2e5f0">🌍 Ecology</td><td style="padding:7px 10px;border-top:1px solid #e2e5f0;color:#94a3b8;font-size:10px">105 species · ecosystems</td></tr>
739
- <tr><td style="padding:7px 10px;border-top:1px solid #e2e5f0"><code style="color:#6366f1;font-weight:600;font-family:JetBrains Mono,monospace;font-size:10px">::law</code></td><td style="padding:7px 10px;border-top:1px solid #e2e5f0">⚖️ Law</td><td style="padding:7px 10px;border-top:1px solid #e2e5f0;color:#94a3b8;font-size:10px">59 jurisdictions</td></tr>
740
- <tr style="background:#fafbfe"><td style="padding:7px 10px;border-top:1px solid #e2e5f0"><code style="color:#6366f1;font-weight:600;font-family:JetBrains Mono,monospace;font-size:10px">::document</code></td><td style="padding:7px 10px;border-top:1px solid #e2e5f0">📄 Document</td><td style="padding:7px 10px;border-top:1px solid #e2e5f0;color:#94a3b8;font-size:10px">71 principles</td></tr>
741
- </table>
742
- </div>
743
- <p style="color:#94a3b8;font-size:9px;margin-top:6px;font-family:JetBrains Mono,monospace">Replace gpt-5.2 with any model — claude-sonnet, deepseek-v3, llama3, etc.</p>
744
- </div>
745
-
746
- <div style="background:var(--surface,#fff);border:1px solid #e2e5f0;border-radius:16px;padding:20px;margin-bottom:16px;box-shadow:0 1px 3px rgba(15,23,42,.04)">
747
- <div style="font-size:10px;font-family:JetBrains Mono,monospace;font-weight:700;color:#6366f1;text-transform:uppercase;letter-spacing:.8px;margin-bottom:10px">🐙 PYTHON SDK</div>
748
- <pre style="background:#f5f6fa;padding:14px;border-radius:10px;font-family:JetBrains Mono,monospace;font-size:11px;line-height:1.8;border:1px solid #e2e5f0;overflow-x:auto"><code><span style="color:#94a3b8"># OpenAI</span>
749
- <span style="color:#6366f1">from</span> marl <span style="color:#6366f1">import</span> Marl, MarlConfig
750
- ml = Marl.from_openai(<span style="color:#d97706">"sk-..."</span>, config=MarlConfig(
751
- mode=<span style="color:#d97706">"emergence"</span>, emergence_type=<span style="color:#d97706">"create"</span>
752
- ))
753
- result = ml.run(<span style="color:#d97706">"Generate 10 movie loglines"</span>)
754
-
755
- <span style="color:#94a3b8"># Anthropic</span>
756
- ml = Marl.from_anthropic(<span style="color:#d97706">"sk-ant-..."</span>)
757
-
758
- <span style="color:#94a3b8"># Ollama (local)</span>
759
- ml = Marl.from_ollama(<span style="color:#d97706">"llama3.1"</span>)
760
-
761
- <span style="color:#94a3b8"># Any OpenAI-compatible</span>
762
- ml = Marl.from_openai(<span style="color:#d97706">"sk-..."</span>, <span style="color:#d97706">"gpt-5.4"</span>)</code></pre>
763
- </div>
764
-
765
- <div style="background:var(--surface,#fff);border:1px solid #e2e5f0;border-radius:16px;padding:20px;margin-bottom:16px;box-shadow:0 1px 3px rgba(15,23,42,.04)">
766
- <div style="font-size:10px;font-family:JetBrains Mono,monospace;font-weight:700;color:#6366f1;text-transform:uppercase;letter-spacing:.8px;margin-bottom:10px">🦞 OPENCLAW INTEGRATION</div>
767
- <div style="display:grid;grid-template-columns:auto 1fr;gap:12px;align-items:start">
768
- <div style="background:linear-gradient(135deg,#6366f1,#4f46e5);color:#fff;width:28px;height:28px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-weight:800;font-size:12px;font-family:JetBrains Mono,monospace;flex-shrink:0">1</div>
769
- <div><b style="font-size:11px;color:#0f172a">Install MARL</b><pre style="background:#f5f6fa;padding:8px 12px;border-radius:8px;font-family:JetBrains Mono,monospace;font-size:11px;margin:6px 0;border:1px solid #e2e5f0"><code style="color:#0d9488">docker run -p 8080:8080 vidraft/marl</code></pre></div>
770
- <div style="background:linear-gradient(135deg,#6366f1,#4f46e5);color:#fff;width:28px;height:28px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-weight:800;font-size:12px;font-family:JetBrains Mono,monospace;flex-shrink:0">2</div>
771
- <div><b style="font-size:11px;color:#0f172a">Set config.json</b><pre style="background:#f5f6fa;padding:8px 12px;border-radius:8px;font-family:JetBrains Mono,monospace;font-size:11px;margin:6px 0;border:1px solid #e2e5f0"><code>{ <span style="color:#6366f1">"llm"</span>: { <span style="color:#6366f1">"baseURL"</span>: <span style="color:#d97706">"http://localhost:8080/v1"</span>, <span style="color:#6366f1">"model"</span>: <span style="color:#d97706">"gpt-5.2::create"</span> } }</code></pre></div>
772
- <div style="background:linear-gradient(135deg,#6366f1,#4f46e5);color:#fff;width:28px;height:28px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-weight:800;font-size:12px;font-family:JetBrains Mono,monospace;flex-shrink:0">3</div>
773
- <div><b style="font-size:11px;color:#0f172a">Chat naturally</b><div style="color:#475569;font-size:10px;margin-top:4px">"Analyze this with MARL" · "Use MARL pharma mode for drug repositioning"</div></div>
774
- </div>
775
- </div>
776
-
777
- <div style="background:var(--surface,#fff);border:1px solid #e2e5f0;border-radius:16px;padding:20px;margin-bottom:16px;box-shadow:0 1px 3px rgba(15,23,42,.04)">
778
- <div style="font-size:10px;font-family:JetBrains Mono,monospace;font-weight:700;color:#6366f1;text-transform:uppercase;letter-spacing:.8px;margin-bottom:10px">🏗️ ARCHITECTURE</div>
779
- <pre style="background:#f5f6fa;padding:14px;border-radius:10px;font-family:JetBrains Mono,monospace;font-size:10px;line-height:1.7;border:1px solid #e2e5f0;overflow-x:auto"><code><span style="color:#0d9488">┌─ Your App ────���────────────────────────────────────┐</span>
780
- │ OpenClaw / Cursor / Custom App / Any LLM Client │
781
- │ client = OpenAI(<span style="color:#e11d48">base_url=</span><span style="color:#d97706">"http://MARL:8080/v1"</span>) │
782
- <span style="color:#0d9488">└────────────────────┬───────────────────────────────┘</span>
783
- │ HTTP (OpenAI API format)
784
-
785
- <span style="color:#6366f1">┌─ MARL Middleware ──────────────────────────────────┐</span>
786
- │ S1 Hypothesis → S2 Solver → S3 Auditor │
787
- │ → S4 Verifier → S5 Synthesizer │
788
- │ <span style="color:#d97706">9 Emergence Engines · 5,538 Seeds</span> │
789
- │ <span style="color:#16a34a">FINAL Bench: MA=0.694 vs ER=0.302 (70%+ ↑)</span> │
790
- <span style="color:#6366f1">└────────────────────┬───────────────────────────────┘</span>
791
- │ API call (×5)
792
-
793
- <span style="color:#94a3b8">┌─ Any LLM ──────────────────────────────────────────┐</span>
794
- │ OpenAI · Anthropic · Gemini · DeepSeek · Ollama │
795
- <span style="color:#94a3b8">└────────────────────────────────────────────────────┘</span></code></pre>
796
- </div>
797
-
798
- <div style="background:var(--surface,#fff);border:1px solid #e2e5f0;border-radius:16px;padding:20px;margin-bottom:16px;box-shadow:0 1px 3px rgba(15,23,42,.04)">
799
- <div style="font-size:10px;font-family:JetBrains Mono,monospace;font-weight:700;color:#6366f1;text-transform:uppercase;letter-spacing:.8px;margin-bottom:10px">📡 SUPPORTED BACKENDS</div>
800
- <div style="overflow-x:auto">
801
- <table style="width:100%;border-collapse:separate;border-spacing:0;font-size:11px;border-radius:10px;overflow:hidden;border:1px solid #e2e5f0">
802
- <tr style="background:#f5f6fa"><th style="text-align:left;padding:8px 10px;font-family:JetBrains Mono,monospace;font-size:8px;color:#94a3b8;text-transform:uppercase;letter-spacing:.5px">Backend</th><th style="text-align:left;padding:8px 10px;font-family:JetBrains Mono,monospace;font-size:8px;color:#94a3b8;text-transform:uppercase;letter-spacing:.5px">Models</th></tr>
803
- <tr style="background:rgba(99,102,241,.06)"><td style="padding:8px 10px;border-top:1px solid #e2e5f0;font-weight:700;color:#6366f1">⭐ OpenAI (Default)</td><td style="padding:8px 10px;border-top:1px solid #e2e5f0;font-weight:600">GPT-5.4, GPT-5.4-pro, GPT-5.2, GPT-4o</td></tr>
804
- <tr style="background:#fafbfe"><td style="padding:8px 10px;border-top:1px solid #e2e5f0">Anthropic</td><td style="padding:8px 10px;border-top:1px solid #e2e5f0">Claude Opus 4.6, Sonnet 4.6, Haiku 4.5</td></tr>
805
- <tr><td style="padding:8px 10px;border-top:1px solid #e2e5f0">Google Gemini</td><td style="padding:8px 10px;border-top:1px solid #e2e5f0">Gemini 2.5 Pro / Flash</td></tr>
806
- <tr style="background:#fafbfe"><td style="padding:8px 10px;border-top:1px solid #e2e5f0">DeepSeek</td><td style="padding:8px 10px;border-top:1px solid #e2e5f0">V3 / R1</td></tr>
807
- <tr><td style="padding:8px 10px;border-top:1px solid #e2e5f0">xAI</td><td style="padding:8px 10px;border-top:1px solid #e2e5f0">Grok-3</td></tr>
808
- <tr style="background:#fafbfe"><td style="padding:8px 10px;border-top:1px solid #e2e5f0">Ollama</td><td style="padding:8px 10px;border-top:1px solid #e2e5f0">Llama, Mistral, Phi, Qwen</td></tr>
809
- <tr><td style="padding:8px 10px;border-top:1px solid #e2e5f0">Custom</td><td style="padding:8px 10px;border-top:1px solid #e2e5f0">Any OpenAI-compatible endpoint</td></tr>
810
- </table>
811
- </div>
812
- </div>
813
-
814
- <div style="text-align:center;padding:16px;background:#f5f6fa;border:1px solid #e2e5f0;border-radius:10px">
815
- <p style="font-size:11px;color:#6366f1;margin:0;font-weight:700;font-family:JetBrains Mono,monospace">MARL · Model-Agnostic Runtime Middleware</p>
816
- <p style="font-size:9px;color:#94a3b8;margin:4px 0 0;font-family:JetBrains Mono,monospace">pip install marl-middleware · Apache 2.0 · <b style="color:#475569">VIDRAFT.net</b></p>
817
- </div>
818
-
819
- </div>''')
820
-
821
- gr.HTML('<div style="text-align:center;padding:20px 0 8px;border-top:1px solid #e2e5f0;margin-top:16px"><p style="font-family:JetBrains Mono,monospace;font-size:8px;color:#94a3b8;letter-spacing:1px"><b style="color:#6366f1">MARL</b> · Model-Agnostic Runtime Middleware · S1→S2→S3→S4→S5 · Apache 2.0 · <b style="color:#475569">VIDRAFT.net</b></p></div>')
822
-
823
- return app
824
-
825
- print(" Creating Gradio app...")
826
- try:
827
- app = create_app()
828
- print(" ✅ App created successfully")
829
- except Exception as e:
830
- print(f" ❌ App creation failed: {e}")
831
- traceback.print_exc()
832
- sys.exit(1)
833
 
834
  if __name__ == "__main__":
835
- print(" 🚀 Launching on 0.0.0.0:7860 ...")
836
- try:
837
- app.launch(server_name="0.0.0.0", server_port=7860, ssr_mode=False)
838
- except TypeError:
839
- # ssr_mode not supported in this gradio version
840
- app.launch(server_name="0.0.0.0", server_port=7860)
 
1
  """
2
+ LastBrain: Survive everything.
3
+ ═══════════════════════════════════════════════════════════════
4
+ 3-Stage MARL Mobile Pipeline for Small Models (≤4B) on T4 GPU.
5
+
6
+ A: Raw model output (single call)
7
+ B: LastBrain 3-stage pipeline (Hypothesis → Draft+Audit → Adversarial Refine)
8
+ Judge: GPT-5.4 scores both outputs 0-100.
9
+
10
+ Built by VIDRAFT — https://vidraft.net
11
  """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
 
13
+ import json
14
+ import os
15
+ import time
16
+ import random
17
+ import torch
18
+ import gradio as gr
19
+ from dataclasses import dataclass, field
20
+ from typing import Callable, Dict, Optional
21
+ from threading import Lock
22
+ from transformers import AutoTokenizer, AutoModelForCausalLM
23
+
24
+ # ═══════════════════════════════════════════════════════════════
25
+ # LASTBRAIN CORE — 3-Stage Pipeline
26
+ # ═══════════════════════════════════════════════════════════════
27
+
28
+ @dataclass
29
+ class LastBrainConfig:
30
+ mode: str = "insight"
31
+ emergence_type: str = "invent"
32
+ s1_budget: int = 256
33
+ s2_budget: int = 2048
34
+ s5_budget: int = 2048
35
+ s1_temp: float = 0.7
36
+ s2_temp: float = 0.5
37
+ s5_temp: float = 0.4
38
+ auto_continue: bool = True
39
+ continue_budget: int = 1024
40
+ include_trace: bool = True
41
+
42
+ @dataclass
43
+ class LastBrainResult:
44
+ answer: str = ""
45
+ raw_answer: str = ""
46
+ trace: Dict[str, str] = field(default_factory=dict)
47
+ elapsed: float = 0.0
48
+ stages_elapsed: Dict[str, float] = field(default_factory=dict)
49
+
50
+ S1_SYSTEM = """You are S1_Hypothesis — Trap & Angle Detector.
51
+ Your ONLY job: find hidden traps, contradictions, and the best angle of attack.
52
+ Output EXACTLY 3 bullets, nothing more:
53
+ (1) Core trap or hidden assumption
54
+ (2) Key contradiction or missing nuance
55
+ (3) Best angle to approach this correctly
56
+ [BUDGET: 80 words MAX]"""
57
+
58
+ S2_SYSTEM = """You are S2_DraftAuditor — Solver + Self-Checker.
59
+ Write a COMPLETE answer to the task.
60
+ RULES:
61
+ - After EVERY major claim, add [CHECK: brief self-verification]
62
+ - If you're uncertain, say so explicitly
63
+ - At the end, add [GAPS: anything you might have missed]
64
+ Be thorough but concise."""
65
+
66
+ S5_SYSTEM = """You are S5_AdversarialRefiner — Final Quality Gate.
67
+ You receive a draft answer with [CHECK] tags.
68
+ Your job:
69
+ 1. Hunt for hallucination, overconfidence, logical errors, missing nuance
70
+ 2. Fix ALL errors silently
71
+ 3. Write a COMPLETE NEW final answer (not patches)
72
+ 4. Remove all [CHECK] and [GAPS] tags — output clean text only
73
+ The user sees ONLY your output. Make it perfect."""
74
+
75
+ MODE_SEEDS = {
76
+ "insight": [
77
+ "Look for common misconceptions that sound plausible",
78
+ "Check if the question contains a hidden false premise",
79
+ "Consider edge cases that change the answer completely",
80
+ "Verify if popular beliefs contradict scientific evidence",
81
+ ],
82
+ "invent": [
83
+ "[TRIZ] Segmentation: divide object into independent parts",
84
+ "[TRIZ] Asymmetry: change symmetrical form to asymmetrical",
85
+ "[BIO] Spider silk: tensile strength 5x steel at 1/6 density",
86
+ "[CONTRADICTION] Strength vs Flexibility — resolve via hierarchy",
87
+ ],
88
+ "create": [
89
+ "[TROPE] Chosen One -> The chosen one was chosen by mistake",
90
+ "[PARADOX] Bootstrap: effect precedes its own cause",
91
+ "[GENRE] Horror x Comedy -> terror played completely straight by funny people",
92
+ "[SENSE] Synesthesia: what does the color of loneliness taste like?",
93
+ ],
94
+ "recipe": [
95
+ "[FLAVOR] Umami x Acid -> fermented + citrus bridge",
96
+ "[TEXTURE] Crispy outside x Molten inside -> temperature contrast",
97
+ "[METHOD] Sous-vide precision x Wok-hei chaos -> controlled disorder",
98
+ ],
99
+ "pharma": [
100
+ "[TARGET] Repurpose: existing approved drug -> new disease indication",
101
+ "[MECHANISM] Checkpoint inhibitor logic -> apply to neurodegeneration",
102
+ "[DELIVERY] Nanoparticle encapsulation -> cross blood-brain barrier",
103
+ ],
104
+ "genomics": [
105
+ "[LETHALITY] Synthetic lethality: gene A + gene B knockout = selective kill",
106
+ "[PATHWAY] Crosstalk: MAPK <-> PI3K interaction in resistance",
107
+ "[PLATFORM] CRISPR base editing -> single nucleotide precision",
108
+ ],
109
+ "chemistry": [
110
+ "[PROPERTY] Contradictory: hard + flexible simultaneously via gradient",
111
+ "[SCALE] Nano-property -> macro-application via self-assembly",
112
+ "[BIO] Nacre structure: brick-and-mortar -> 3000x toughness increase",
113
+ ],
114
+ "ecology": [
115
+ "[TRANSFER] Island conservation success -> apply to urban fragment",
116
+ "[INVERSION] Invasive threat -> commercial resource (lionfish -> sashimi)",
117
+ "[STACK] Single intervention -> carbon + water + food + mental health",
118
+ ],
119
+ "law": [
120
+ "[JURISDICTION] EU strict liability vs US negligence -> hybrid framework",
121
+ "[COLLISION] AI-generated content x copyright law -> new doctrine needed",
122
+ "[TRANSPLANT] Data privacy framework -> apply to genetic data",
123
+ ],
124
+ "document": [
125
+ "[STRUCTURE] Argue BOTH sides before concluding",
126
+ "[EVIDENCE] Every claim needs a verifiable source or explicit uncertainty",
127
+ "[DILEMMA] Present the core tradeoff the reader must decide",
128
+ ],
129
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
130
 
131
+
132
+ class LastBrain:
133
+ """3-stage MARL pipeline: S1 Hypothesis -> S2 Draft+Audit -> S5 Adversarial Refine"""
134
+
135
+ def __init__(self, call_fn: Callable, config: Optional[LastBrainConfig] = None):
136
+ self.call_fn = call_fn
137
+ self.config = config or LastBrainConfig()
138
+
139
+ def _get_seeds(self, prompt):
140
+ mode = self.config.emergence_type if self.config.mode == "emergence" else "insight"
141
+ seeds = MODE_SEEDS.get(mode, MODE_SEEDS["insight"])
142
+ return "\n".join(random.sample(seeds, min(2, len(seeds))))
143
+
144
+ def _detect_truncation(self, text):
145
+ if not text or len(text) < 20:
146
+ return False
147
+ if text[-1] == '\n':
148
+ return False
149
+ t = text.rstrip()
150
+ if len(t) < 10:
151
+ return False
152
+ return t[-1] not in '.!?)"\':\n'
153
+
154
+ def run(self, prompt, system_context=""):
155
+ start = time.time()
156
+ trace, stages_elapsed = {}, {}
157
+ full_prompt = f"[Context]\n{system_context}\n\n[Task]\n{prompt}" if system_context else prompt
158
+ seeds = self._get_seeds(prompt)
159
+
160
+ # S1: Hypothesis + Seeds
161
+ t1 = time.time()
162
+ mode_label = self.config.emergence_type.upper() if self.config.mode == "emergence" else "INSIGHT"
163
+ s1_out = self.call_fn(full_prompt, f"{S1_SYSTEM}\n\n[MODE: {mode_label}]\n[SEEDS]\n{seeds}",
164
+ self.config.s1_budget, self.config.s1_temp)
165
+ trace["S1_Hypothesis"] = s1_out
166
+ stages_elapsed["S1"] = time.time() - t1
167
+
168
+ # S2: Draft + Inline Audit
169
+ t2 = time.time()
170
+ s2_ctx = f"[S1 ANALYSIS]\n{s1_out[:300]}\n\n" if s1_out and not s1_out.startswith("[ERROR") else ""
171
+ s2_out = self.call_fn(f"{s2_ctx}[TASK]\n{full_prompt}", S2_SYSTEM,
172
+ self.config.s2_budget, self.config.s2_temp)
173
+ if self.config.auto_continue and self._detect_truncation(s2_out):
174
+ cont = self.call_fn(
175
+ f"You were writing but got CUT OFF. Last part:\n---\n{s2_out[-500:]}\n---\n"
176
+ f"CONTINUE from exactly where you stopped. Do NOT repeat.",
177
+ "You are S2_DraftAuditor continuing. Be concise.",
178
+ self.config.continue_budget, self.config.s2_temp)
179
+ if cont and not cont.startswith("[ERROR"):
180
+ s2_out = s2_out + "\n" + cont
181
+ trace["S2_DraftAudit"] = s2_out
182
+ stages_elapsed["S2"] = time.time() - t2
183
+
184
+ # S5: Adversarial Refine
185
+ t5 = time.time()
186
+ s2_compressed = s2_out[:1500]
187
+ if len(s2_out) > 1500:
188
+ s2_compressed += "\n[... draft truncated — write your OWN complete version]"
189
+ s5_prompt = (f"[ORIGINAL TASK]\n{prompt}\n\n"
190
+ f"[S1 TRAPS]\n{s1_out[:200] if s1_out else 'none'}\n\n"
191
+ f"[S2 DRAFT — reference only, write your OWN]\n{s2_compressed}")
192
+ s5_out = self.call_fn(s5_prompt, S5_SYSTEM, self.config.s5_budget, self.config.s5_temp)
193
+ if self.config.auto_continue and self._detect_truncation(s5_out):
194
+ cont = self.call_fn(
195
+ f"You were writing the FINAL ANSWER but got CUT OFF:\n---\n{s5_out[-500:]}\n---\n"
196
+ f"CONTINUE from exactly where you stopped. Complete ALL remaining items.",
197
+ "You are S5_AdversarialRefiner continuing. Clean final text only.",
198
+ self.config.continue_budget, self.config.s5_temp)
199
+ if cont and not cont.startswith("[ERROR"):
200
+ s5_out = s5_out + "\n" + cont
201
+ trace["S5_AdversarialRefine"] = s5_out
202
+ stages_elapsed["S5"] = time.time() - t5
203
+
204
+ answer = s5_out if s5_out and not s5_out.startswith("[ERROR") else s2_out
205
+ return LastBrainResult(answer=answer, raw_answer=s2_out, trace=trace,
206
+ elapsed=time.time() - start, stages_elapsed=stages_elapsed)
207
+
208
+
209
+ # ═══════════════════════════════════════════════════════════════
210
+ # MODELS
211
+ # ═══════════════════════════════════════════════════════════════
212
  MODELS = {
213
+ "google/gemma-3n-e4b-it": {
214
+ "name": "Gemma-3n-E4B", "params": "4B (2B active)", "ram": "~2GB",
215
+ "why": "Self-correction 89, Metacognition 90 — optimal for LastBrain",
216
+ "score": 87.5, "badge": "Most Balanced",
217
+ },
218
+ "Qwen/Qwen3-4B": {
219
+ "name": "Qwen3-4B", "params": "4B", "ram": "~2.8GB",
220
+ "why": "Trap detection 100, Math 100, Coding 100 — strongest reasoning",
221
+ "score": 86.9, "badge": "Best Reasoning",
222
+ },
223
+ "Qwen/Qwen3-1.7B": {
224
+ "name": "Qwen3-1.7B", "params": "1.7B", "ram": "~1.2GB",
225
+ "why": "Metacognition 90 at 1.7B — ultralight mobile champion",
226
+ "score": 76.8, "badge": "Lightest",
227
+ },
228
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
229
 
230
+ loaded_model = {"id": None, "tokenizer": None, "model": None}
231
+ model_lock = Lock()
232
+
233
+
234
+ # ═══════════════════════════════════════════════════════════════
235
+ # GPU MODEL LOADING
236
+ # ═══════════════════════════════════════════════════════════════
237
+ def load_model(model_id, progress=gr.Progress()):
238
+ global loaded_model
239
+ if not model_id or model_id not in MODELS:
240
+ return "Select a model first."
241
+ with model_lock:
242
+ if loaded_model["id"] == model_id:
243
+ return f"Already loaded: {MODELS[model_id]['name']}"
244
+ progress(0.1, desc="Clearing GPU memory...")
245
+ if loaded_model["model"] is not None:
246
+ del loaded_model["model"]; del loaded_model["tokenizer"]
247
+ torch.cuda.empty_cache()
248
+ progress(0.3, desc=f"Loading {MODELS[model_id]['name']}...")
249
+ try:
250
+ tok = AutoTokenizer.from_pretrained(model_id, trust_remote_code=True)
251
+ mdl = AutoModelForCausalLM.from_pretrained(
252
+ model_id, torch_dtype=torch.float16, device_map="auto", trust_remote_code=True)
253
+ loaded_model.update({"id": model_id, "tokenizer": tok, "model": mdl})
254
+ vram = torch.cuda.memory_allocated() / 1024**3
255
+ progress(1.0, desc="Done!")
256
+ return f"{MODELS[model_id]['name']} loaded | VRAM: {vram:.1f}GB"
257
+ except Exception as e:
258
+ loaded_model.update({"id": None, "tokenizer": None, "model": None})
259
+ return f"Failed: {str(e)[:200]}"
260
+
261
+
262
+ # ═══════════════════════════════════════════════════════════════
263
+ # LOCAL INFERENCE
264
+ # ═══════════════════════════════════════════════════════════════
265
+ def local_generate(prompt, system="", max_tokens=2048, temperature=0.5):
266
+ if loaded_model["model"] is None:
267
+ return "[ERROR] No model loaded"
268
+ tok, mdl = loaded_model["tokenizer"], loaded_model["model"]
269
+ messages = []
270
+ if system: messages.append({"role": "system", "content": system})
271
+ messages.append({"role": "user", "content": prompt})
272
+ try:
273
+ text = tok.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
274
+ except Exception:
275
+ text = f"{system}\n\n{prompt}" if system else prompt
276
+ inputs = tok(text, return_tensors="pt", truncation=True, max_length=4096).to(mdl.device)
277
+ with torch.no_grad():
278
+ outputs = mdl.generate(**inputs, max_new_tokens=max_tokens, temperature=max(temperature, 0.01),
279
+ do_sample=True, top_p=0.9, repetition_penalty=1.1,
280
+ pad_token_id=tok.eos_token_id)
281
+ return tok.decode(outputs[0][inputs["input_ids"].shape[1]:], skip_special_tokens=True).strip()
282
+
283
+
284
+ # ═══════════════════════════════════════════════════════════════
285
+ # GPT-5.4 JUDGE
286
+ # ═══════════════════════════════════════════════════════════════
287
+ JUDGE_PROMPT = """You are an expert AI evaluator. Score two AI responses to the same question.
288
+
289
+ QUESTION:
290
+ {question}
291
+
292
+ RESPONSE A (Raw model, single call):
293
+ {response_a}
294
+
295
+ RESPONSE B (LastBrain, 3-stage pipeline):
296
+ {response_b}
297
+
298
+ Score each response on these criteria (0-100):
299
+ 1. Accuracy: Are facts correct? Any hallucination?
300
+ 2. Completeness: Are all aspects addressed?
301
+ 3. Self-awareness: Does it acknowledge uncertainty when appropriate?
302
+ 4. Reasoning depth: Is reasoning thorough and multi-layered?
303
+
304
+ Respond ONLY with JSON:
305
+ {{"score_a":{{"accuracy":N,"completeness":N,"self_awareness":N,"reasoning":N,"total":N}},"score_b":{{"accuracy":N,"completeness":N,"self_awareness":N,"reasoning":N,"total":N}},"winner":"A" or "B" or "TIE","reason":"one sentence"}}"""
306
+
307
+ def judge_with_gpt(question, response_a, response_b, api_key):
308
+ if not api_key: return {"error": "OpenAI API key required for judging"}
309
+ import requests
310
  try:
311
+ r = requests.post("https://api.openai.com/v1/chat/completions",
312
+ headers={"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"},
313
+ json={"model": "gpt-5.4", "max_completion_tokens": 500, "temperature": 0,
314
+ "messages": [{"role": "user", "content": JUDGE_PROMPT.format(
315
+ question=question[:1000], response_a=response_a[:2000], response_b=response_b[:2000])}]},
316
+ timeout=30)
317
+ text = r.json()["choices"][0]["message"]["content"].strip().replace("```json","").replace("```","").strip()
318
+ return json.loads(text)
 
 
 
 
 
 
 
 
 
 
 
 
 
319
  except Exception as e:
320
+ return {"error": str(e)[:200]}
 
321
 
 
 
 
322
 
323
+ # ═══════════════════════════════════════════════════════════════
324
+ # A/B TEST
325
+ # ═══════════════════════════════════════════════════════════════
326
+ def run_ab_test(prompt, mode, etype, api_key, progress=gr.Progress()):
327
+ if loaded_model["model"] is None:
328
+ yield "Load a model first.", "", "", "", ""
 
329
  return
330
+ mn = MODELS.get(loaded_model["id"], {}).get("name", "?")
331
+
332
+ # Raw
333
+ progress(0.1, desc="[A] Raw generating...")
 
 
 
 
 
 
 
 
 
 
 
 
334
  t0 = time.time()
335
+ raw = local_generate(prompt, "Answer thoroughly and accurately.", 2048, 0.5)
336
+ t_raw = time.time() - t0
337
+
338
+ # LastBrain
339
+ progress(0.4, desc="[B] LastBrain S1...")
340
+ cfg = LastBrainConfig(mode="emergence" if mode == "Emergence" else "insight",
341
+ emergence_type=etype.lower() if etype else "invent")
342
+ lb = LastBrain(local_generate, cfg)
343
+ result = lb.run(prompt)
344
+ t_lb = result.elapsed
345
+
346
+ se = result.stages_elapsed
347
+ status = f"""<div style="padding:12px;background:linear-gradient(135deg,#0f172a,#1e293b);border-radius:10px;color:#f1f5f9;font-family:'JetBrains Mono',monospace;font-size:12px">
348
+ <span style="color:#22d3ee;font-weight:700">LastBrain</span> <span style="color:#64748b">|</span> {mn}
349
+ <span style="color:#64748b">|</span> Raw: <span style="color:#fbbf24">{t_raw:.1f}s</span>
350
+ <span style="color:#64748b">|</span> LastBrain: <span style="color:#22d3ee">{t_lb:.1f}s</span>
351
+ <span style="color:#64748b">|</span> S1: {se.get('S1',0):.1f}s S2: {se.get('S2',0):.1f}s S5: {se.get('S5',0):.1f}s
352
+ <span style="color:#64748b">|</span> x{t_lb/max(t_raw,0.1):.1f}</div>"""
353
+
354
+ raw_html = f"""<div style="padding:16px;background:#fff;border-radius:10px;border:1px solid #e2e5f0">
355
+ <div style="font-size:11px;color:#94a3b8;margin-bottom:8px;font-family:'JetBrains Mono',monospace">
356
+ RAW {mn} | {t_raw:.1f}s | {len(raw.split())} words</div>
357
+ <div style="white-space:pre-wrap;font-size:13px;line-height:1.7">{raw}</div></div>"""
358
+
359
+ marl_html = f"""<div style="padding:16px;background:#fff;border-radius:10px;border:2px solid #22d3ee">
360
+ <div style="font-size:11px;color:#22d3ee;margin-bottom:8px;font-family:'JetBrains Mono',monospace">
361
+ LASTBRAIN {mn} | {t_lb:.1f}s | {len(result.answer.split())} words</div>
362
+ <div style="white-space:pre-wrap;font-size:13px;line-height:1.7">{result.answer}</div></div>"""
363
+
364
+ trace_parts = []
365
+ for stage, text in result.trace.items():
366
+ ts = se.get(stage.split("_")[0], 0)
367
+ trace_parts.append(f"""<details style="margin:4px 0"><summary style="cursor:pointer;font-weight:600;font-size:12px;padding:6px;background:#f8f9fc;border-radius:6px">
368
+ {stage} ({ts:.1f}s)</summary>
369
+ <pre style="white-space:pre-wrap;font-size:11px;padding:10px;background:#0f172a;color:#e2e8f0;border-radius:6px;max-height:300px;overflow-y:auto;margin-top:4px">{text}</pre></details>""")
370
+ trace_html = "\n".join(trace_parts)
371
+
372
+ yield status, raw_html, marl_html, trace_html, ""
373
+
374
+ # Judge
375
+ if api_key:
376
+ progress(0.8, desc="[Judge] GPT-5.4 evaluating...")
377
+ v = judge_with_gpt(prompt, raw, result.answer, api_key)
378
+ if "error" in v:
379
+ judge_html = f"<div style='color:#e11d48;padding:12px'>Judge error: {v['error']}</div>"
380
+ else:
381
+ sa, sb = v.get("score_a",{}), v.get("score_b",{})
382
+ w = v.get("winner","?"); reason = v.get("reason","")
383
+ wc = "#94a3b8" if w == "A" else "#22d3ee" if w == "B" else "#fbbf24"
384
+ wl = f"Raw {mn}" if w == "A" else "LastBrain" if w == "B" else "TIE"
385
+ wi = "A" if w == "A" else "B" if w == "B" else "="
386
+ judge_html = f"""<div style="padding:20px;background:linear-gradient(135deg,#0f172a,#1e293b);border-radius:12px;border:2px solid {wc}">
387
+ <div style="text-align:center;font-size:24px;font-weight:800;color:{wc};margin-bottom:16px;font-family:'JetBrains Mono',monospace;letter-spacing:1px">
388
+ WINNER: {wl} [{wi}]</div>
389
+ <div style="display:grid;grid-template-columns:1fr 1fr;gap:16px;margin-bottom:16px">
390
+ <div style="padding:14px;background:rgba(255,255,255,0.05);border-radius:8px;border:1px solid #334155">
391
+ <div style="font-weight:700;color:#94a3b8;margin-bottom:8px;font-size:13px">A Raw {mn}: {sa.get('total',0)}/100</div>
392
+ <div style="font-size:11px;color:#64748b;line-height:1.6">
393
+ Accuracy: {sa.get('accuracy',0)} | Completeness: {sa.get('completeness',0)}<br>
394
+ Self-awareness: {sa.get('self_awareness',0)} | Reasoning: {sa.get('reasoning',0)}</div></div>
395
+ <div style="padding:14px;background:rgba(34,211,238,0.08);border-radius:8px;border:1px solid #22d3ee">
396
+ <div style="font-weight:700;color:#22d3ee;margin-bottom:8px;font-size:13px">B — LastBrain: {sb.get('total',0)}/100</div>
397
+ <div style="font-size:11px;color:#64748b;line-height:1.6">
398
+ Accuracy: {sb.get('accuracy',0)} | Completeness: {sb.get('completeness',0)}<br>
399
+ Self-awareness: {sb.get('self_awareness',0)} | Reasoning: {sb.get('reasoning',0)}</div></div></div>
400
+ <div style="text-align:center;font-size:11px;color:#64748b">{reason}</div>
401
+ <div style="text-align:center;font-size:10px;color:#475569;margin-top:8px;font-family:'JetBrains Mono',monospace">
402
+ Raw: {t_raw:.1f}s | LastBrain: {t_lb:.1f}s (x{t_lb/max(t_raw,0.1):.1f}) | Judge: GPT-5.4</div></div>"""
403
+ yield status, raw_html, marl_html, trace_html, judge_html
404
+ else:
405
+ yield status, raw_html, marl_html, trace_html, "<div style='padding:12px;color:#fbbf24;background:#0f172a;border-radius:8px;font-size:12px'>Enter OpenAI API key to enable GPT-5.4 judging.</div>"
406
+
407
+
408
+ # ═══════════════════════════════════════════════════════════════
409
+ # EXAMPLES
410
+ # ═══════════════════════════════════════════════════════════════
411
+ EXAMPLES = [
412
+ ["Is 0.9999... less than 1? Prove your answer.", "Insight", "invent"],
413
+ ["A startup claims 99.9% cancer detection from a selfie. Evaluate this claim.", "Insight", "invent"],
414
+ ["Can you replicate Korean bulgogi taste with only plant-based ingredients? Explain the chemistry.", "Emergence", "recipe"],
415
+ ["Identify ONE existing drug and build a case for repositioning it to treat Alzheimer's.", "Emergence", "pharma"],
416
+ ["Is it physically possible to combine graphene-level strength with rubber-level flexibility?", "Emergence", "chemistry"],
417
+ ["A self-driving car kills a pedestrian. Design a liability framework resolving EU, US, and Korean law.", "Emergence", "law"],
418
+ ["Write ONE movie logline that would make both A24 and Marvel want to bid.", "Emergence", "create"],
419
+ ["An island nation is sinking. $10M budget. Sea walls, coral, or relocation? 50-year analysis.", "Emergence", "ecology"],
420
+ ]
421
+
422
+
423
+ # ═══════════════════════════════════════════════════════════════
424
+ # UI
425
+ # ═══════════════════════════════════════════════════════════════
426
+ with gr.Blocks(
427
+ title="LastBrain: Survive everything.",
428
+ theme=gr.themes.Base(primary_hue="cyan", neutral_hue="slate"),
429
+ css="""
430
+ .gradio-container { max-width: 1200px !important; background: #0f172a !important; }
431
+ .main { background: #0f172a !important; }
432
+ body { background: #0f172a !important; }
433
+ .dark { background: #0f172a !important; }
434
+ footer { display: none !important; }
435
+ """
436
+ ) as app:
437
+
438
+ # Header
439
+ gr.HTML("""<div style="text-align:center;padding:32px 0 16px">
440
+ <h1 style="font-size:42px;font-weight:900;margin:0;letter-spacing:-1px;
441
+ background:linear-gradient(135deg,#22d3ee,#6366f1,#a855f7);-webkit-background-clip:text;-webkit-text-fill-color:transparent">
442
+ LastBrain</h1>
443
+ <p style="color:#22d3ee;font-size:14px;font-weight:600;letter-spacing:4px;text-transform:uppercase;margin:4px 0">
444
+ Survive everything.</p>
445
+ <p style="color:#475569;font-size:11px;margin:8px 0;font-family:'JetBrains Mono',monospace">
446
+ 3-Stage Metacognitive Pipeline for Small Models | Raw vs LastBrain | Judged by GPT-5.4</p>
447
+ </div>""")
448
+
449
+ # Model selection
450
+ gr.HTML("<div style='color:#64748b;font-size:10px;text-transform:uppercase;letter-spacing:2px;padding:0 4px;margin-bottom:4px;font-weight:700'>Select Model</div>")
451
+ with gr.Row():
452
+ with gr.Column(scale=3):
453
+ model_dd = gr.Dropdown(choices=list(MODELS.keys()), label="Model", container=False)
454
+ with gr.Column(scale=1):
455
+ load_btn = gr.Button("Load on GPU", variant="primary")
456
+ load_status = gr.Textbox(label="Status", interactive=False, lines=1)
457
+ model_info = gr.HTML("")
458
+
459
+ def show_info(mid):
460
+ if not mid or mid not in MODELS: return ""
461
+ m = MODELS[mid]
462
+ return f"""<div style="padding:10px 14px;background:#1e293b;border-radius:8px;border-left:3px solid #22d3ee;margin:4px 0;font-size:12px;color:#cbd5e1">
463
+ <b style="color:#22d3ee">{m['name']}</b>
464
+ <span style="color:#475569">|</span> {m['params']}
465
+ <span style="color:#475569">|</span> RAM: {m['ram']}
466
+ <span style="color:#475569">|</span> Score: <b style="color:#fbbf24">{m['score']}</b>
467
+ <span style="color:#475569">|</span> <span style="background:#22d3ee;color:#0f172a;padding:1px 6px;border-radius:4px;font-size:10px;font-weight:700">{m['badge']}</span>
468
+ <br><span style="color:#64748b">{m['why']}</span></div>"""
469
+
470
+ model_dd.change(fn=show_info, inputs=[model_dd], outputs=[model_info])
471
+ load_btn.click(fn=load_model, inputs=[model_dd], outputs=[load_status])
472
+
473
+ # Prompt
474
+ gr.HTML("<hr style='margin:16px 0;border-color:#1e293b'>")
475
+ with gr.Row():
476
+ with gr.Column(scale=3):
477
+ prompt = gr.Textbox(label="Prompt", placeholder="Ask anything...", lines=3)
478
+ with gr.Column(scale=1):
479
+ mode = gr.Radio(["Insight", "Emergence"], value="Insight", label="Mode")
480
+ etype = gr.Dropdown(
481
+ ["invent","create","recipe","pharma","genomics","chemistry","ecology","law","document"],
482
+ value="invent", label="Engine", visible=False)
483
+ mode.change(fn=lambda m: gr.Dropdown(visible=m=="Emergence"), inputs=[mode], outputs=[etype])
484
+
485
+ api_key = gr.Textbox(label="OpenAI API Key (GPT-5.4 Judge)", type="password",
486
+ placeholder="sk-... (recommended)", value=os.getenv("OPENAI_API_KEY",""))
487
+ run_btn = gr.Button("Run A/B Test", variant="primary", size="lg")
488
+
489
+ gr.Examples(examples=EXAMPLES, inputs=[prompt, mode, etype], label="Examples")
490
+
491
+ # Output
492
+ status_out = gr.HTML()
493
+ with gr.Row():
494
+ with gr.Column():
495
+ gr.HTML("<div style='text-align:center;font-weight:700;color:#94a3b8;font-size:13px;padding:8px'>A Raw Model</div>")
496
+ raw_out = gr.HTML()
497
+ with gr.Column():
498
+ gr.HTML("<div style='text-align:center;font-weight:700;color:#22d3ee;font-size:13px;padding:8px'>B LastBrain</div>")
499
+ marl_out = gr.HTML()
500
+
501
+ judge_out = gr.HTML()
502
+
503
+ with gr.Accordion("Reasoning Trace (S1 → S2 → S5)", open=False):
504
+ trace_out = gr.HTML()
505
+
506
+ run_btn.click(fn=run_ab_test, inputs=[prompt, mode, etype, api_key],
507
+ outputs=[status_out, raw_out, marl_out, trace_out, judge_out])
508
+
509
+ gr.HTML(f"""<div style="text-align:center;padding:20px;margin-top:16px">
510
+ <span style="color:#22d3ee;font-weight:800;font-size:14px;letter-spacing:2px">LastBrain</span>
511
+ <span style="color:#475569;font-size:11px"> — Survive everything.</span><br>
512
+ <span style="color:#334155;font-size:10px;font-family:'JetBrains Mono',monospace">
513
+ S1 Hypothesis (256t) → S2 Draft+Audit (2048t) → S5 Adversarial Refine (2048t)<br>
514
+ <a href="https://vidraft.net" style="color:#475569">VIDRAFT</a> ·
515
+ <a href="https://github.com/Vidraft/MARL" style="color:#475569">GitHub</a> ·
516
+ <a href="https://pypi.org/project/marl-middleware/" style="color:#475569">PyPI</a> ·
517
+ <a href="https://clawhub.ai/Cutechicken99/marl-middleware" style="color:#475569">ClawHub</a>
518
+ </span></div>""")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
519
 
520
  if __name__ == "__main__":
521
+ app.launch(server_name="0.0.0.0", server_port=7860)