File size: 7,856 Bytes
95c0719
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
import os
import uvicorn
import re
import numpy as np
import matplotlib
matplotlib.use('Agg')  # Prevents extra GUI windows from opening on your Ubuntu desktop
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()

# 1. GPU INITIALIZATION (RTX 4060 Ti)
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."""
    # Maps common Matplotlib names to their closest Plotly counterparts
    color_map = {
        "spring": "Viridis",
        "summer": "Plasma",
        "autumn": "Inferno",
        "winter": "Cividis",
        "magma": "Magma"
    }
    for mpl_name, plotly_name in color_map.items():
        # Handles both single and double quotes, and case sensitivity
        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, "&apos;")}'></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()
    
    # Apply the colorscale fix
    code = fix_plotly_colorscales(code)

    try:
        plt.clf()
        plt.close('all')
        
        # Scope with "Dummy Show" to prevent Ubuntu window popups
        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:
            # Captures Plotly (3D/Animated)
            html_output = exec_scope["fig"].to_html(full_html=False, include_plotlyjs='cdn')
        else:
            # Captures Matplotlib (2D)
            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)