Antigravity Agent commited on
Commit ·
575d372
1
Parent(s): f6dedb3
feat(ui): add animated architecture flow and 'How It Works' tab
Browse files- app.py +31 -9
- flow_module.py +85 -0
- theme.css +33 -0
app.py
CHANGED
|
@@ -15,6 +15,7 @@ from verification_service import calculate_symbolic_score
|
|
| 15 |
from consensus_fusion import evaluate_consensus
|
| 16 |
from report_module import generate_mvm2_report, export_to_pdf
|
| 17 |
from image_enhancing import ImageEnhancer
|
|
|
|
| 18 |
|
| 19 |
# Initialize Engines
|
| 20 |
ocr_engine = MVM2OCREngine()
|
|
@@ -91,6 +92,9 @@ def process_mvm2_pipeline(image, auto_enhance):
|
|
| 91 |
latex_text = ocr_results['latex_output']
|
| 92 |
ocr_conf = ocr_results['weighted_confidence']
|
| 93 |
|
|
|
|
|
|
|
|
|
|
| 94 |
# 3. Multi-Agent Reasoning
|
| 95 |
agent_responses = run_agent_orchestrator(latex_text)
|
| 96 |
|
|
@@ -144,7 +148,9 @@ def process_mvm2_pipeline(image, auto_enhance):
|
|
| 144 |
pdf_path = os.path.join(tempfile.gettempdir(), f'MVM2_Report_{reports["report_id"]}.pdf')
|
| 145 |
export_to_pdf(json.loads(reports['json']), pdf_path)
|
| 146 |
|
| 147 |
-
|
|
|
|
|
|
|
| 148 |
|
| 149 |
# Build Interface
|
| 150 |
with gr.Blocks(css=css_content, title="MVM²: Senior UI AI Dashboard") as demo:
|
|
@@ -171,13 +177,29 @@ with gr.Blocks(css=css_content, title="MVM²: Senior UI AI Dashboard") as demo:
|
|
| 171 |
|
| 172 |
# --- CENTER STAGE: Canvas ---
|
| 173 |
with gr.Column(scale=2):
|
| 174 |
-
gr.
|
| 175 |
-
|
| 176 |
-
|
| 177 |
-
|
| 178 |
-
|
| 179 |
-
|
| 180 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 181 |
|
| 182 |
# --- RIGHT PANEL: Signal Intel ---
|
| 183 |
with gr.Column(scale=1, variant="panel"):
|
|
@@ -201,7 +223,7 @@ with gr.Blocks(css=css_content, title="MVM²: Senior UI AI Dashboard") as demo:
|
|
| 201 |
run_btn.click(
|
| 202 |
fn=process_mvm2_pipeline,
|
| 203 |
inputs=[input_img, enhance_toggle],
|
| 204 |
-
outputs=[preview_output, canvas_latex, signal_gauges, calib_bar_html, trace_html, download_btn]
|
| 205 |
)
|
| 206 |
|
| 207 |
if __name__ == "__main__":
|
|
|
|
| 15 |
from consensus_fusion import evaluate_consensus
|
| 16 |
from report_module import generate_mvm2_report, export_to_pdf
|
| 17 |
from image_enhancing import ImageEnhancer
|
| 18 |
+
from flow_module import generate_flow_html
|
| 19 |
|
| 20 |
# Initialize Engines
|
| 21 |
ocr_engine = MVM2OCREngine()
|
|
|
|
| 92 |
latex_text = ocr_results['latex_output']
|
| 93 |
ocr_conf = ocr_results['weighted_confidence']
|
| 94 |
|
| 95 |
+
# Update flow for current state (Visual sync)
|
| 96 |
+
flow_html = generate_flow_html("reasoning") # Transitioning to reasoning
|
| 97 |
+
|
| 98 |
# 3. Multi-Agent Reasoning
|
| 99 |
agent_responses = run_agent_orchestrator(latex_text)
|
| 100 |
|
|
|
|
| 148 |
pdf_path = os.path.join(tempfile.gettempdir(), f'MVM2_Report_{reports["report_id"]}.pdf')
|
| 149 |
export_to_pdf(json.loads(reports['json']), pdf_path)
|
| 150 |
|
| 151 |
+
final_flow = generate_flow_html("success")
|
| 152 |
+
|
| 153 |
+
return preview_img, latex_text, gauges_html, conf_bar, md_report, pdf_path, final_flow
|
| 154 |
|
| 155 |
# Build Interface
|
| 156 |
with gr.Blocks(css=css_content, title="MVM²: Senior UI AI Dashboard") as demo:
|
|
|
|
| 177 |
|
| 178 |
# --- CENTER STAGE: Canvas ---
|
| 179 |
with gr.Column(scale=2):
|
| 180 |
+
with gr.Tabs():
|
| 181 |
+
with gr.TabItem("Solver Visualization"):
|
| 182 |
+
gr.Markdown("### 🎨 MVM² Verification Canvas")
|
| 183 |
+
with gr.Group(elem_classes="glass-card"):
|
| 184 |
+
canvas_latex = gr.Textbox(label="Canonical LaTeX Transcription", lines=2, interactive=False, elem_classes="monospace")
|
| 185 |
+
calib_bar_html = gr.HTML()
|
| 186 |
+
|
| 187 |
+
gr.Markdown("### 🪜 Dynamic Reasoning Trace")
|
| 188 |
+
trace_html = gr.HTML()
|
| 189 |
+
|
| 190 |
+
with gr.TabItem("How It Works (Architecture Flow)"):
|
| 191 |
+
gr.Markdown("### 🚀 Real-Time Pipeline Visualization")
|
| 192 |
+
flow_view = gr.HTML(generate_flow_html("idle"))
|
| 193 |
+
gr.Markdown(
|
| 194 |
+
"""
|
| 195 |
+
**Pipeline Phases:**
|
| 196 |
+
1. **Enhance:** CLAHE & Gaussian Blur noise reduction.
|
| 197 |
+
2. **OCR:** Pix2Text LaTeX structure reconstruction.
|
| 198 |
+
3. **Reasoning:** Quad-agent parallel logic processing.
|
| 199 |
+
4. **Verification:** SymPy deterministic symbolic check.
|
| 200 |
+
5. **Consensus:** Multi-signal weighted confidence fusion.
|
| 201 |
+
"""
|
| 202 |
+
)
|
| 203 |
|
| 204 |
# --- RIGHT PANEL: Signal Intel ---
|
| 205 |
with gr.Column(scale=1, variant="panel"):
|
|
|
|
| 223 |
run_btn.click(
|
| 224 |
fn=process_mvm2_pipeline,
|
| 225 |
inputs=[input_img, enhance_toggle],
|
| 226 |
+
outputs=[preview_output, canvas_latex, signal_gauges, calib_bar_html, trace_html, download_btn, flow_view]
|
| 227 |
)
|
| 228 |
|
| 229 |
if __name__ == "__main__":
|
flow_module.py
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
def generate_flow_html(active_phase="idle"):
|
| 2 |
+
"""
|
| 3 |
+
Generates animated SVG for the MVM² Architecture Flow.
|
| 4 |
+
Phases: 'idle', 'enhance', 'ocr', 'reasoning', 'verification', 'consensus', 'success'
|
| 5 |
+
"""
|
| 6 |
+
|
| 7 |
+
# Define colors
|
| 8 |
+
bg_color = "#1e293b"
|
| 9 |
+
accent = "#6366f1"
|
| 10 |
+
success = "#10b981"
|
| 11 |
+
text = "#94a3b8"
|
| 12 |
+
|
| 13 |
+
# Pulse animation logic
|
| 14 |
+
def get_pulse(phase):
|
| 15 |
+
return 'active-pulse' if active_phase == phase else ''
|
| 16 |
+
|
| 17 |
+
def get_node_style(phase):
|
| 18 |
+
if active_phase == 'success': return 'stroke: #10b981; filter: drop-shadow(0 0 8px #10b981);'
|
| 19 |
+
if active_phase == phase: return f'stroke: {accent}; filter: drop-shadow(0 0 8px {accent});'
|
| 20 |
+
return 'stroke: rgba(255,255,255,0.1);'
|
| 21 |
+
|
| 22 |
+
html = f"""
|
| 23 |
+
<div class="glass-card" style="width: 100%; height: 400px; display: flex; justify-content: center; align-items: center; overflow: hidden; position: relative;">
|
| 24 |
+
<svg width="800" height="350" viewBox="0 0 800 350" preserveAspectRatio="xMidYMid meet">
|
| 25 |
+
<!-- Definitions for markers and gradients -->
|
| 26 |
+
<defs>
|
| 27 |
+
<marker id="arrowhead" markerWidth="10" markerHeight="7" refX="0" refY="3.5" orient="auto">
|
| 28 |
+
<polygon points="0 0, 10 3.5, 0 7" fill="{text}" />
|
| 29 |
+
</marker>
|
| 30 |
+
<linearGradient id="pulse-grad" x1="0%" y1="0%" x2="100%" y2="0%">
|
| 31 |
+
<stop offset="0%" stop-color="transparent" />
|
| 32 |
+
<stop offset="50%" stop-color="{accent}" />
|
| 33 |
+
<stop offset="100%" stop-color="transparent" />
|
| 34 |
+
</linearGradient>
|
| 35 |
+
</defs>
|
| 36 |
+
|
| 37 |
+
<!-- CONNECTIONS -->
|
| 38 |
+
<!-- Enhance to OCR -->
|
| 39 |
+
<path d="M100 175 H200" class="flow-line {get_pulse('enhance')}" stroke="{text}" stroke-width="2" marker-end="url(#arrowhead)" />
|
| 40 |
+
|
| 41 |
+
<!-- OCR to Reasoning -->
|
| 42 |
+
<path d="M300 175 H400" class="flow-line {get_pulse('ocr')}" stroke="{text}" stroke-width="2" marker-end="url(#arrowhead)" />
|
| 43 |
+
|
| 44 |
+
<!-- Reasoning to Consensus (4 Parallel) -->
|
| 45 |
+
<path d="M500 100 Q 550 175 600 175" class="flow-line {get_pulse('reasoning')}" stroke="{text}" stroke-width="1.5" />
|
| 46 |
+
<path d="M500 150 Q 550 175 600 175" class="flow-line {get_pulse('reasoning')}" stroke="{text}" stroke-width="1.5" />
|
| 47 |
+
<path d="M500 200 Q 550 175 600 175" class="flow-line {get_pulse('reasoning')}" stroke="{text}" stroke-width="1.5" />
|
| 48 |
+
<path d="M500 250 Q 550 175 600 175" class="flow-line {get_pulse('reasoning')}" stroke="{text}" stroke-width="1.5" />
|
| 49 |
+
|
| 50 |
+
<!-- Symbolic Bridge -->
|
| 51 |
+
<path d="M450 300 Q 450 250 450 250" class="flow-line {get_pulse('verification')}" stroke="{success}" stroke-width="3" stroke-dasharray="5,5" />
|
| 52 |
+
|
| 53 |
+
<!-- NODES -->
|
| 54 |
+
<!-- 1. Input/Enhance -->
|
| 55 |
+
<rect x="20" y="140" width="80" height="70" rx="10" fill="{bg_color}" style="{get_node_style('enhance')}" />
|
| 56 |
+
<text x="60" y="180" text-anchor="middle" fill="white" font-size="12">ENHANCE</text>
|
| 57 |
+
|
| 58 |
+
<!-- 2. OCR Module -->
|
| 59 |
+
<circle cx="250" cy="175" r="45" fill="{bg_color}" style="{get_node_style('ocr')}" />
|
| 60 |
+
<text x="250" y="180" text-anchor="middle" fill="white" font-size="12">OCR</text>
|
| 61 |
+
|
| 62 |
+
<!-- 3. Reasoning Agents (Parallel) -->
|
| 63 |
+
<g class="{'thinking' if active_phase == 'reasoning' else ''}">
|
| 64 |
+
<circle cx="450" cy="80" r="25" fill="{bg_color}" style="{get_node_style('reasoning')}" />
|
| 65 |
+
<circle cx="450" cy="140" r="25" fill="{bg_color}" style="{get_node_style('reasoning')}" />
|
| 66 |
+
<circle cx="450" cy="200" r="25" fill="{bg_color}" style="{get_node_style('reasoning')}" />
|
| 67 |
+
<circle cx="450" cy="260" r="25" fill="{bg_color}" style="{get_node_style('reasoning')}" />
|
| 68 |
+
<text x="450" y="45" text-anchor="middle" fill="{text}" font-size="10">REASONING</text>
|
| 69 |
+
</g>
|
| 70 |
+
|
| 71 |
+
<!-- 4. Verification Bridge (SymPy) -->
|
| 72 |
+
<rect x="400" y="300" width="100" height="40" rx="5" fill="{bg_color}" style="{get_node_style('verification')}" />
|
| 73 |
+
<text x="450" y="325" text-anchor="middle" fill="{success}" font-size="10" font-weight="bold">SYMPY CORE</text>
|
| 74 |
+
|
| 75 |
+
<!-- 5. Consensus -->
|
| 76 |
+
<polygon points="650,125 750,175 650,225" fill="{bg_color}" style="{get_node_style('consensus')}" />
|
| 77 |
+
<text x="680" y="180" text-anchor="middle" fill="white" font-size="12">CONSENSUS</text>
|
| 78 |
+
</svg>
|
| 79 |
+
|
| 80 |
+
<div style="position: absolute; bottom: 20px; left: 50%; transform: translateX(-50%); font-size: 0.8em; color: {text};">
|
| 81 |
+
Current Stage: <span style="color: {accent if active_phase != 'success' else success}; font-weight: bold; text-transform: uppercase;">{active_phase}</span>
|
| 82 |
+
</div>
|
| 83 |
+
</div>
|
| 84 |
+
"""
|
| 85 |
+
return html
|
theme.css
CHANGED
|
@@ -118,6 +118,39 @@ h2 { font-weight: 600; color: var(--text-main); }
|
|
| 118 |
border-left: 4px solid var(--primary) !important;
|
| 119 |
}
|
| 120 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 121 |
/* Gradio Overrides */
|
| 122 |
.stButton button { border-radius: 10px !important; }
|
| 123 |
.stMarkdown { color: var(--text-main) !important; }
|
|
|
|
| 118 |
border-left: 4px solid var(--primary) !important;
|
| 119 |
}
|
| 120 |
|
| 121 |
+
/* Architecture Flow Animations */
|
| 122 |
+
.flow-line {
|
| 123 |
+
stroke-dasharray: 1000;
|
| 124 |
+
stroke-dashoffset: 1000;
|
| 125 |
+
animation: none;
|
| 126 |
+
}
|
| 127 |
+
|
| 128 |
+
.active-pulse {
|
| 129 |
+
stroke-dashoffset: 0;
|
| 130 |
+
animation: pulse-flow 2s infinite linear;
|
| 131 |
+
stroke: var(--primary) !important;
|
| 132 |
+
opacity: 1;
|
| 133 |
+
}
|
| 134 |
+
|
| 135 |
+
@keyframes pulse-flow {
|
| 136 |
+
0% { stroke-dashoffset: 1000; opacity: 0.3; }
|
| 137 |
+
50% { opacity: 1; }
|
| 138 |
+
100% { stroke-dashoffset: 0; opacity: 0.3; }
|
| 139 |
+
}
|
| 140 |
+
|
| 141 |
+
.thinking circle {
|
| 142 |
+
animation: thinking-pulse 1.5s infinite ease-in-out;
|
| 143 |
+
}
|
| 144 |
+
|
| 145 |
+
@keyframes thinking-pulse {
|
| 146 |
+
0%, 100% { transform: scale(1); opacity: 0.8; }
|
| 147 |
+
50% { transform: scale(1.1); opacity: 1; filter: brightness(1.2); }
|
| 148 |
+
}
|
| 149 |
+
|
| 150 |
+
.thinking {
|
| 151 |
+
transform-origin: 450px 175px;
|
| 152 |
+
}
|
| 153 |
+
|
| 154 |
/* Gradio Overrides */
|
| 155 |
.stButton button { border-radius: 10px !important; }
|
| 156 |
.stMarkdown { color: var(--text-main) !important; }
|