Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Message Trimmer Demo - WildChat Conversations</title> | |
| <style> | |
| :root { | |
| --bg: #0b0b0b; | |
| --surface: #111111; | |
| --surface-2: #141414; | |
| --text: #eaeaea; | |
| --muted: #9b9b9b; | |
| --border: #1f1f1f; | |
| --accent: #8ab4f8; /* subtle blue */ | |
| --radius: 12px; | |
| } | |
| * { margin: 0; padding: 0; box-sizing: border-box; } | |
| body { | |
| font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif; | |
| background: var(--bg); | |
| color: var(--text); | |
| padding: 24px; | |
| min-height: 100vh; | |
| line-height: 1.45; | |
| } | |
| .container { | |
| max-width: 1100px; | |
| margin: 0 auto; | |
| background: var(--surface); | |
| border: 1px solid var(--border); | |
| border-radius: var(--radius); | |
| overflow: hidden; | |
| } | |
| header { | |
| background: transparent; | |
| border-bottom: 1px solid var(--border); | |
| padding: 28px 30px; | |
| text-align: center; | |
| } | |
| h1 { font-size: 1.75em; font-weight: 700; letter-spacing: 0.2px; } | |
| .subtitle { color: var(--muted); font-size: 0.95em; margin-top: 6px; } | |
| .controls { | |
| padding: 24px 30px; | |
| background: var(--surface-2); | |
| border-bottom: 1px solid var(--border); | |
| } | |
| .control-group { margin-bottom: 18px; } | |
| .control-group:last-child { margin-bottom: 0; } | |
| label { | |
| display: block; | |
| font-weight: 600; | |
| margin-bottom: 8px; | |
| color: var(--muted); | |
| font-size: 0.9em; | |
| } | |
| input[type="range"] { | |
| width: 100%; | |
| height: 4px; | |
| border-radius: 2px; | |
| background: #222; | |
| outline: none; | |
| -webkit-appearance: none; | |
| } | |
| input[type="range"]::-webkit-slider-thumb { | |
| -webkit-appearance: none; appearance: none; | |
| width: 16px; height: 16px; border-radius: 50%; | |
| background: var(--accent); | |
| cursor: pointer; | |
| border: 2px solid #0d0d0d; | |
| box-shadow: 0 0 0 2px rgba(138,180,248,0.15); | |
| } | |
| input[type="range"]::-moz-range-thumb { | |
| width: 16px; height: 16px; border-radius: 50%; | |
| background: var(--accent); | |
| cursor: pointer; border: 2px solid #0d0d0d; | |
| } | |
| .value-display { | |
| display: inline-block; | |
| color: var(--accent); | |
| padding: 2px 10px; | |
| border-radius: 999px; | |
| font-size: 0.85em; font-weight: 600; | |
| margin-left: 10px; | |
| border: 1px solid var(--border); | |
| background: #0e0e0e; | |
| } | |
| select { | |
| width: 100%; padding: 10px 12px; | |
| border: 1px solid var(--border); border-radius: 8px; | |
| font-size: 0.95em; background: #0f0f0f; color: var(--text); | |
| cursor: pointer; | |
| } | |
| select:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px rgba(138,180,248,0.15); } | |
| .conversation { padding: 26px 30px; } | |
| .message { margin-bottom: 28px; animation: fadeIn 0.25s ease-in; } | |
| @keyframes fadeIn { from { opacity: 0; transform: translateY(6px);} to { opacity: 1; transform: translateY(0);} } | |
| .message-header { display: flex; align-items: center; margin-bottom: 10px; gap: 10px; } | |
| .role-badge { | |
| padding: 6px 12px; border-radius: 999px; font-size: 0.72em; font-weight: 700; | |
| text-transform: uppercase; letter-spacing: 0.5px; background: #0f0f0f; color: var(--muted); | |
| border: 1px solid var(--border); | |
| } | |
| .char-count { font-size: 0.75em; color: var(--muted); font-weight: 500; } | |
| .message-content { | |
| background: var(--surface-2); | |
| padding: 18px; border-radius: 10px; border-left: 3px solid var(--border); | |
| } | |
| .message-user .message-content { border-left-color: #2a2a2a; } | |
| .message-assistant .message-content { border-left-color: var(--accent); } | |
| .original { margin-bottom: 14px; padding-bottom: 14px; border-bottom: 1px dashed var(--border); } | |
| .trimmed, .no-trim { | |
| background: var(--surface); | |
| padding: 14px; border-radius: 8px; border: 1px solid var(--border); | |
| } | |
| .trimmed { border-color: rgba(138,180,248,0.35); } | |
| .section-label { | |
| font-size: 0.7em; font-weight: 700; text-transform: uppercase; letter-spacing: 0.5px; | |
| color: var(--muted); margin-bottom: 8px; | |
| } | |
| .trimmed .section-label { color: var(--accent); } | |
| .content-text { line-height: 1.6; color: var(--text); white-space: pre-wrap; word-wrap: break-word; } | |
| .stats { | |
| margin-top: 10px; padding: 10px; border-radius: 6px; font-size: 0.8em; color: var(--muted); | |
| background: #0f0f0f; border: 1px solid var(--border); | |
| } | |
| .stats strong { color: var(--text); } | |
| .reduction { color: var(--accent); font-weight: 600; } | |
| .code-reference { | |
| background: #0f0f0f; border: 1px solid var(--border); border-radius: 8px; padding: 18px; margin: 20px 30px; | |
| font-family: 'Monaco', 'Menlo', monospace; font-size: 0.85em; line-height: 1.6; color: var(--text); | |
| } | |
| .code-reference h3 { margin-bottom: 12px; color: var(--muted); font-size: 1em; font-weight: 600; } | |
| .code-line { color: var(--text); margin: 4px 0; } | |
| .comment { color: #7a7a7a; } | |
| button { | |
| background: #0f0f0f; color: var(--text); border: 1px solid var(--border); | |
| padding: 10px 16px; border-radius: 8px; cursor: pointer; font-size: 0.9em; font-weight: 600; margin-top: 10px; | |
| } | |
| button:hover { border-color: var(--accent); box-shadow: 0 0 0 3px rgba(138,180,248,0.15); } | |
| .info-box { | |
| background: var(--surface-2); border: 1px solid var(--border); border-left: 3px solid var(--accent); | |
| border-radius: 8px; padding: 18px; margin: 20px 30px; | |
| } | |
| .info-box h3 { color: var(--text); margin-bottom: 8px; font-size: 1em; } | |
| .info-box p { color: var(--muted); line-height: 1.6; font-size: 0.9em; } | |
| /* Focus styles */ | |
| :focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="container"> | |
| <header> | |
| <h1>Message Trimmer Demo</h1> | |
| <p class="subtitle">Demonstrating trim functions on real WildChat-1M conversations</p> | |
| </header> | |
| <div class="info-box"> | |
| <h3>About This Demo</h3> | |
| <p> | |
| This demo shows how the <strong>trimMiddle()</strong> function works in the LLM router architecture. | |
| The function trims messages by keeping the start (60%) and end (40%) with an ellipsis in between, | |
| reducing token count while preserving context for intent classification. | |
| Adjust the max length sliders below to see how different messages are trimmed. | |
| </p> | |
| </div> | |
| <div class="controls"> | |
| <div class="control-group"> | |
| <label> | |
| Max Assistant Message Length: <span class="value-display" id="assistantLengthValue">500</span> | |
| </label> | |
| <input type="range" id="assistantLength" min="50" max="1000" value="500" step="10"> | |
| </div> | |
| <div class="control-group"> | |
| <label> | |
| Max Previous User Message Length: <span class="value-display" id="userLengthValue">400</span> | |
| </label> | |
| <input type="range" id="userLength" min="50" max="1000" value="400" step="10"> | |
| </div> | |
| <div class="control-group"> | |
| <label>Select Conversation:</label> | |
| <select id="conversationSelect"> | |
| <option value="0">Loading conversations...</option> | |
| </select> | |
| </div> | |
| </div> | |
| <div class="code-reference"> | |
| <h3>Implementation (from src/lib/server/router/arch.ts)</h3> | |
| <div class="code-line"><span class="comment">// Trim assistant messages to reduce routing prompt size</span></div> | |
| <div class="code-line">if (m.role === "assistant") {</div> | |
| <div class="code-line"> return { ...m, content: trimMiddle(m.content, maxAssistantLength) };</div> | |
| <div class="code-line">}</div> | |
| <div class="code-line"></div> | |
| <div class="code-line"><span class="comment">// Trim previous user messages (latest preserved)</span></div> | |
| <div class="code-line">if (m.role === "user" && idx !== lastUserIndex) {</div> | |
| <div class="code-line"> return { ...m, content: trimMiddle(m.content, maxPrevUserLength) };</div> | |
| <div class="code-line">}</div> | |
| </div> | |
| <div id="conversationDisplay" class="conversation"> | |
| <p style="text-align: center; color: var(--muted);">Loading conversations...</p> | |
| </div> | |
| </div> | |
| <script> | |
| // Implementation of trimMiddle function from arch.ts | |
| function trimMiddle(content, maxLength) { | |
| if (content.length <= maxLength) return content; | |
| const indicator = "…"; | |
| const availableLength = maxLength - indicator.length; | |
| if (availableLength <= 0) { | |
| return content.slice(0, maxLength); | |
| } | |
| // Reserve more space for the start (60%) | |
| const startLength = Math.ceil(availableLength * 0.6); | |
| const endLength = availableLength - startLength; | |
| if (endLength <= 0) { | |
| return content.slice(0, availableLength) + indicator; | |
| } | |
| const start = content.slice(0, startLength); | |
| const end = content.slice(-endLength); | |
| return start + indicator + end; | |
| } | |
| // Fetch and store conversations | |
| let conversations = []; | |
| async function loadConversations() { | |
| try { | |
| const response = await fetch('https://datasets-server.huggingface.co/rows?dataset=allenai%2FWildChat-1M&config=default&split=train&offset=0&length=100'); | |
| const data = await response.json(); | |
| // Filter for conversations with multiple turns | |
| conversations = data.rows | |
| .filter(row => row.row.conversation && row.row.conversation.length >= 2) | |
| .map(row => row.row.conversation) | |
| .slice(0, 20); // Take first 20 conversations | |
| populateConversationSelect(); | |
| displayConversation(0); | |
| } catch (error) { | |
| console.error('Error loading conversations:', error); | |
| document.getElementById('conversationDisplay').innerHTML = | |
| '<p style="text-align: center; color: var(--accent);">Error loading conversations. Please refresh the page.</p>'; | |
| } | |
| } | |
| function populateConversationSelect() { | |
| const select = document.getElementById('conversationSelect'); | |
| select.innerHTML = conversations.map((conv, idx) => { | |
| const preview = conv[0].content.slice(0, 60).replace(/\n/g, ' '); | |
| return `<option value="${idx}">Conversation ${idx + 1}: ${preview}...</option>`; | |
| }).join(''); | |
| } | |
| function displayConversation(index) { | |
| const conversation = conversations[index]; | |
| if (!conversation) return; | |
| const assistantLength = parseInt(document.getElementById('assistantLength').value); | |
| const userLength = parseInt(document.getElementById('userLength').value); | |
| const lastUserIndex = conversation.map((m, idx) => ({ idx, role: m.role })) | |
| .reverse() | |
| .find(m => m.role === 'user')?.idx ?? -1; | |
| let html = ''; | |
| conversation.forEach((message, idx) => { | |
| const isUser = message.role === 'user'; | |
| const isLastUser = idx === lastUserIndex; | |
| const content = message.content; | |
| let trimmed = content; | |
| let shouldTrim = false; | |
| // Apply trimming logic | |
| if (message.role === 'assistant') { | |
| trimmed = trimMiddle(content, assistantLength); | |
| shouldTrim = content.length > assistantLength; | |
| } else if (message.role === 'user' && !isLastUser) { | |
| trimmed = trimMiddle(content, userLength); | |
| shouldTrim = content.length > userLength; | |
| } | |
| const reductionPercent = shouldTrim ? | |
| (((content.length - trimmed.length) / content.length) * 100).toFixed(1) : 0; | |
| html += ` | |
| <div class="message message-${message.role}"> | |
| <div class="message-header"> | |
| <span class="role-badge role-${message.role}">${message.role}</span> | |
| <span class="char-count">${content.length} characters</span> | |
| ${isLastUser ? '<span class="char-count" style="color: var(--accent); font-weight: 600;">★ Latest user message · preserved</span>' : ''} | |
| </div> | |
| <div class="message-content"> | |
| <div class="original"> | |
| <div class="section-label">Original</div> | |
| <div class="content-text">${escapeHtml(content)}</div> | |
| </div> | |
| <div class="${shouldTrim ? 'trimmed' : 'no-trim'}"> | |
| <div class="section-label">${shouldTrim ? 'Trimmed for Router' : 'No Trimming Required'}</div> | |
| <div class="content-text">${escapeHtml(trimmed)}</div> | |
| ${shouldTrim ? ` | |
| <div class="stats"> | |
| <strong>Original:</strong> ${content.length} chars | | |
| <strong>Trimmed:</strong> ${trimmed.length} chars | | |
| <span class="reduction">Reduced by ${reductionPercent}%</span> | |
| </div> | |
| ` : ''} | |
| </div> | |
| </div> | |
| </div> | |
| `; | |
| }); | |
| document.getElementById('conversationDisplay').innerHTML = html; | |
| } | |
| function escapeHtml(text) { | |
| const div = document.createElement('div'); | |
| div.textContent = text; | |
| return div.innerHTML; | |
| } | |
| // Event listeners | |
| document.getElementById('assistantLength').addEventListener('input', (e) => { | |
| document.getElementById('assistantLengthValue').textContent = e.target.value; | |
| const selectedIndex = parseInt(document.getElementById('conversationSelect').value); | |
| displayConversation(selectedIndex); | |
| }); | |
| document.getElementById('userLength').addEventListener('input', (e) => { | |
| document.getElementById('userLengthValue').textContent = e.target.value; | |
| const selectedIndex = parseInt(document.getElementById('conversationSelect').value); | |
| displayConversation(selectedIndex); | |
| }); | |
| document.getElementById('conversationSelect').addEventListener('change', (e) => { | |
| displayConversation(parseInt(e.target.value)); | |
| }); | |
| // Initialize | |
| loadConversations(); | |
| </script> | |
| </body> | |
| </html> | |