| import os |
| import uvicorn |
| import re |
| import numpy as np |
| import matplotlib |
| matplotlib.use('Agg') |
| import matplotlib.pyplot as plt |
| import mpld3 |
| import plotly.graph_objects as go |
| from fastapi import FastAPI, Request |
| from fastapi.responses import HTMLResponse |
| from llama_cpp import Llama |
|
|
| app = FastAPI() |
|
|
| |
| llm = Llama( |
| model_path="/mnt/ai_data/math_APP/qwen_math_q4_k_m.gguf", |
| n_gpu_layers=-1, |
| n_ctx=2048, |
| verbose=False |
| ) |
|
|
| def fix_plotly_colorscales(code): |
| """Intercepts Matplotlib colormap names and swaps them for Plotly equivalents.""" |
| |
| color_map = { |
| "spring": "Viridis", |
| "summer": "Plasma", |
| "autumn": "Inferno", |
| "winter": "Cividis", |
| "magma": "Magma" |
| } |
| for mpl_name, plotly_name in color_map.items(): |
| |
| code = re.sub(f"['\"]{mpl_name}['\"]", f"'{plotly_name}'", code, flags=re.IGNORECASE) |
| return code |
|
|
| @app.get("/", response_class=HTMLResponse) |
| async def index(): |
| return """ |
| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <title>Interactive Math Core</title> |
| <script src="https://cdn.tailwindcss.com"></script> |
| <style> |
| @import url('https://fonts.googleapis.com/css2?family=Fira+Code&display=swap'); |
| .code-box { font-family: 'Fira Code', monospace; } |
| iframe { border: none; width: 100%; height: 100%; border-radius: 1rem; background: white; } |
| </style> |
| </head> |
| <body class="bg-gray-950 text-gray-100 min-h-screen p-6"> |
| <div class="max-w-7xl mx-auto"> |
| <header class="mb-8 flex justify-between items-center"> |
| <h1 class="text-3xl font-bold text-transparent bg-clip-text bg-gradient-to-r from-blue-400 to-cyan-400">Math Visualizer Core</h1> |
| <div class="text-xs font-mono text-gray-500 bg-gray-900 px-3 py-1 rounded-full border border-gray-800">Resilient Engine v2.0</div> |
| </header> |
| |
| <div class="grid grid-cols-1 lg:grid-cols-12 gap-6"> |
| <div class="lg:col-span-4 space-y-4"> |
| <div class="bg-gray-900 p-5 rounded-2xl border border-gray-800 shadow-xl"> |
| <label class="block text-xs font-bold text-gray-500 uppercase mb-2">Research Prompt</label> |
| <textarea id="promptInput" rows="5" |
| class="w-full bg-gray-800 border border-gray-700 rounded-xl p-4 text-sm focus:ring-2 focus:ring-blue-500 outline-none transition-all" |
| placeholder="e.g., Create a rotating 3D torus..."></textarea> |
| <button onclick="generateVisual()" id="genBtn" |
| class="w-full mt-4 bg-blue-600 hover:bg-blue-500 py-3 rounded-xl font-bold transition-all shadow-lg shadow-blue-900/40"> |
| Execute on GPU |
| </button> |
| </div> |
| <div class="bg-gray-900 p-5 rounded-2xl border border-gray-800"> |
| <pre id="codeDisplay" class="code-box bg-black p-4 rounded-lg overflow-x-auto text-[11px] text-green-400 border border-gray-800 min-h-[200px]"></pre> |
| </div> |
| </div> |
| |
| <div class="lg:col-span-8"> |
| <div class="bg-gray-900 rounded-2xl border border-gray-800 h-[650px] relative overflow-hidden flex items-center justify-center"> |
| <div id="loader" class="hidden z-20 flex flex-col items-center"> |
| <div class="animate-spin rounded-full h-12 w-12 border-t-2 border-b-2 border-blue-500 mb-4"></div> |
| <p class="text-blue-400">Solving Geometry...</p> |
| </div> |
| <div id="vizContainer" class="w-full h-full p-2"> |
| <div id="placeholder" class="h-full flex items-center justify-center text-gray-600 italic"> |
| Ready for computation. |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
| |
| <script> |
| async function generateVisual() { |
| const prompt = document.getElementById('promptInput').value; |
| const btn = document.getElementById('genBtn'); |
| const loader = document.getElementById('loader'); |
| const vizContainer = document.getElementById('vizContainer'); |
| const placeholder = document.getElementById('placeholder'); |
| const codeBox = document.getElementById('codeDisplay'); |
| |
| if (!prompt) return; |
| |
| btn.disabled = true; |
| loader.classList.remove('hidden'); |
| placeholder.classList.add('hidden'); |
| codeBox.innerText = "# Synthesizing logic..."; |
| |
| try { |
| const response = await fetch('/process', { |
| method: 'POST', |
| headers: {'Content-Type': 'application/json'}, |
| body: JSON.stringify({ prompt: prompt }) |
| }); |
| const data = await response.json(); |
| codeBox.innerText = data.code; |
| |
| if (data.html) { |
| vizContainer.innerHTML = `<iframe srcdoc='${data.html.replace(/'/g, "'")}'></iframe>`; |
| } else if (data.error) { |
| vizContainer.innerHTML = `<div class='p-10 text-red-500 font-mono text-xs overflow-auto h-full whitespace-pre-wrap'>${data.error}</div>`; |
| } |
| } catch (err) { |
| codeBox.innerText = "# Execution failed."; |
| } finally { |
| loader.classList.add('hidden'); |
| btn.disabled = false; |
| } |
| } |
| </script> |
| </body> |
| </html> |
| """ |
|
|
| @app.post("/process") |
| async def process_request(request: Request): |
| data = await request.json() |
| user_prompt = data.get("prompt") |
| |
| instruction = ( |
| "Output ONLY raw Python code. Use plotly.graph_objects (go) for 3D/animations " |
| "and name the figure 'fig'. Use matplotlib.pyplot (plt) for 2D. " |
| "CRITICAL: Never use plt.show() or fig.show(). Use Plotly-safe colorscales." |
| ) |
| |
| formatted = f"<|im_start|>system\n{instruction}<|im_end|>\n<|im_start|>user\n{user_prompt}<|im_end|>\n<|im_start|>assistant\n" |
| output = llm(formatted, max_tokens=1536, stop=["<|im_end|>"]) |
| code = re.sub(r"```python|```", "", output['choices'][0]['text']).strip() |
| |
| |
| code = fix_plotly_colorscales(code) |
|
|
| try: |
| plt.clf() |
| plt.close('all') |
| |
| |
| def dummy_show(*args, **kwargs): pass |
| |
| exec_scope = { |
| "plt": plt, "np": np, "go": go, "mpld3": mpld3, |
| "__builtins__": __builtins__ |
| } |
| plt.show = dummy_show |
| |
| exec(code, exec_scope) |
| |
| if "fig" in exec_scope: |
| |
| html_output = exec_scope["fig"].to_html(full_html=False, include_plotlyjs='cdn') |
| else: |
| |
| html_output = mpld3.fig_to_html(plt.gcf()) |
|
|
| return {"code": code, "html": html_output} |
|
|
| except Exception as e: |
| return {"code": code, "error": str(e)} |
|
|
| if __name__ == "__main__": |
| uvicorn.run(app, host="0.0.0.0", port=8000) |