1 line
8.8 KiB
HTML
1 line
8.8 KiB
HTML
<!DOCTYPE html>\n<html>\n<head>\n <title>流式LLM测试</title>\n <meta charset=\"utf-8\">\n <style>\n body {\n font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;\n max-width: 800px;\n margin: 0 auto;\n padding: 20px;\n background-color: #f5f5f5;\n }\n .container {\n background: white;\n border-radius: 8px;\n padding: 20px;\n box-shadow: 0 2px 10px rgba(0,0,0,0.1);\n }\n h1 {\n color: #333;\n text-align: center;\n }\n .controls {\n display: flex;\n gap: 10px;\n margin-bottom: 20px;\n align-items: flex-end;\n }\n .form-group {\n flex: 1;\n }\n label {\n display: block;\n margin-bottom: 5px;\n font-weight: bold;\n color: #555;\n }\n input, textarea {\n width: 100%;\n padding: 8px;\n border: 1px solid #ddd;\n border-radius: 4px;\n font-size: 14px;\n }\n button {\n padding: 10px 20px;\n background: #667eea;\n color: white;\n border: none;\n border-radius: 4px;\n cursor: pointer;\n font-size: 14px;\n transition: background 0.3s;\n }\n button:hover {\n background: #5a6fd8;\n }\n button:disabled {\n background: #ccc;\n cursor: not-allowed;\n }\n .output {\n background: #f8f9fa;\n border: 1px solid #e9ecef;\n border-radius: 4px;\n padding: 15px;\n min-height: 200px;\n white-space: pre-wrap;\n font-family: 'Courier New', monospace;\n line-height: 1.6;\n overflow-y: auto;\n max-height: 500px;\n }\n .status {\n margin: 10px 0;\n padding: 8px;\n border-radius: 4px;\n }\n .status.info {\n background: #d1ecf1;\n color: #0c5460;\n }\n .status.error {\n background: #f8d7da;\n color: #721c24;\n }\n .status.success {\n background: #d4edda;\n color: #155724;\n }\n </style>\n</head>\n<body>\n <div class=\"container\">\n <h1>🤖 流式LLM总结测试</h1>\n \n <div class=\"controls\">\n <div class=\"form-group\">\n <label>会议ID:</label>\n <input type=\"number\" id=\"meetingId\" value=\"38\" min=\"1\">\n </div>\n <div class=\"form-group\">\n <label>用户提示词:</label>\n <textarea id=\"userPrompt\" rows=\"2\" placeholder=\"请重点关注决策事项和待办任务\">请重点关注决策事项和待办任务</textarea>\n </div>\n <button id=\"streamBtn\">🚀 开始流式生成</button>\n <button id=\"normalBtn\">📝 普通生成</button>\n <button id=\"clearBtn\">🗑️ 清空</button>\n </div>\n \n <div id=\"status\" class=\"status\" style=\"display: none;\"></div>\n <div class=\"output\" id=\"output\">点击按钮开始测试...</div>\n </div>\n\n <script>\n const meetingIdInput = document.getElementById('meetingId');\n const userPromptInput = document.getElementById('userPrompt');\n const streamBtn = document.getElementById('streamBtn');\n const normalBtn = document.getElementById('normalBtn');\n const clearBtn = document.getElementById('clearBtn');\n const output = document.getElementById('output');\n const status = document.getElementById('status');\n\n function showStatus(message, type = 'info') {\n status.textContent = message;\n status.className = `status ${type}`;\n status.style.display = 'block';\n }\n\n function hideStatus() {\n status.style.display = 'none';\n }\n\n // 流式生成\n streamBtn.addEventListener('click', async () => {\n const meetingId = meetingIdInput.value;\n const userPrompt = userPromptInput.value;\n \n if (!meetingId) {\n showStatus('请输入会议ID', 'error');\n return;\n }\n\n streamBtn.disabled = true;\n normalBtn.disabled = true;\n output.textContent = '';\n showStatus('正在连接流式接口...', 'info');\n\n try {\n const response = await fetch(`http://localhost:8000/api/meetings/${meetingId}/generate-summary-stream`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': 'Bearer your_token_here' // 需要替换为实际token\n },\n body: JSON.stringify({ user_prompt: userPrompt })\n });\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n\n showStatus('📡 正在接收流式数据...', 'info');\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n const chunk = decoder.decode(value, { stream: true });\n const lines = chunk.split('\\n');\n \n for (const line of lines) {\n if (line.startsWith('data: ')) {\n try {\n const data = JSON.parse(line.slice(6));\n if (data.content) {\n output.textContent += data.content;\n output.scrollTop = output.scrollHeight;\n } else if (data.error) {\n showStatus(`错误: ${data.error}`, 'error');\n break;\n } else if (data.done) {\n showStatus('✅ 流式生成完成!', 'success');\n }\n } catch (e) {\n // 忽略无效的JSON数据\n }\n }\n }\n }\n\n } catch (error) {\n showStatus(`连接失败: ${error.message}`, 'error');\n console.error('Stream error:', error);\n } finally {\n streamBtn.disabled = false;\n normalBtn.disabled = false;\n }\n });\n\n // 普通生成\n normalBtn.addEventListener('click', async () => {\n const meetingId = meetingIdInput.value;\n const userPrompt = userPromptInput.value;\n \n if (!meetingId) {\n showStatus('请输入会议ID', 'error');\n return;\n }\n\n streamBtn.disabled = true;\n normalBtn.disabled = true;\n output.textContent = '';\n showStatus('正在调用普通接口...', 'info');\n\n try {\n const response = await fetch(`http://localhost:8000/api/meetings/${meetingId}/generate-summary`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': 'Bearer your_token_here' // 需要替换为实际token\n },\n body: JSON.stringify({ user_prompt: userPrompt })\n });\n\n const result = await response.json();\n\n if (response.ok) {\n output.textContent = result.content;\n showStatus('✅ 普通生成完成!', 'success');\n } else {\n showStatus(`错误: ${result.detail}`, 'error');\n }\n\n } catch (error) {\n showStatus(`请求失败: ${error.message}`, 'error');\n console.error('Request error:', error);\n } finally {\n streamBtn.disabled = false;\n normalBtn.disabled = false;\n }\n });\n\n // 清空输出\n clearBtn.addEventListener('click', () => {\n output.textContent = '点击按钮开始测试...';\n hideStatus();\n });\n </script>\n</body>\n</html> |