Spaces:
Runtime error
Runtime error
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <title>Finance Q/A Bot</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> | |
| </head> | |
| <body class="bg-gray-100 min-h-screen flex"> | |
| <!-- Main Content --> | |
| <div class="w-3/4 p-10"> | |
| <h1 class="text-3xl font-bold mb-6">Finance Q/A Bot</h1> | |
| <form id="searchForm" class="space-y-4"> | |
| <input | |
| type="text" | |
| id="query" | |
| name="query" | |
| placeholder="Ask a question" | |
| class="p-2 border w-3/4 rounded" | |
| required | |
| /> | |
| <br /> | |
| <div class="flex items-center space-x-2"> | |
| <button | |
| type="submit" | |
| class="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600" | |
| > | |
| Submit | |
| </button> | |
| <!-- Loading spinner --> | |
| <div id="loadingSpinner" class="hidden"> | |
| <svg class="animate-spin h-5 w-5 text-blue-500" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"> | |
| <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle> | |
| <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path> | |
| </svg> | |
| </div> | |
| </div> | |
| </form> | |
| <!-- Context Adherence Message (above the result box) --> | |
| <div id="adherenceMessage" class="mt-6 p-3 rounded hidden"></div> | |
| <!-- Result area --> | |
| <div id="result" class="mt-4 p-4 bg-white shadow rounded hidden"></div> | |
| </div> | |
| <!-- Sidebar on the right --> | |
| <div class="w-1/4 bg-white shadow p-6"> | |
| <h2 class="text-xl font-bold mb-4">Options</h2> | |
| <div class="flex flex-col space-y-4"> | |
| <label class="block"> | |
| <span class="text-gray-700">Top K:</span> | |
| <input | |
| type="number" | |
| id="top_k" | |
| name="top_k" | |
| value="5" | |
| class="mt-1 p-2 w-full border rounded" | |
| /> | |
| </label> | |
| <label class="flex items-center space-x-2"> | |
| <input | |
| type="checkbox" | |
| id="protection" | |
| name="protection" | |
| class="form-checkbox text-green-600 focus:ring-green-500" | |
| /> | |
| <span>Enable Galileo Protection</span> | |
| </label> | |
| <label class="flex items-center space-x-2"> | |
| <input | |
| type="checkbox" | |
| id="hallucination_detection" | |
| name="hallucination_detection" | |
| class="form-checkbox text-blue-600 focus:ring-blue-500" | |
| /> | |
| <span>Enable Hallucination Detection</span> | |
| </label> | |
| <label class="flex items-center space-x-2"> | |
| <input | |
| type="checkbox" | |
| id="induce_hallucination" | |
| name="induce_hallucination" | |
| class="form-checkbox text-red-600 focus:ring-red-500" | |
| /> | |
| <span>Induce Hallucination</span> | |
| </label> | |
| </div> | |
| </div> | |
| <script> | |
| $(document).ready(function () { | |
| // Check for URL parameters and pre-fill form | |
| const urlParams = new URLSearchParams(window.location.search); | |
| if (urlParams.has('query')) { | |
| $('#query').val(urlParams.get('query')); | |
| } | |
| if (urlParams.has('top_k')) { | |
| $('#top_k').val(urlParams.get('top_k')); | |
| } | |
| if (urlParams.has('protection')) { | |
| $('#protection').prop('checked', urlParams.get('protection') === 'true'); | |
| } | |
| if (urlParams.has('hallucination_detection')) { | |
| $('#hallucination_detection').prop('checked', urlParams.get('hallucination_detection') === 'true'); | |
| } | |
| if (urlParams.has('induce_hallucination')) { | |
| $('#induce_hallucination').prop('checked', urlParams.get('induce_hallucination') === 'true'); | |
| } | |
| $('#searchForm').on('submit', function (e) { | |
| e.preventDefault(); | |
| const query = $('#query').val(); | |
| const top_k = $('#top_k').val(); | |
| const protection = $('#protection').is(':checked'); | |
| const hallucination_detection = $('#hallucination_detection').is(':checked'); | |
| const induce_hallucination = $('#induce_hallucination').is(':checked'); | |
| // Show loading spinner | |
| $('#loadingSpinner').removeClass('hidden'); | |
| // Hide previous results | |
| $('#adherenceMessage').addClass('hidden'); | |
| $('#result').addClass('hidden'); | |
| $.ajax({ | |
| type: 'POST', | |
| url: '/search', | |
| data: { | |
| query: query, | |
| top_k: top_k, | |
| protection: protection, | |
| hallucination_detection: hallucination_detection, | |
| induce_hallucination: induce_hallucination | |
| }, | |
| success: function (response) { | |
| // Hide loading spinner | |
| $('#loadingSpinner').addClass('hidden'); | |
| // Check for PII flag first | |
| const piiFlag = response.metrics && response.metrics.pii_flag; | |
| // Check if any PII types are detected | |
| const piiDetected = piiFlag && Object.values(piiFlag).some(value => value === true); | |
| // If PII is detected, display specific PII warning | |
| if (piiDetected) { | |
| // Build specific PII warning message | |
| const detectedTypes = []; | |
| if (piiFlag.phone_number) detectedTypes.push('phone number'); | |
| if (piiFlag.email) detectedTypes.push('<span style="color:yellow; font-weight: bold">email address</span>'); | |
| if (piiFlag.name) detectedTypes.push('<span style="color:yellow; font-weight: bold">personal name</span>'); | |
| if (piiFlag.company) detectedTypes.push('<span style="color:yellow; font-weight: bold">company name</span>'); | |
| let piiMessage = 'Sensitive personally identifiable information detected! The following types of PII were found: '; | |
| if (detectedTypes.length === 1) { | |
| piiMessage += detectedTypes[0]; | |
| } else if (detectedTypes.length === 2) { | |
| piiMessage += detectedTypes.join(' and '); | |
| } else { | |
| piiMessage += detectedTypes.slice(0, -1).join(', ') + ', and ' + detectedTypes.slice(-1); | |
| } | |
| // Display the PII warning and response | |
| $('#result') | |
| .removeClass('hidden') | |
| .html(` | |
| <div class="space-y-4"> | |
| <!-- PII Warning Message with red background --> | |
| <div class="bg-red-500 text-white p-3 rounded-lg"> | |
| <div class="flex items-start"> | |
| <div class="flex-shrink-0"> | |
| <svg class="h-5 w-5 text-white" viewBox="0 0 20 20" fill="currentColor"> | |
| <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd" /> | |
| </svg> | |
| </div> | |
| <div class="ml-3"> | |
| <p class="font-medium">${piiMessage}</p> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Original Message in red text --> | |
| <div class="bg-white border border-gray-200 rounded-lg p-4"> | |
| <p class="font-medium text-gray-700 mb-2">Original Message:</p> | |
| <p class="text-black-600"> | |
| <style> | |
| tag { | |
| font-weight: bold; | |
| background-color: yellow; | |
| } | |
| </style> | |
| ${response.message}</p> | |
| </div> | |
| <!-- Redacted Message in green text --> | |
| <div class="bg-white border border-gray-200 rounded-lg p-4"> | |
| <p class="font-medium text-gray-700 mb-2">Redacted Message:</p> | |
| <p class="text-black-600"> | |
| <style> | |
| pii { | |
| font-weight: bold; | |
| background-color: yellow; | |
| } | |
| </style> | |
| ${response.redacted_message || 'No redacted version available'} | |
| </p> | |
| </div> | |
| </div> | |
| `); | |
| } else if ((induce_hallucination && response.original_message && response.message) || | |
| (response.metrics && response.metrics.context_adherence < 0.8)) { | |
| // Display hallucination warning and both responses | |
| const adherenceScore = response.metrics ? response.metrics.context_adherence : 1; | |
| const isInducedHallucination = induce_hallucination && response.original_message && response.message; | |
| let warningMessage = ''; | |
| if (isInducedHallucination) { | |
| warningMessage = 'Hallucination induced for demonstration purposes! Comparing original vs safe response.'; | |
| } else { | |
| warningMessage = 'Potential hallucination detected! Comparing original vs safe response.'; | |
| } | |
| $('#result') | |
| .removeClass('hidden') | |
| .html(` | |
| <div class="space-y-4"> | |
| <!-- Hallucination Warning Message with orange background --> | |
| <div class="bg-orange-500 text-white p-3 rounded-lg"> | |
| <div class="flex items-start"> | |
| <div class="flex-shrink-0"> | |
| <svg class="h-5 w-5 text-white" viewBox="0 0 20 20" fill="currentColor"> | |
| <path fill-rule="evenodd" d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z" clip-rule="evenodd" /> | |
| </svg> | |
| </div> | |
| <div class="ml-3"> | |
| <p class="font-medium">${warningMessage}</p> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Potentially Hallucinatory Response --> | |
| <div class="bg-white border border-red-200 rounded-lg p-4"> | |
| <p class="font-medium text-red-700 mb-2">Original Hallucinatory Response:</p> | |
| <p class="text-black-600">${response.message}</p> | |
| </div> | |
| <!-- Fallback Response --> | |
| <div class="bg-white border border-green-200 rounded-lg p-4"> | |
| <p class="font-medium text-green-700 mb-2">Safe Response:</p> | |
| <p class="text-black-600">I cannot provide a reliable answer to this question based on the available information! Please try again.</p> | |
| </div> | |
| <!-- Try Again Option --> | |
| <div class="bg-blue-50 border border-blue-200 rounded-lg p-4"> | |
| <p class="font-medium text-blue-700 mb-3">Retry with different search parameters:</p> | |
| <div class="flex items-center space-x-3"> | |
| <label class="text-sm text-blue-600"> | |
| Top K: | |
| <input | |
| type="number" | |
| id="retry_top_k" | |
| value="5" | |
| min="1" | |
| max="100" | |
| class="ml-2 p-1 w-16 border border-blue-300 rounded text-sm" | |
| /> | |
| </label> | |
| <button id="retry_button" class="bg-blue-500 text-white px-3 py-1 rounded text-sm hover:bg-blue-600"> | |
| Try Again | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| `); | |
| } else { | |
| // Display the main result in normal black color | |
| $('#result') | |
| .removeClass('hidden') | |
| .html(` | |
| <p class="text-black font-bold">${response.message}</p> | |
| `); | |
| } | |
| // Always display context adherence message below the response (regardless of PII detection) | |
| if (response.metrics && response.metrics.context_adherence !== undefined) { | |
| const adherenceScore = response.metrics.context_adherence; | |
| // Only show adherence message if score is not exactly 1 (default value) | |
| if (adherenceScore !== 1.0 || hallucination_detection) { | |
| let adherenceMessage = ''; | |
| let adherenceClass = ''; | |
| if (adherenceScore >= 0.8) { | |
| adherenceMessage = 'No hallucination detected - The answer is reliable'; | |
| adherenceClass = 'bg-green-100 border border-green-300 text-green-800'; | |
| } else if (adherenceScore >= 0.3) { | |
| adherenceMessage = 'Potential hallucination detected- The answer is unreliable'; | |
| adherenceClass = 'bg-orange-100 border border-orange-300 text-orange-800'; | |
| } else { | |
| adherenceMessage = 'High hallucination detected - The answer is unusable'; | |
| adherenceClass = 'bg-red-100 border border-red-300 text-red-800'; | |
| } | |
| $('#adherenceMessage') | |
| .removeClass('hidden bg-green-100 bg-yellow-100 bg-orange-100 bg-red-100 border-green-300 border-yellow-300 border-orange-300 border-red-300 text-green-800 text-yellow-800 text-orange-800 text-red-800') | |
| .addClass(adherenceClass) | |
| .html(` | |
| <div class="flex items-center justify-between"> | |
| <span class="font-medium">${adherenceMessage}</span> | |
| ${adherenceScore <= 0.8 ? `<span class="text-sm opacity-75">${((1-adherenceScore) * 100).toFixed(1)}% Hallucination detected</span>` : ''} | |
| </div> | |
| `); | |
| } else { | |
| $('#adherenceMessage').addClass('hidden'); | |
| } | |
| } else { | |
| $('#adherenceMessage').addClass('hidden'); | |
| } | |
| }, | |
| error: function () { | |
| // Hide loading spinner | |
| $('#loadingSpinner').addClass('hidden'); | |
| $('#adherenceMessage').addClass('hidden'); | |
| $('#result') | |
| .removeClass('hidden') | |
| .html('<p class="text-red-600 font-bold">An error occurred while searching.</p>'); | |
| } | |
| }); | |
| }); | |
| // Handle retry button click | |
| $(document).on('click', '#retry_button', function() { | |
| const query = $('#query').val(); | |
| const retry_top_k = $('#retry_top_k').val(); | |
| const protection = $('#protection').is(':checked'); | |
| const hallucination_detection = $('#hallucination_detection').is(':checked'); | |
| const induce_hallucination = $('#induce_hallucination').is(':checked'); | |
| // Create URL parameters to reload with form pre-filled | |
| const params = new URLSearchParams(); | |
| params.set('query', query); | |
| params.set('top_k', retry_top_k); | |
| params.set('retry', 'true'); // Flag to indicate this is a retry attempt | |
| if (protection) params.set('protection', 'true'); | |
| if (hallucination_detection) params.set('hallucination_detection', 'true'); | |
| if (induce_hallucination) params.set('induce_hallucination', 'true'); | |
| // Reload the page with parameters | |
| window.location.href = window.location.pathname + '?' + params.toString(); | |
| }); | |
| // Auto-submit if this is a retry attempt (only once) - do this after all handlers are attached | |
| if (urlParams.has('retry') && urlParams.get('retry') === 'true') { | |
| $('#searchForm').submit(); | |
| } | |
| }); | |
| </script> | |
| </body> | |
| </html> | |