diff --git a/bridge/agent_event_handler.py b/bridge/agent_event_handler.py
index 8bc7f62..b04c77b 100644
--- a/bridge/agent_event_handler.py
+++ b/bridge/agent_event_handler.py
@@ -94,15 +94,15 @@ class AgentEventHandler:
def _send_to_channel(self, message):
"""
- Try to send message to channel
-
- Args:
- message: Message to send
+ Try to send intermediate message to channel.
+ Skipped in SSE mode because thinking text is already streamed via on_event.
"""
+ if self.context and self.context.get("on_event"):
+ return
+
if self.channel:
try:
from bridge.reply import Reply, ReplyType
- # Create a Reply object for the message
reply = Reply(ReplyType.TEXT, message)
self.channel._send(reply, self.context)
except Exception as e:
diff --git a/channel/channel.py b/channel/channel.py
index 42d613f..f01189e 100644
--- a/channel/channel.py
+++ b/channel/channel.py
@@ -57,11 +57,14 @@ class Channel(object):
if context and "channel_type" not in context:
context["channel_type"] = self.channel_type
+ # Read on_event callback injected by the channel (e.g. web SSE)
+ on_event = context.get("on_event") if context else None
+
# Use agent bridge to handle the query
return Bridge().fetch_agent_reply(
query=query,
context=context,
- on_event=None,
+ on_event=on_event,
clear_history=False
)
except Exception as e:
diff --git a/channel/web/chat.html b/channel/web/chat.html
index 2ad5474..6f28ebc 100644
--- a/channel/web/chat.html
+++ b/channel/web/chat.html
@@ -1,1545 +1,524 @@
-
+
- // 添加一个变量来跟踪输入法状态
- let isComposing = false;
-
- // 监听输入法组合状态开始
- input.addEventListener('compositionstart', function() {
- isComposing = true;
- });
-
- // 监听输入法组合状态结束
- input.addEventListener('compositionend', function() {
- isComposing = false;
- });
-
- // 自动调整文本区域高度
- input.addEventListener('input', function() {
- this.style.height = 'auto';
- this.style.height = (this.scrollHeight) + 'px';
-
- // 启用/禁用发送按钮
- sendButton.disabled = !this.value.trim();
- });
-
- // 处理示例卡片点击
- exampleCards.forEach(card => {
- card.addEventListener('click', function() {
- const exampleText = this.querySelector('.example-text').textContent;
- input.value = exampleText;
- input.dispatchEvent(new Event('input'));
- input.focus();
- });
- });
-
- // 处理菜单切换
- menuToggle.addEventListener('click', function(event) {
- event.stopPropagation(); // 防止事件冒泡到 main-content
- sidebar.classList.toggle('active');
- });
-
- // 处理新对话按钮 - 创建新的会话ID和清空当前对话
- newChatButton.addEventListener('click', function() {
- // 生成新的会话ID
- sessionId = generateSessionId();
- // 将新的会话ID保存到全局变量,供轮询函数使用
- window.sessionId = sessionId;
- console.log('New conversation started with new session ID:', sessionId);
-
- // 清空聊天记录
- clearChat();
- });
-
- // 发送按钮点击事件
- sendButton.addEventListener('click', function() {
- sendMessage();
- });
-
- // 输入框按键事件
- input.addEventListener('keydown', function(event) {
- // Ctrl+Enter 或 Shift+Enter 添加换行
- if ((event.ctrlKey || event.shiftKey) && event.key === 'Enter') {
- const start = this.selectionStart;
- const end = this.selectionEnd;
- const value = this.value;
-
- this.value = value.substring(0, start) + '\n' + value.substring(end);
- this.selectionStart = this.selectionEnd = start + 1;
-
- event.preventDefault();
- }
- // Enter 键发送消息,但只在不是输入法组合状态时
- else if (event.key === 'Enter' && !event.shiftKey && !event.ctrlKey && !isComposing) {
- sendMessage();
- event.preventDefault();
- }
- });
-
- // 在发送消息函数前添加调试代码
- console.log('Axios loaded:', typeof axios !== 'undefined');
-
- // 发送消息函数
- function sendMessage() {
- console.log('Send message function called');
- const userMessage = input.value.trim();
- if (userMessage) {
- // 隐藏欢迎屏幕
- const welcomeScreenElement = document.getElementById('welcome-screen');
- if (welcomeScreenElement) {
- welcomeScreenElement.remove();
- }
-
- const timestamp = new Date();
-
- // 添加用户消息到界面
- addUserMessage(userMessage, timestamp);
-
- // 添加一个等待中的机器人消息
- const loadingContainer = addLoadingMessage();
-
- // 清空输入框并重置高度 - 移到这里,确保发送后立即清空
- input.value = '';
- input.style.height = '52px';
- sendButton.disabled = true;
-
- // 使用当前的全局会话ID
- const currentSessionId = window.sessionId || sessionId;
-
- // 发送到服务器并获取请求ID
- axios({
- method: 'post',
- url: '/message',
- data: {
- session_id: currentSessionId, // 使用最新的会话ID
- message: userMessage,
- timestamp: timestamp.toISOString()
- },
- timeout: 10000 // 10秒超时
- })
- .then(response => {
- if (response.data.status === "success") {
- // 保存当前请求ID,用于识别响应
- const currentRequestId = response.data.request_id;
-
- // 如果还没有开始轮询,则开始轮询
- if (!window.isPolling) {
- startPolling(currentSessionId);
- }
-
- // 将请求ID和加载容器关联起来
- window.loadingContainers = window.loadingContainers || {};
- window.loadingContainers[currentRequestId] = loadingContainer;
-
- // 初始化请求的响应容器映射
- window.requestContainers = window.requestContainers || {};
- } else {
- // 处理错误
- if (loadingContainer.parentNode) {
- messagesDiv.removeChild(loadingContainer);
- }
- addBotMessage("抱歉,发生了错误,请稍后再试。", new Date());
- }
- })
- .catch(error => {
- console.error('Error sending message:', error);
- // 移除加载消息
- if (loadingContainer.parentNode) {
- messagesDiv.removeChild(loadingContainer);
- }
- // 显示错误消息
- if (error.code === 'ECONNABORTED') {
- addBotMessage("请求超时,请再试一次吧。", new Date());
- } else {
- addBotMessage("抱歉,发生了错误,请稍后再试。", new Date());
- }
- });
- }
- }
-
- // 修改轮询函数,确保正确处理多条回复
- function startPolling(sessionId) {
- if (window.isPolling) return;
-
- window.isPolling = true;
- console.log('Starting polling with session ID:', sessionId);
-
- function poll() {
- if (!window.isPolling) return;
-
- // 如果页面已关闭或导航离开,停止轮询
- if (document.hidden) {
- setTimeout(poll, 5000); // 页面不可见时降低轮询频率
- return;
- }
-
- // 使用当前的会话ID,而不是闭包中的sessionId
- const currentSessionId = window.sessionId || sessionId;
-
- axios({
- method: 'post',
- url: '/poll',
- data: {
- session_id: currentSessionId
- },
- timeout: 5000
- })
- .then(response => {
- if (response.data.status === "success") {
- if (response.data.has_content) {
- console.log('Received response:', response.data);
-
- // 获取请求ID和内容
- const requestId = response.data.request_id;
- const content = response.data.content;
- const timestamp = new Date(response.data.timestamp * 1000);
-
- // 检查是否有对应的加载容器
- if (window.loadingContainers && window.loadingContainers[requestId]) {
- // 移除加载容器
- const loadingContainer = window.loadingContainers[requestId];
- if (loadingContainer && loadingContainer.parentNode) {
- messagesDiv.removeChild(loadingContainer);
- }
-
- // 删除已处理的加载容器引用
- delete window.loadingContainers[requestId];
- }
-
- // 始终创建新的消息,无论是否是同一个请求的后续回复
- addBotMessage(content, timestamp, requestId);
-
- // 滚动到底部
- scrollToBottom();
- }
-
- // 继续轮询,使用原来的2秒间隔
- setTimeout(poll, 2000);
- } else {
- // 处理错误但继续轮询
- console.error('Error in polling response:', response.data.message);
- setTimeout(poll, 3000);
- }
- })
- .catch(error => {
- console.error('Error polling for response:', error);
- // 出错后继续轮询,但间隔更长
- setTimeout(poll, 3000);
- });
- }
-
- // 开始轮询
- poll();
- }
-
- // 添加机器人消息的函数 (保存到localStorage),增加requestId参数
- function addBotMessage(content, timestamp, requestId) {
- // 显示消息
- displayBotMessage(content, timestamp, requestId);
-
- // 保存到localStorage
- saveMessageToLocalStorage({
- role: 'assistant',
- content: content,
- timestamp: timestamp.getTime(),
- requestId: requestId
- });
- }
-
- // 修改显示机器人消息的函数,增加requestId参数
- function displayBotMessage(content, timestamp, requestId) {
- const botContainer = document.createElement('div');
- botContainer.className = 'bot-container';
-
- // 如果有requestId,将其存储在数据属性中
- if (requestId) {
- botContainer.dataset.requestId = requestId;
- }
-
- const messageContainer = document.createElement('div');
- messageContainer.className = 'message-container';
-
- // 安全地格式化消息
- let formattedContent;
- try {
- formattedContent = formatMessage(content);
- } catch (e) {
- console.error('Error formatting bot message:', e);
- formattedContent = `
${content.replace(/\n/g, '
')}
`;
- }
-
- messageContainer.innerHTML = `
-
-
+
+
+
+