feat: 流式输出支持 - 思考内容流式显示后折叠,回答内容流式输出

This commit is contained in:
2026-04-12 17:16:06 +08:00
parent 6adeb9b371
commit a62fe929c1
2 changed files with 170 additions and 47 deletions

View File

@@ -349,7 +349,6 @@
switch (data.type) {
case 'history':
displayHistory(data.messages);
// 更新Agent选择框为该对话使用的Agent
if (data.agent_id) {
currentAgentId = data.agent_id;
updateAgentSelect(data.agent_id);
@@ -368,14 +367,23 @@
currentAgentId = data.agent_id;
console.log('Agent已切换:', data.agent_name);
break;
// 流式输出处理
case 'thinking_start':
showThinkingIndicator();
createStreamingMessage('thinking');
break;
case 'thinking_content':
updateThinkingContent(data.content);
case 'thinking_stream':
appendStreamText('thinking', data.text);
break;
case 'thinking_end':
hideThinkingIndicator();
collapseThinking(data.content);
createStreamingMessage('content');
break;
case 'content_stream':
appendStreamText('content', data.text);
break;
case 'stream_end':
finishStreaming();
document.getElementById('sendBtn').disabled = false;
break;
case 'user_message':
appendMessage('user', data.message.content, data.message.source);
@@ -385,46 +393,129 @@
}
break;
case 'assistant_message':
appendMessage('assistant', data.message.content, data.message.source, data.message.thinking_content, data.message.agent_name);
document.getElementById('sendBtn').disabled = false;
// 流式完成后更新最终消息添加Agent信息等
updateFinalMessage(data.message);
break;
case 'error':
alert(data.message);
showError(data.message);
document.getElementById('sendBtn').disabled = false;
break;
}
}
// 显示思考指示
function showThinkingIndicator() {
// 创建流式消息容
function createStreamingMessage(type) {
const container = document.getElementById('messagesContainer');
const thinking = document.createElement('div');
thinking.id = 'thinkingIndicator';
thinking.className = 'message assistant';
thinking.innerHTML = `
<div class="message-avatar">🤔</div>
<div class="message-content" style="color:#667eea;">
<span class="loading">正在思考</span>
</div>
`;
container.appendChild(thinking);
container.querySelector('.welcome')?.remove();
const existingStream = document.getElementById('streaming-message');
if (existingStream && type === 'content') return; // 回答区域已存在
const div = document.createElement('div');
div.id = 'streaming-message';
div.className = 'message assistant';
if (type === 'thinking') {
div.innerHTML = `
<div class="message-avatar">🤔</div>
<div class="message-content">
<div class="thinking-stream" id="thinking-stream" style="background:#f8f9fa;border-left:3px solid #667eea;padding:12px;font-size:14px;color:#667eea;">
<strong>💭 思考过程</strong><br>
<span id="thinking-text"></span>
</div>
</div>
`;
} else {
div.innerHTML = `
<div class="message-avatar">🤖</div>
<div class="message-content">
<div id="thinking-collapsed" style="display:none;"></div>
<span id="content-text"></span>
</div>
`;
}
container.appendChild(div);
container.scrollTop = container.scrollHeight;
}
// 更新思考内容
function updateThinkingContent(content) {
const indicator = document.getElementById('thinkingIndicator');
if (indicator) {
indicator.querySelector('.message-content').innerHTML = `
<div class="thinking-content">${formatContent(content)}</div>
`;
// 追加流式文本
function appendStreamText(type, text) {
const textEl = document.getElementById(type === 'thinking' ? 'thinking-text' : 'content-text');
if (textEl) {
textEl.textContent += text;
const container = document.getElementById('messagesContainer');
container.scrollTop = container.scrollHeight;
}
}
// 隐藏思考指示器
function hideThinkingIndicator() {
const indicator = document.getElementById('thinkingIndicator');
if (indicator) indicator.remove();
// 折叠思考内容
function collapseThinking(content) {
const streamEl = document.getElementById('thinking-stream');
if (streamEl) {
// 创建折叠版本
const collapsedDiv = document.getElementById('thinking-collapsed');
if (collapsedDiv) {
collapsedDiv.style.display = 'block';
collapsedDiv.innerHTML = `
<div class="thinking-collapsed" onclick="toggleThinkingBlock(this)" style="cursor:pointer;background:#f0f0f0;padding:8px 12px;border-radius:4px;margin-bottom:8px;font-size:13px;color:#667eea;">
<i class="ri-lightbulb-line"></i> 思考过程 <span class="toggle-hint">(点击展开)</span>
</div>
<div class="thinking-expanded" style="display:none;background:#f8f9fa;border-left:3px solid #667eea;padding:12px;font-size:14px;margin-bottom:12px;">
${formatContent(content)}
</div>
`;
}
streamEl.style.display = 'none';
}
}
// 展开/折叠思考内容
function toggleThinkingBlock(el) {
const expanded = el.parentElement.querySelector('.thinking-expanded');
const hint = el.querySelector('.toggle-hint');
if (expanded.style.display === 'none') {
expanded.style.display = 'block';
hint.textContent = '(点击折叠)';
} else {
expanded.style.display = 'none';
hint.textContent = '(点击展开)';
}
}
// 完成流式输出
function finishStreaming() {
const streamMsg = document.getElementById('streaming-message');
if (streamMsg) {
streamMsg.id = ''; // 移除临时ID
}
}
// 更新最终消息添加Agent信息
function updateFinalMessage(msg) {
const messages = document.querySelectorAll('.message.assistant');
const lastMsg = messages[messages.length - 1];
if (lastMsg && msg.agent_name) {
const contentDiv = lastMsg.querySelector('.message-content');
if (contentDiv && !contentDiv.querySelector('.agent-info')) {
const agentInfo = document.createElement('div');
agentInfo.className = 'agent-info';
agentInfo.style.cssText = 'font-size:12px;color:#999;margin-top:8px;';
agentInfo.innerHTML = `<i class="ri-robot-line"></i> ${msg.agent_name}`;
contentDiv.appendChild(agentInfo);
}
}
}
// 显示错误
function showError(msg) {
const container = document.getElementById('messagesContainer');
const div = document.createElement('div');
div.className = 'message assistant';
div.innerHTML = `
<div class="message-avatar">❌</div>
<div class="message-content" style="color:#dc3545;">${msg}</div>
`;
container.appendChild(div);
}
// 清空消息