DevKX's picture
Initial deploy
cffeaa1
<!DOCTYPE html>
<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>