修改了数据集上传,修改了模型配置页面
This commit is contained in:
396
streaming-test.html
Normal file
396
streaming-test.html
Normal file
@@ -0,0 +1,396 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>流式输出测试页面</title>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
||||
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
||||
sans-serif;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
min-height: 100vh;
|
||||
padding: 20px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.container {
|
||||
background: white;
|
||||
border-radius: 16px;
|
||||
padding: 40px;
|
||||
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
|
||||
max-width: 800px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
h1 {
|
||||
text-align: center;
|
||||
color: #333;
|
||||
margin-bottom: 30px;
|
||||
font-size: 2.5rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.controls {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
margin-bottom: 30px;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
button {
|
||||
background: #667eea;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 12px 24px;
|
||||
border-radius: 8px;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background: #5a67d8;
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
button:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
button:disabled {
|
||||
background: #a0aec0;
|
||||
cursor: not-allowed;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.stream-output {
|
||||
background: #f7fafc;
|
||||
border: 2px solid #e2e8f0;
|
||||
border-radius: 12px;
|
||||
padding: 24px;
|
||||
min-height: 400px;
|
||||
max-height: 600px;
|
||||
overflow-y: auto;
|
||||
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
||||
font-size: 16px;
|
||||
line-height: 1.8;
|
||||
color: #2d3748;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.stream-output::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
}
|
||||
|
||||
.stream-output::-webkit-scrollbar-track {
|
||||
background: #edf2f7;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.stream-output::-webkit-scrollbar-thumb {
|
||||
background: #cbd5e0;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.stream-output::-webkit-scrollbar-thumb:hover {
|
||||
background: #a0aec0;
|
||||
}
|
||||
|
||||
.stats {
|
||||
margin-top: 20px;
|
||||
padding: 15px;
|
||||
background: #edf2f7;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
color: #4a5568;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.stats-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.loading-dots {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.loading-dots::after {
|
||||
content: '.';
|
||||
animation: dots 1s steps(5, end) infinite;
|
||||
}
|
||||
|
||||
@keyframes dots {
|
||||
0%, 20% { content: '.'; }
|
||||
40% { content: '..'; }
|
||||
60% { content: '...'; }
|
||||
80%, 100% { content: '.'; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>✨ 流式输出测试</h1>
|
||||
|
||||
<div class="controls">
|
||||
<button id="startBtn">开始流式输出</button>
|
||||
<button id="stopBtn" disabled>停止输出</button>
|
||||
<button id="clearBtn">清空内容</button>
|
||||
</div>
|
||||
|
||||
<div id="output" class="stream-output">
|
||||
<div style="color: #718096;">点击上方按钮开始测试流式输出...</div>
|
||||
</div>
|
||||
|
||||
<div class="stats">
|
||||
<div class="stats-item">
|
||||
<span>状态:</span>
|
||||
<span id="status" style="font-weight: 600;">就绪</span>
|
||||
</div>
|
||||
<div class="stats-item">
|
||||
<span>字符数:</span>
|
||||
<span id="charCount">0</span>
|
||||
</div>
|
||||
<div class="stats-item">
|
||||
<span>耗时:</span>
|
||||
<span id="timeElapsed">0.00s</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// 页面元素
|
||||
const startBtn = document.getElementById('startBtn');
|
||||
const stopBtn = document.getElementById('stopBtn');
|
||||
const clearBtn = document.getElementById('clearBtn');
|
||||
const output = document.getElementById('output');
|
||||
const statusEl = document.getElementById('status');
|
||||
const charCountEl = document.getElementById('charCount');
|
||||
const timeElapsedEl = document.getElementById('timeElapsed');
|
||||
|
||||
// 状态变量
|
||||
let controller = null;
|
||||
let startTime = 0;
|
||||
let charCount = 0;
|
||||
let timer = null;
|
||||
|
||||
// 模拟大模型输出的文本内容
|
||||
const sampleText = `欢迎使用流式输出测试页面!
|
||||
|
||||
这是一个模拟大语言模型生成内容的演示。
|
||||
|
||||
流式输出是一种将生成的内容逐字逐句发送给用户的技术,
|
||||
它可以显著提升用户体验,让用户感觉内容是实时生成的。
|
||||
|
||||
在实际应用中,大语言模型会在生成每个token后立即发送给客户端,
|
||||
而不需要等待整个响应完成。
|
||||
|
||||
这种技术特别适合:
|
||||
- 长文本生成
|
||||
- 对话系统
|
||||
- 代码生成
|
||||
- 实时翻译
|
||||
|
||||
流式输出的优势包括:
|
||||
1. 更快的感知响应速度
|
||||
2. 更好的用户体验
|
||||
3. 更低的内存占用
|
||||
4. 支持更长的内容生成
|
||||
|
||||
在这个测试中,我们将模拟每秒生成约50个字符的速度,
|
||||
您可以看到文本是如何逐字逐句显示的。
|
||||
|
||||
您可以随时点击"停止输出"按钮来中断流式传输,
|
||||
也可以点击"清空内容"按钮重新开始。
|
||||
|
||||
让我们继续探索流式输出的更多应用场景...
|
||||
|
||||
在Web应用中,流式输出通常使用以下技术实现:
|
||||
- Server-Sent Events (SSE)
|
||||
- WebSockets
|
||||
- HTTP/2 Server Push
|
||||
|
||||
每种技术都有其优缺点,选择合适的技术取决于具体的应用场景。
|
||||
|
||||
例如,SSE适合单向的服务器到客户端通信,
|
||||
而WebSockets适合双向通信。
|
||||
|
||||
流式输出不仅提升了用户体验,
|
||||
也为开发者提供了更多的灵活性和控制能力。
|
||||
|
||||
感谢您使用这个测试页面!
|
||||
|
||||
希望您对流式输出技术有了更深入的了解。
|
||||
|
||||
如果您有任何问题或建议,欢迎随时提出。
|
||||
|
||||
祝您使用愉快!`;
|
||||
|
||||
// 更新统计信息
|
||||
function updateStats() {
|
||||
charCountEl.textContent = charCount;
|
||||
|
||||
if (startTime > 0) {
|
||||
const elapsed = (Date.now() - startTime) / 1000;
|
||||
timeElapsedEl.textContent = elapsed.toFixed(2) + 's';
|
||||
}
|
||||
}
|
||||
|
||||
// 清空输出
|
||||
function clearOutput() {
|
||||
output.innerHTML = '<div style="color: #718096;">点击上方按钮开始测试流式输出...</div>';
|
||||
charCount = 0;
|
||||
updateStats();
|
||||
statusEl.textContent = '就绪';
|
||||
}
|
||||
|
||||
// 模拟流式输出
|
||||
async function simulateStreaming() {
|
||||
startBtn.disabled = true;
|
||||
stopBtn.disabled = false;
|
||||
clearOutput();
|
||||
|
||||
output.textContent = '';
|
||||
statusEl.innerHTML = '生成中 <span class="loading-dots"></span>';
|
||||
|
||||
startTime = Date.now();
|
||||
charCount = 0;
|
||||
|
||||
// 创建AbortController用于取消操作
|
||||
controller = new AbortController();
|
||||
const signal = controller.signal;
|
||||
|
||||
try {
|
||||
// 使用ReadableStream模拟流式输出
|
||||
const stream = new ReadableStream({
|
||||
async start(controller) {
|
||||
let index = 0;
|
||||
|
||||
while (index < sampleText.length && !signal.aborted) {
|
||||
// 随机延迟,模拟真实生成速度
|
||||
const delay = Math.random() * 80 + 40; // 40-120ms
|
||||
await new Promise(resolve => setTimeout(resolve, delay));
|
||||
|
||||
// 每次发送1-5个字符
|
||||
const chunkSize = Math.floor(Math.random() * 5) + 1;
|
||||
const chunk = sampleText.slice(index, index + chunkSize);
|
||||
|
||||
controller.enqueue(new TextEncoder().encode(chunk));
|
||||
index += chunkSize;
|
||||
|
||||
// 更新统计信息
|
||||
charCount += chunk.length;
|
||||
updateStats();
|
||||
}
|
||||
|
||||
controller.close();
|
||||
},
|
||||
cancel() {
|
||||
console.log('流式输出已取消');
|
||||
}
|
||||
});
|
||||
|
||||
// 读取并显示流内容
|
||||
const reader = stream.getReader();
|
||||
const decoder = new TextDecoder();
|
||||
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
if (done) break;
|
||||
|
||||
// 追加内容到输出
|
||||
output.textContent += decoder.decode(value, { stream: true });
|
||||
|
||||
// 自动滚动到底部
|
||||
output.scrollTop = output.scrollHeight;
|
||||
}
|
||||
|
||||
// 完成
|
||||
statusEl.textContent = '完成';
|
||||
output.style.borderColor = '#48bb78';
|
||||
|
||||
// 添加完成标记
|
||||
const doneMarker = document.createElement('div');
|
||||
doneMarker.style.cssText = `
|
||||
margin-top: 20px;
|
||||
padding: 10px;
|
||||
background: #48bb78;
|
||||
color: white;
|
||||
border-radius: 6px;
|
||||
text-align: center;
|
||||
font-weight: 600;
|
||||
`;
|
||||
doneMarker.textContent = '✅ 流式输出完成!';
|
||||
output.appendChild(doneMarker);
|
||||
|
||||
} catch (error) {
|
||||
if (error.name === 'AbortError') {
|
||||
statusEl.textContent = '已停止';
|
||||
output.style.borderColor = '#ed8936';
|
||||
|
||||
const stopMarker = document.createElement('div');
|
||||
stopMarker.style.cssText = `
|
||||
margin-top: 20px;
|
||||
padding: 10px;
|
||||
background: #ed8936;
|
||||
color: white;
|
||||
border-radius: 6px;
|
||||
text-align: center;
|
||||
font-weight: 600;
|
||||
`;
|
||||
stopMarker.textContent = '⏸️ 流式输出已停止';
|
||||
output.appendChild(stopMarker);
|
||||
} else {
|
||||
statusEl.textContent = '错误';
|
||||
output.innerHTML += `\n\n❌ 发生错误: ${error.message}`;
|
||||
output.style.borderColor = '#f56565';
|
||||
}
|
||||
} finally {
|
||||
// 重置状态
|
||||
startBtn.disabled = false;
|
||||
stopBtn.disabled = true;
|
||||
controller = null;
|
||||
if (timer) {
|
||||
clearInterval(timer);
|
||||
timer = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 停止流式输出
|
||||
function stopStreaming() {
|
||||
if (controller) {
|
||||
controller.abort();
|
||||
controller = null;
|
||||
}
|
||||
}
|
||||
|
||||
// 事件监听器
|
||||
startBtn.addEventListener('click', simulateStreaming);
|
||||
stopBtn.addEventListener('click', stopStreaming);
|
||||
clearBtn.addEventListener('click', clearOutput);
|
||||
|
||||
// 页面加载完成
|
||||
window.addEventListener('load', () => {
|
||||
console.log('流式输出测试页面已加载');
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user