Spaces:
Running
Running
| <html lang="vi"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>WebGPU RAG - Debug Mode</title> | |
| <script src="coi-serviceworker.js"></script> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.min.js"></script> | |
| <script>pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.worker.min.js';</script> | |
| <script src='https://cdn.jsdelivr.net/npm/tesseract.js@5/dist/tesseract.min.js'></script> | |
| <style> | |
| body { font-family: monospace; max-width: 900px; margin: 0 auto; padding: 20px; background: #f0f0f0; } | |
| .container { display: flex; gap: 20px; } | |
| .main-panel { flex: 1; background: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 5px rgba(0,0,0,0.1); } | |
| /* Debug Console Style */ | |
| #debug-console { | |
| flex: 1; | |
| background: #1e1e1e; | |
| color: #00ff00; | |
| padding: 15px; | |
| height: 500px; | |
| overflow-y: scroll; | |
| border-radius: 8px; | |
| font-size: 12px; | |
| font-family: 'Consolas', monospace; | |
| border: 1px solid #333; | |
| } | |
| .log-time { color: #888; margin-right: 5px; } | |
| .log-info { color: #4caf50; } | |
| .log-warn { color: #ff9800; } | |
| .log-error { color: #f44336; font-weight: bold; } | |
| .log-system { color: #2196f3; font-weight: bold; } | |
| #chat-box { border: 1px solid #eee; height: 300px; overflow-y: auto; margin-bottom: 10px; padding: 10px; background: #fff; } | |
| .ai-msg { background: #e3f2fd; padding: 8px; border-radius: 5px; margin: 5px 0; } | |
| .user-msg { background: #f5f5f5; padding: 8px; border-radius: 5px; margin: 5px 0; text-align: right; } | |
| </style> | |
| </head> | |
| <body> | |
| <h2>🛠️ WebGPU RAG System Monitor</h2> | |
| <div class="container"> | |
| <div class="main-panel"> | |
| <h3>1. Input Data</h3> | |
| <input type="file" id="pdf-upload" accept="application/pdf"> | |
| <p id="status-text">Trạng thái: Đang chờ...</p> | |
| <hr> | |
| <h3>2. Chat (Granite 4.0)</h3> | |
| <div id="chat-box"></div> | |
| <input type="text" id="user-input" placeholder="Nhập câu hỏi..." style="width: 70%;"> | |
| <button onclick="sendMessage()">Gửi</button> | |
| </div> | |
| <div id="debug-console"> | |
| <div>[LOG] Hệ thống khởi động...</div> | |
| </div> | |
| </div> | |
| <script type="module"> | |
| // --- HỆ THỐNG LOGGING --- | |
| const debugConsole = document.getElementById('debug-console'); | |
| function logger(msg, type = 'info') { | |
| const now = new Date(); | |
| const timeStr = `${now.getHours()}:${now.getMinutes()}:${now.getSeconds()}.${now.getMilliseconds()}`; | |
| const div = document.createElement('div'); | |
| // Ghi vào giao diện | |
| div.innerHTML = `<span class="log-time">[${timeStr}]</span> <span class="log-${type}">${msg}</span>`; | |
| debugConsole.appendChild(div); | |
| debugConsole.scrollTop = debugConsole.scrollHeight; | |
| // Ghi song song vào F12 Console để tiện check object | |
| console.log(`[${type.toUpperCase()}]`, msg); | |
| } | |
| logger("Kiểm tra môi trường: " + (window.crossOriginIsolated ? "✅ Secure Context (WebGPU OK)" : "❌ Insecure Context (WebGPU sẽ lỗi)"), window.crossOriginIsolated ? 'system' : 'error'); | |
| // --- KHỞI TẠO WORKER --- | |
| let worker; | |
| try { | |
| logger("Đang khởi tạo Web Worker...", 'system'); | |
| worker = new Worker('worker.js', { type: 'module' }); | |
| } catch (e) { | |
| logger("Lỗi tạo Worker: " + e.message, 'error'); | |
| } | |
| let isModelReady = false; // Biến kiểm tra | |
| // Lắng nghe logs từ Worker gửi về | |
| worker.onmessage = function(e) { | |
| const { type, payload } = e.data; | |
| switch (type) { | |
| case 'log': // Log thông thường từ worker | |
| logger(`[Worker] ${payload}`, 'info'); | |
| break; | |
| case 'status': // Trạng thái tải model | |
| logger(`[Model Status] ${payload}`, 'warn'); | |
| document.getElementById('status-text').textContent = payload; | |
| break; | |
| case 'download_progress': // Tiến độ tải | |
| // Chỉ log mỗi 10% để đỡ spam | |
| if (payload.progress % 10 < 1) { | |
| logger(`[Download] ${payload.file}: ${Math.round(payload.progress)}%`, 'system'); | |
| } | |
| break; | |
| case 'init_complete': | |
| logger("✅ TẤT CẢ MODEL ĐÃ SẴN SÀNG!", 'system'); | |
| document.getElementById('status-text').textContent = "Hệ thống Online."; | |
| isModelReady = true; // <--- BẬT CỜ LÊN | |
| break; | |
| case 'answer': | |
| logger("Nhận câu trả lời từ LLM.", 'system'); | |
| appendChat("AI: " + payload, 'ai-msg'); | |
| break; | |
| case 'error': | |
| logger(`❌ LỖI WORKER: ${payload}`, 'error'); | |
| break; | |
| } | |
| }; | |
| // --- XỬ LÝ OCR (CÓ LOG CHI TIẾT) --- | |
| document.getElementById('pdf-upload').addEventListener('change', async (e) => { | |
| // THÊM ĐOẠN KIỂM TRA NÀY VÀO ĐẦU | |
| if (!isModelReady) { | |
| alert("⚠️ Vui lòng chờ AI tải xong mô hình (Xem logs bên phải)!"); | |
| e.target.value = ''; // Reset input | |
| return; | |
| } | |
| const file = e.target.files[0]; | |
| if (!file) return; | |
| logger(`Bắt đầu xử lý file: ${file.name} (${(file.size/1024/1024).toFixed(2)} MB)`, 'info'); | |
| try { | |
| const buffer = await file.arrayBuffer(); | |
| const pdf = await pdfjsLib.getDocument(buffer).promise; | |
| logger(`PDF đã tải: ${pdf.numPages} trang.`, 'info'); | |
| let fullText = ""; | |
| const workerOcr = await Tesseract.createWorker('vie', 1, { | |
| logger: m => { | |
| // Log tiến độ Tesseract (chỉ log status chính) | |
| if (m.status === 'recognizing text' && (m.progress * 100) % 20 === 0) { | |
| logger(`[OCR-Internal] ${Math.round(m.progress * 100)}%`, 'warn'); | |
| } | |
| } | |
| }); | |
| for (let i = 1; i <= pdf.numPages; i++) { | |
| logger(`Đang render trang ${i}/${pdf.numPages} ra Canvas...`, 'info'); | |
| const page = await pdf.getPage(i); | |
| const viewport = page.getViewport({ scale: 1.5 }); | |
| const canvas = document.createElement('canvas'); | |
| canvas.width = viewport.width; | |
| canvas.height = viewport.height; | |
| await page.render({ canvasContext: canvas.getContext('2d'), viewport }).promise; | |
| logger(`Đang OCR trang ${i}...`, 'info'); | |
| const { data: { text } } = await workerOcr.recognize(canvas); | |
| logger(`-> Trang ${i}: Trích xuất được ${text.length} ký tự.`, 'system'); | |
| fullText += text + "\n"; | |
| } | |
| await workerOcr.terminate(); | |
| logger(`OCR Hoàn tất. Tổng ký tự: ${fullText.length}. Gửi sang Worker để Indexing...`, 'system'); | |
| worker.postMessage({ type: 'ingest_text', payload: fullText }); | |
| } catch (err) { | |
| logger("Lỗi xử lý file: " + err.message, 'error'); | |
| } | |
| }); | |
| // --- CHAT FUNC --- | |
| window.sendMessage = () => { | |
| const input = document.getElementById('user-input'); | |
| const text = input.value; | |
| if(!text) return; | |
| appendChat("Bạn: " + text, 'user-msg'); | |
| logger(`Gửi câu hỏi: "${text}"`, 'info'); | |
| worker.postMessage({ type: 'query', payload: text }); | |
| input.value = ''; | |
| } | |
| function appendChat(text, cls) { | |
| const div = document.createElement('div'); | |
| div.className = cls; | |
| div.textContent = text; | |
| document.getElementById('chat-box').appendChild(div); | |
| } | |
| </script> | |
| </body> | |
| </html> |