Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
|
@@ -1,840 +1,521 @@
|
|
| 1 |
"""
|
| 2 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
"
|
| 168 |
-
"
|
| 169 |
-
|
| 170 |
-
"
|
| 171 |
-
|
| 172 |
-
|
| 173 |
-
|
| 174 |
-
"
|
| 175 |
-
|
| 176 |
-
|
| 177 |
-
"
|
| 178 |
-
"
|
| 179 |
-
|
|
|
|
|
|
|
| 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 |
-
|
| 492 |
-
|
| 493 |
-
|
| 494 |
-
|
| 495 |
-
|
| 496 |
-
|
| 497 |
-
|
| 498 |
-
|
| 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
|
| 514 |
-
return None, "❌ Unsupported"
|
| 515 |
|
| 516 |
-
# ════════════════════════════════════════════════════════════════
|
| 517 |
-
# A/B Test (Streaming)
|
| 518 |
-
# ════════════════════════════════════════════════════════════════
|
| 519 |
|
| 520 |
-
|
| 521 |
-
|
| 522 |
-
|
| 523 |
-
|
| 524 |
-
|
| 525 |
-
|
| 526 |
-
yield (f'<div style="color:#e11d48;padding:12px">{_esc(st)}</div>',"","","")
|
| 527 |
return
|
| 528 |
-
|
| 529 |
-
|
| 530 |
-
|
| 531 |
-
|
| 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 |
-
|
| 546 |
-
|
| 547 |
-
|
| 548 |
-
|
| 549 |
-
|
| 550 |
-
|
| 551 |
-
|
| 552 |
-
|
| 553 |
-
|
| 554 |
-
|
| 555 |
-
|
| 556 |
-
|
| 557 |
-
|
| 558 |
-
|
| 559 |
-
|
| 560 |
-
|
| 561 |
-
|
| 562 |
-
|
| 563 |
-
|
| 564 |
-
|
| 565 |
-
|
| 566 |
-
|
| 567 |
-
|
| 568 |
-
|
| 569 |
-
|
| 570 |
-
|
| 571 |
-
|
| 572 |
-
|
| 573 |
-
|
| 574 |
-
|
| 575 |
-
|
| 576 |
-
|
| 577 |
-
|
| 578 |
-
|
| 579 |
-
|
| 580 |
-
|
| 581 |
-
|
| 582 |
-
|
| 583 |
-
|
| 584 |
-
|
| 585 |
-
|
| 586 |
-
|
| 587 |
-
|
| 588 |
-
|
| 589 |
-
|
| 590 |
-
|
| 591 |
-
|
| 592 |
-
|
| 593 |
-
|
| 594 |
-
|
| 595 |
-
|
| 596 |
-
|
| 597 |
-
|
| 598 |
-
|
| 599 |
-
|
| 600 |
-
|
| 601 |
-
|
| 602 |
-
|
| 603 |
-
|
| 604 |
-
|
| 605 |
-
|
| 606 |
-
|
| 607 |
-
|
| 608 |
-
|
| 609 |
-
|
| 610 |
-
|
| 611 |
-
|
| 612 |
-
|
| 613 |
-
|
| 614 |
-
|
| 615 |
-
|
| 616 |
-
|
| 617 |
-
|
| 618 |
-
|
| 619 |
-
|
| 620 |
-
|
| 621 |
-
|
| 622 |
-
|
| 623 |
-
|
| 624 |
-
|
| 625 |
-
|
| 626 |
-
|
| 627 |
-
|
| 628 |
-
|
| 629 |
-
|
| 630 |
-
|
| 631 |
-
|
| 632 |
-
|
| 633 |
-
|
| 634 |
-
|
| 635 |
-
|
| 636 |
-
|
| 637 |
-
|
| 638 |
-
|
| 639 |
-
|
| 640 |
-
|
| 641 |
-
|
| 642 |
-
|
| 643 |
-
|
| 644 |
-
|
| 645 |
-
|
| 646 |
-
|
| 647 |
-
|
| 648 |
-
|
| 649 |
-
|
| 650 |
-
|
| 651 |
-
|
| 652 |
-
|
| 653 |
-
|
| 654 |
-
|
| 655 |
-
|
| 656 |
-
|
| 657 |
-
|
| 658 |
-
|
| 659 |
-
|
| 660 |
-
|
| 661 |
-
|
| 662 |
-
|
| 663 |
-
|
| 664 |
-
|
| 665 |
-
|
| 666 |
-
|
| 667 |
-
|
| 668 |
-
|
| 669 |
-
|
| 670 |
-
|
| 671 |
-
|
| 672 |
-
|
| 673 |
-
|
| 674 |
-
|
| 675 |
-
|
| 676 |
-
|
| 677 |
-
|
| 678 |
-
|
| 679 |
-
|
| 680 |
-
|
| 681 |
-
|
| 682 |
-
|
| 683 |
-
|
| 684 |
-
|
| 685 |
-
|
| 686 |
-
|
| 687 |
-
|
| 688 |
-
|
| 689 |
-
|
| 690 |
-
|
| 691 |
-
|
| 692 |
-
|
| 693 |
-
|
| 694 |
-
|
| 695 |
-
|
| 696 |
-
|
| 697 |
-
|
| 698 |
-
|
| 699 |
-
|
| 700 |
-
|
| 701 |
-
|
| 702 |
-
|
| 703 |
-
|
| 704 |
-
|
| 705 |
-
<
|
| 706 |
-
|
| 707 |
-
|
| 708 |
-
<div style=
|
| 709 |
-
|
| 710 |
-
|
| 711 |
-
|
| 712 |
-
|
| 713 |
-
|
| 714 |
-
|
| 715 |
-
|
| 716 |
-
|
| 717 |
-
|
| 718 |
-
|
| 719 |
-
|
| 720 |
-
|
| 721 |
-
<span style="color:#
|
| 722 |
-
|
| 723 |
-
<
|
| 724 |
-
|
| 725 |
-
<
|
| 726 |
-
<
|
| 727 |
-
<
|
| 728 |
-
<div
|
| 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 |
-
|
| 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)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|