Download src/api/enhanced_ui.html from sbicy/prof-demo: direct link, hf CLI and curl.
- Browser
- Download file 19.3 kB
-
https://huggingface.co/spaces/sbicy/prof-demo/resolve/main/src/api/enhanced_ui.html
- Command line
-
hf download hf://spaces/sbicy/prof-demo/src/api/enhanced_ui.html
-
curl -L -o enhanced_ui.html https://huggingface.co/spaces/sbicy/prof-demo/resolve/main/src/api/enhanced_ui.html
19.3 kB
| <html> | |
| <head> | |
| <title>Context-Aware Profanity Handler - Interactive Demo</title> | |
| <style> | |
| * { box-sizing: border-box; } | |
| body { | |
| font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; | |
| margin: 0; | |
| padding: 20px; | |
| background: #f5f5f7; | |
| } | |
| .header { | |
| max-width: 1400px; | |
| margin: 0 auto 30px; | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
| padding: 40px; | |
| border-radius: 12px; | |
| color: white; | |
| box-shadow: 0 10px 30px rgba(0,0,0,0.1); | |
| } | |
| .header h1 { margin: 0 0 10px; font-size: 36px; } | |
| .header p { margin: 0; opacity: 0.95; font-size: 18px; } | |
| .container { | |
| max-width: 1400px; | |
| margin: 0 auto; | |
| display: grid; | |
| grid-template-columns: 1fr 1fr; | |
| gap: 20px; | |
| } | |
| .panel { | |
| background: white; | |
| padding: 30px; | |
| border-radius: 12px; | |
| box-shadow: 0 2px 8px rgba(0,0,0,0.08); | |
| } | |
| .panel.full { grid-column: 1 / -1; } | |
| h2 { | |
| margin-top: 0; | |
| color: #1d1d1f; | |
| font-size: 24px; | |
| border-bottom: 2px solid #667eea; | |
| padding-bottom: 10px; | |
| } | |
| .input-group { | |
| margin: 20px 0; | |
| } | |
| label { | |
| display: block; | |
| margin-bottom: 8px; | |
| font-weight: 600; | |
| color: #1d1d1f; | |
| } | |
| textarea, select { | |
| width: 100%; | |
| padding: 12px; | |
| border: 2px solid #d2d2d7; | |
| border-radius: 8px; | |
| font-size: 15px; | |
| font-family: inherit; | |
| transition: border-color 0.2s; | |
| } | |
| textarea:focus, select:focus { | |
| outline: none; | |
| border-color: #667eea; | |
| } | |
| textarea { min-height: 120px; resize: vertical; } | |
| .checkbox-group { | |
| display: flex; | |
| gap: 20px; | |
| margin: 20px 0; | |
| } | |
| .checkbox-group label { | |
| display: flex; | |
| align-items: center; | |
| gap: 8px; | |
| font-weight: 500; | |
| } | |
| input[type="checkbox"] { | |
| width: 18px; | |
| height: 18px; | |
| cursor: pointer; | |
| } | |
| button { | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
| color: white; | |
| padding: 14px 32px; | |
| border: none; | |
| border-radius: 8px; | |
| cursor: pointer; | |
| font-size: 16px; | |
| font-weight: 600; | |
| transition: transform 0.2s, box-shadow 0.2s; | |
| box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4); | |
| } | |
| button:hover { | |
| transform: translateY(-2px); | |
| box-shadow: 0 6px 16px rgba(102, 126, 234, 0.5); | |
| } | |
| button:active { | |
| transform: translateY(0); | |
| } | |
| .badge { | |
| display: inline-block; | |
| padding: 6px 12px; | |
| border-radius: 6px; | |
| font-size: 13px; | |
| font-weight: 600; | |
| margin: 4px; | |
| } | |
| .badge.safe { background: #d1f4e0; color: #0f5132; } | |
| .badge.mild { background: #fff3cd; color: #997404; } | |
| .badge.explicit { background: #f8d7da; color: #842029; } | |
| .badge.slur { background: #f5c2c7; color: #58151c; } | |
| .badge.threat { background: #ea868f; color: #58151c; } | |
| .comparison { | |
| display: grid; | |
| grid-template-columns: 1fr 1fr; | |
| gap: 20px; | |
| margin-top: 20px; | |
| } | |
| .comparison-item { | |
| padding: 15px; | |
| background: #f5f5f7; | |
| border-radius: 8px; | |
| border-left: 4px solid #667eea; | |
| } | |
| .comparison-item h4 { | |
| margin-top: 0; | |
| color: #667eea; | |
| font-size: 14px; | |
| text-transform: uppercase; | |
| letter-spacing: 0.5px; | |
| } | |
| .comparison-item pre { | |
| margin: 0; | |
| white-space: pre-wrap; | |
| word-wrap: break-word; | |
| font-size: 14px; | |
| line-height: 1.6; | |
| } | |
| .metrics { | |
| display: grid; | |
| grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); | |
| gap: 15px; | |
| margin: 20px 0; | |
| } | |
| .metric { | |
| background: #f5f5f7; | |
| padding: 20px; | |
| border-radius: 8px; | |
| text-align: center; | |
| } | |
| .metric-value { | |
| font-size: 32px; | |
| font-weight: 700; | |
| color: #667eea; | |
| margin: 10px 0; | |
| } | |
| .metric-label { | |
| font-size: 13px; | |
| color: #86868b; | |
| text-transform: uppercase; | |
| letter-spacing: 0.5px; | |
| } | |
| .log-viewer { | |
| background: #1d1d1f; | |
| color: #f5f5f7; | |
| padding: 20px; | |
| border-radius: 8px; | |
| font-family: 'Courier New', monospace; | |
| font-size: 13px; | |
| max-height: 400px; | |
| overflow-y: auto; | |
| } | |
| .log-entry { | |
| margin: 10px 0; | |
| padding: 10px; | |
| background: rgba(255,255,255,0.05); | |
| border-radius: 4px; | |
| } | |
| .log-redacted { | |
| color: #ff9f0a; | |
| } | |
| .log-verbatim { | |
| color: #30d158; | |
| } | |
| .example-box { | |
| background: linear-gradient(135deg, #e0e7ff 0%, #f0e7ff 100%); | |
| padding: 20px; | |
| border-radius: 8px; | |
| margin: 20px 0; | |
| border-left: 4px solid #667eea; | |
| } | |
| .example-box h3 { | |
| margin-top: 0; | |
| color: #1d1d1f; | |
| } | |
| .tab-container { | |
| margin-top: 20px; | |
| } | |
| .tabs { | |
| display: flex; | |
| gap: 10px; | |
| border-bottom: 2px solid #d2d2d7; | |
| margin-bottom: 20px; | |
| } | |
| .tab { | |
| padding: 10px 20px; | |
| cursor: pointer; | |
| border: none; | |
| background: none; | |
| font-size: 15px; | |
| font-weight: 600; | |
| color: #86868b; | |
| border-bottom: 3px solid transparent; | |
| transition: all 0.2s; | |
| } | |
| .tab.active { | |
| color: #667eea; | |
| border-bottom-color: #667eea; | |
| } | |
| .tab-content { | |
| display: none; | |
| } | |
| .tab-content.active { | |
| display: block; | |
| } | |
| .hidden { display: none ; } | |
| .ai-scores { | |
| display: grid; | |
| grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); | |
| gap: 10px; | |
| margin: 15px 0; | |
| } | |
| .ai-score { | |
| background: #f5f5f7; | |
| padding: 12px; | |
| border-radius: 6px; | |
| text-align: center; | |
| } | |
| .ai-score-label { | |
| font-size: 11px; | |
| color: #86868b; | |
| text-transform: uppercase; | |
| } | |
| .ai-score-value { | |
| font-size: 20px; | |
| font-weight: 700; | |
| color: #1d1d1f; | |
| margin-top: 5px; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="header"> | |
| <h1>π§© Context-Aware Profanity Handler</h1> | |
| <p>Interactive demonstration of context-aware profanity detection, delexicalization, and audit logging</p> | |
| </div> | |
| <div class="container"> | |
| <!-- Input Panel --> | |
| <div class="panel"> | |
| <h2>π Input</h2> | |
| <div class="example-box"> | |
| <h3>π‘ Example Use Case</h3> | |
| <p><strong>Scenario:</strong> A user wants to generate a report about an asset with an explicit song title.</p> | |
| <p><strong>Input:</strong> "Report on asset: <em>Do You Want to Fuck Me Tonight</em>"</p> | |
| <p><strong>Context:</strong> Song Title (Entity Name)</p> | |
| </div> | |
| <div class="input-group"> | |
| <label for="text">Text to Analyze:</label> | |
| <textarea id="text" placeholder="Enter text here...">Report on asset: Do You Want to Fuck Me Tonight</textarea> | |
| </div> | |
| <div class="input-group"> | |
| <label for="context">Content Category:</label> | |
| <select id="context"> | |
| <option value="song_title">Song Title</option> | |
| <option value="entity_name">Entity Name</option> | |
| <option value="brand_name">Brand Name</option> | |
| <option value="user_input">User Input</option> | |
| </select> | |
| </div> | |
| <div class="checkbox-group"> | |
| <label> | |
| <input type="checkbox" id="strict_mode"> Strict Mode | |
| </label> | |
| <label> | |
| <input type="checkbox" id="use_ai" checked> Use AI Classifier | |
| </label> | |
| <label> | |
| <input type="checkbox" id="include_explicit"> Include in Export | |
| </label> | |
| </div> | |
| <button onclick="analyzeText()">π Analyze Text</button> | |
| </div> | |
| <!-- Results Panel --> | |
| <div class="panel"> | |
| <h2>π Analysis Results</h2> | |
| <div id="results" style="color: #86868b; text-align: center; padding: 40px;"> | |
| Run an analysis to see results here | |
| </div> | |
| </div> | |
| <!-- Comparison Panel --> | |
| <div class="panel full hidden" id="comparisonPanel"> | |
| <h2>π Text Comparison</h2> | |
| <div class="comparison"> | |
| <div class="comparison-item"> | |
| <h4>π Original Text (Verbatim)</h4> | |
| <pre id="originalText"></pre> | |
| </div> | |
| <div class="comparison-item"> | |
| <h4>β¨ Delexicalized Text (Safe for AI)</h4> | |
| <pre id="safeText"></pre> | |
| </div> | |
| <div class="comparison-item"> | |
| <h4>π€ Export Text (Based on Preference)</h4> | |
| <pre id="exportText"></pre> | |
| </div> | |
| <div class="comparison-item"> | |
| <h4>π Detected Words</h4> | |
| <pre id="detectedWords"></pre> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Logs Panel --> | |
| <div class="panel full hidden" id="logsPanel"> | |
| <h2>π Audit Logs Visualization</h2> | |
| <div class="tab-container"> | |
| <div class="tabs"> | |
| <button class="tab active" onclick="switchTab('redacted')">π Redacted Logs (Analytics)</button> | |
| <button class="tab" onclick="switchTab('verbatim')">π Verbatim Logs (Compliance)</button> | |
| </div> | |
| <div class="tab-content active" id="redacted-tab"> | |
| <p style="color: #86868b; margin-bottom: 15px;"> | |
| <strong>Purpose:</strong> Safe for analytics and monitoring. Contains metadata without sensitive content. | |
| </p> | |
| <div class="log-viewer" id="redactedLogs"> | |
| <div class="log-redacted">Waiting for analysis...</div> | |
| </div> | |
| </div> | |
| <div class="tab-content" id="verbatim-tab"> | |
| <p style="color: #86868b; margin-bottom: 15px;"> | |
| <strong>Purpose:</strong> Full audit trail for compliance. Access should be restricted (RBAC). | |
| </p> | |
| <div class="log-viewer" id="verbatimLogs"> | |
| <div class="log-verbatim">Waiting for analysis...</div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| let currentRequestId = null; | |
| function switchTab(tabName) { | |
| document.querySelectorAll('.tab').forEach(t => t.classList.remove('active')); | |
| document.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active')); | |
| event.target.classList.add('active'); | |
| document.getElementById(tabName + '-tab').classList.add('active'); | |
| } | |
| async function analyzeText() { | |
| const text = document.getElementById('text').value; | |
| const context = document.getElementById('context').value; | |
| const strict_mode = document.getElementById('strict_mode').checked; | |
| const use_ai = document.getElementById('use_ai').checked; | |
| const include_explicit_in_export = document.getElementById('include_explicit').checked; | |
| const resultsDiv = document.getElementById('results'); | |
| resultsDiv.innerHTML = '<div style="text-align: center; padding: 20px;">β³ Analyzing...</div>'; | |
| try { | |
| const response = await fetch('/analyze', { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify({ text, context, strict_mode, use_ai, include_explicit_in_export }) | |
| }); | |
| const data = await response.json(); | |
| currentRequestId = data.request_id; | |
| // Display results | |
| displayResults(data, text); | |
| // Show comparison panel | |
| displayComparison(text, data); | |
| // Load and display logs | |
| await displayLogs(data.request_id); | |
| } catch (error) { | |
| resultsDiv.innerHTML = `<div style="color: #d1180b;">Error: ${error.message}</div>`; | |
| } | |
| } | |
| function displayResults(data, originalText) { | |
| const resultsDiv = document.getElementById('results'); | |
| let html = '<div class="metrics">'; | |
| html += `<div class="metric"> | |
| <div class="metric-label">Profanity</div> | |
| <div class="metric-value">${data.contains_profanity ? 'β οΈ' : 'β '}</div> | |
| </div>`; | |
| html += `<div class="metric"> | |
| <div class="metric-label">Toxicity</div> | |
| <div class="metric-value"><span class="badge ${data.toxicity_level}">${data.toxicity_level.toUpperCase()}</span></div> | |
| </div>`; | |
| html += '</div>'; | |
| html += `<div style="margin: 20px 0;"> | |
| <strong>Message:</strong> | |
| <div style="padding: 15px; background: #f5f5f7; border-radius: 8px; margin-top: 10px;"> | |
| ${data.message} | |
| </div> | |
| </div>`; | |
| if (data.ai_confidence) { | |
| html += '<div style="margin: 20px 0;"><strong>AI Confidence Scores:</strong><div class="ai-scores">'; | |
| for (const [label, score] of Object.entries(data.ai_confidence)) { | |
| html += `<div class="ai-score"> | |
| <div class="ai-score-label">${label}</div> | |
| <div class="ai-score-value">${(score * 100).toFixed(1)}%</div> | |
| </div>`; | |
| } | |
| html += '</div></div>'; | |
| } | |
| html += `<div style="margin-top: 20px; font-size: 13px; color: #86868b;"> | |
| Request ID: <code>${data.request_id}</code> | |
| </div>`; | |
| resultsDiv.innerHTML = html; | |
| } | |
| function displayComparison(originalText, data) { | |
| document.getElementById('comparisonPanel').classList.remove('hidden'); | |
| document.getElementById('originalText').textContent = originalText; | |
| document.getElementById('safeText').textContent = data.safe_text; | |
| document.getElementById('exportText').textContent = data.export_text; | |
| document.getElementById('detectedWords').textContent = | |
| data.detected_words && data.detected_words.length > 0 | |
| ? data.detected_words.join(', ') | |
| : 'None detected'; | |
| } | |
| async function displayLogs(requestId) { | |
| document.getElementById('logsPanel').classList.remove('hidden'); | |
| try { | |
| // Load redacted logs | |
| const redactedResponse = await fetch('/logs/redacted'); | |
| const redactedData = await redactedResponse.json(); | |
| const redactedLogs = document.getElementById('redactedLogs'); | |
| if (redactedData.logs && redactedData.logs.length > 0) { | |
| redactedLogs.innerHTML = redactedData.logs.slice(-5).reverse().map(log => ` | |
| <div class="log-entry log-redacted"> | |
| <strong>Request ID:</strong> ${log.request_id}<br> | |
| <strong>Timestamp:</strong> ${new Date(log.timestamp).toLocaleString()}<br> | |
| <strong>Context:</strong> ${log.context}<br> | |
| <strong>Profanity:</strong> ${log.contains_profanity ? 'Yes' : 'No'}<br> | |
| <strong>Toxicity:</strong> ${log.toxicity_level}<br> | |
| <strong>Text Hash:</strong> ${log.text_hash}<br> | |
| <strong>Text Length:</strong> ${log.text_length} chars | |
| </div> | |
| `).join(''); | |
| } | |
| // Load verbatim log for current request | |
| const verbatimResponse = await fetch(`/logs/verbatim/${requestId}`); | |
| const verbatimData = await verbatimResponse.json(); | |
| const verbatimLogs = document.getElementById('verbatimLogs'); | |
| if (verbatimData.request_id) { | |
| verbatimLogs.innerHTML = ` | |
| <div class="log-entry log-verbatim"> | |
| <strong>β οΈ COMPLIANCE ACCESS ONLY β οΈ</strong><br><br> | |
| <strong>Request ID:</strong> ${verbatimData.request_id}<br> | |
| <strong>Timestamp:</strong> ${new Date(verbatimData.timestamp).toLocaleString()}<br> | |
| <strong>Context:</strong> ${verbatimData.context}<br> | |
| <strong>Original Text:</strong> ${verbatimData.original_text}<br> | |
| <strong>Safe Text:</strong> ${verbatimData.safe_text}<br> | |
| <strong>Profanity:</strong> ${verbatimData.contains_profanity ? 'Yes' : 'No'}<br> | |
| <strong>Toxicity:</strong> ${verbatimData.toxicity_level} | |
| </div> | |
| `; | |
| } else { | |
| verbatimLogs.innerHTML = '<div class="log-verbatim">Verbatim logs not available or disabled</div>'; | |
| } | |
| } catch (error) { | |
| console.error('Error loading logs:', error); | |
| } | |
| } | |
| </script> | |
| </body> | |
| </html> | |