Spaces:
Sleeping
Sleeping
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>GenAI Loan Advisor</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <style> | |
| .loader { | |
| border: 4px solid #f3f3f3; | |
| border-top: 4px solid #3b82f6; | |
| border-radius: 50%; | |
| width: 30px; | |
| height: 30px; | |
| animation: spin 1s linear infinite; | |
| display: inline-block; | |
| } | |
| @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } | |
| pre { white-space: pre-wrap; word-wrap: break-word; font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; } | |
| .data-box { max-height: 300px; overflow-y: auto; } | |
| #finalOutput { white-space: pre-wrap; line-height: 1.6; } | |
| </style> | |
| </head> | |
| <body class="bg-gray-100 min-h-screen flex flex-col items-center py-10"> | |
| <div class="w-full max-w-5xl bg-white shadow-xl rounded-xl p-8 border border-gray-200"> | |
| <div class="border-b pb-4 mb-6 text-center"> | |
| <h1 class="text-3xl font-extrabold text-gray-800 tracking-tight">GenAI Loan Advisor</h1> | |
| <p class="text-gray-500 mt-2 text-sm">Autonomous Multi-Agent System (CrewAI + Mistral)</p> | |
| </div> | |
| <div class="flex flex-col gap-4 mb-6"> | |
| <div> | |
| <label class="block text-xs font-bold text-gray-500 uppercase mb-1">Access Code</label> | |
| <input type="password" id="accessCode" | |
| class="w-full p-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-red-500 shadow-sm text-gray-700 placeholder-gray-400" | |
| placeholder="π Enter the class code (Required)"> | |
| </div> | |
| <div> | |
| <label class="block text-xs font-bold text-gray-500 uppercase mb-1">Your Question</label> | |
| <div class="flex gap-2"> | |
| <input type="text" id="userQuery" | |
| class="w-full p-4 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 shadow-sm text-gray-700" | |
| placeholder="e.g., What is the rate for Andy?"> | |
| <button onclick="sendQuery()" id="submitBtn" | |
| class="bg-blue-600 hover:bg-blue-700 text-white font-bold py-2 px-8 rounded-lg transition duration-200 shadow-md whitespace-nowrap disabled:bg-gray-400 disabled:cursor-not-allowed"> | |
| Analyze | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| <p class="text-xs text-gray-400 text-center mb-6">Try Asking What is the recommended rate for Hilda? What is consider high risk? Or just say Hello</p> | |
| <div id="loadingState" class="hidden mt-8 text-center"> | |
| <div class="loader mb-3"></div> | |
| <p class="text-gray-500 text-sm font-medium animate-pulse"> | |
| π Agents are coordinating... this may take up to 45 seconds. | |
| </p> | |
| <p class="text-xs text-gray-400 mt-1">(Gathering data, checking policies, and underwriting)</p> | |
| </div> | |
| <div id="resultsArea" class="hidden mt-8 space-y-6 animate-fade-in"> | |
| <div class="bg-gradient-to-r from-green-50 to-white border-l-4 border-green-500 p-6 rounded shadow-sm"> | |
| <h3 class="text-lg font-bold text-green-800 flex items-center gap-2">β Final Decision</h3> | |
| <div id="finalOutput" class="text-gray-800 mt-3 text-base leading-relaxed"></div> | |
| </div> | |
| <div class="bg-blue-50 border-l-4 border-blue-500 p-5 rounded shadow-sm"> | |
| <h3 class="text-sm font-bold text-blue-800 uppercase tracking-wide mb-3 flex items-center gap-2">π§ Supervisor Strategy</h3> | |
| <div id="planOutput" class="text-sm text-gray-700"></div> | |
| </div> | |
| <div class="grid grid-cols-1 md:grid-cols-2 gap-6"> | |
| <div class="bg-gray-50 border border-gray-200 p-5 rounded shadow-sm h-full"> | |
| <h3 class="text-xs font-bold text-gray-500 uppercase mb-3 flex items-center gap-2">π€ Customer Profile</h3> | |
| <div id="customerOutput" class="data-box text-sm text-gray-700"></div> | |
| </div> | |
| <div class="bg-gray-50 border border-gray-200 p-5 rounded shadow-sm h-full"> | |
| <h3 class="text-xs font-bold text-gray-500 uppercase mb-3 flex items-center gap-2">π Policy Context (RAG)</h3> | |
| <div id="policyOutput" class="data-box text-xs text-gray-600 bg-white p-3 rounded border border-gray-100 italic"></div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| async function sendQuery() { | |
| const queryInput = document.getElementById('userQuery'); | |
| const codeInput = document.getElementById('accessCode'); | |
| const resultsArea = document.getElementById('resultsArea'); | |
| const loadingState = document.getElementById('loadingState'); // Target the new container | |
| const submitBtn = document.getElementById('submitBtn'); // Target button to disable it | |
| const query = queryInput.value.trim(); | |
| const code = codeInput.value.trim(); | |
| if (!code || !query) return alert("Please fill in both fields."); | |
| // UI Updates: Hide results, Show Loader, Disable Button | |
| resultsArea.classList.add('hidden'); | |
| loadingState.classList.remove('hidden'); | |
| submitBtn.disabled = true; | |
| submitBtn.innerText = "Processing..."; | |
| try { | |
| const response = await fetch('/ask', { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify({ query, code }) | |
| }); | |
| const result = await response.json(); | |
| if (result.status === 'success') { | |
| const data = result.data; | |
| // 1. FINAL RESOLUTION | |
| let recText = data.final_recommendation || "System could not provide a resolution."; | |
| recText = recText.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>'); | |
| document.getElementById('finalOutput').innerHTML = recText; | |
| // 2. STRATEGY | |
| const planObj = data.plan || {}; | |
| const planHtml = ` | |
| <div class="grid grid-cols-1 md:grid-cols-2 gap-4"> | |
| <div class="col-span-1 md:col-span-2"><span class="font-semibold text-blue-900">Thought:</span> <span class="italic text-gray-600">${planObj.thought_process || "Processing query..."}</span></div> | |
| <div><span class="font-semibold text-gray-700">Intent:</span> ${planObj.intent || "Unknown"}</div> | |
| <div><span class="font-semibold text-gray-700">Detected Name:</span> ${planObj.customer_name || "None"}</div> | |
| <div><span class="font-semibold text-gray-700">DB Check:</span> <span class="${planObj.requires_database ? 'text-green-600 font-bold' : 'text-gray-400'}">${planObj.requires_database ? "YES" : "NO"}</span></div> | |
| <div><span class="font-semibold text-gray-700">Policy Check:</span> <span class="${planObj.requires_policy ? 'text-green-600 font-bold' : 'text-gray-400'}">${planObj.requires_policy ? "YES" : "NO"}</span></div> | |
| </div>`; | |
| document.getElementById('planOutput').innerHTML = planHtml; | |
| // 3. CUSTOMER DATA | |
| let customerHtml = ""; | |
| try { | |
| let custData = data.customer_data; | |
| if (typeof custData === 'string') { | |
| if (custData.includes("NOT_FOUND") || custData.includes("N/A")) { | |
| customerHtml = `<div class="bg-red-50 text-red-700 p-3 rounded font-bold text-center">β No Record Found</div>`; | |
| } else { | |
| customerHtml = `<div class="text-gray-600 text-xs italic">${custData}</div>`; | |
| } | |
| } | |
| else if (typeof custData === 'object' && custData !== null) { | |
| customerHtml = `<div class="grid grid-cols-2 gap-y-2 gap-x-4">`; | |
| for (const [key, value] of Object.entries(custData)) { | |
| let label = key.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase()); | |
| let valDisplay = value; | |
| if (value === false) valDisplay = '<span class="text-red-500 font-bold">False</span>'; | |
| if (value === true) valDisplay = '<span class="text-green-600 font-bold">True</span>'; | |
| if (key === 'credit_score' && typeof value === 'number') { | |
| valDisplay = value >= 700 | |
| ? `<span class="text-green-600 font-bold">${value}</span>` | |
| : `<span class="text-amber-600 font-bold">${value}</span>`; | |
| } | |
| customerHtml += `<div class="text-gray-400 text-[10px] uppercase pt-1">${label}</div><div class="font-medium text-gray-800 break-words">${valDisplay}</div>`; | |
| } | |
| customerHtml += `</div>`; | |
| } | |
| } catch (e) { | |
| console.error("Rendering Error:", e); | |
| customerHtml = `<div class="text-gray-600 text-xs italic">Error loading data.</div>`; | |
| } | |
| document.getElementById('customerOutput').innerHTML = customerHtml; | |
| // 4. POLICY CONTEXT | |
| document.getElementById('policyOutput').textContent = data.policy_data || "No relevant policy snippets retrieved."; | |
| resultsArea.classList.remove('hidden'); | |
| } else { | |
| alert(result.message || "Access Denied."); | |
| } | |
| } catch (error) { | |
| console.error(error); | |
| alert("Server connection failed."); | |
| } finally { | |
| // Restore UI state | |
| loadingState.classList.add('hidden'); | |
| submitBtn.disabled = false; | |
| submitBtn.innerText = "Analyze"; | |
| } | |
| } | |
| document.getElementById("userQuery").addEventListener("keypress", function(event) { | |
| if (event.key === "Enter") sendQuery(); | |
| }); | |
| </script> | |
| </body> | |
| </html> |