feat:添加思考状态发送,优化s4u队列
This commit is contained in:
@@ -147,49 +147,30 @@ class ContextWebManager:
|
|||||||
border-left: 4px solid #00ff88;
|
border-left: 4px solid #00ff88;
|
||||||
backdrop-filter: blur(5px);
|
backdrop-filter: blur(5px);
|
||||||
animation: slideIn 0.3s ease-out;
|
animation: slideIn 0.3s ease-out;
|
||||||
|
transform: translateY(0);
|
||||||
|
transition: transform 0.5s ease, opacity 0.5s ease;
|
||||||
}
|
}
|
||||||
.message:hover {
|
.message:hover {
|
||||||
background: rgba(0, 0, 0, 0.5);
|
background: rgba(0, 0, 0, 0.5);
|
||||||
transform: translateX(5px);
|
transform: translateX(5px);
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
}
|
}
|
||||||
.user-info {
|
.message-line {
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
.username {
|
|
||||||
font-weight: bold;
|
|
||||||
color: #00ff88;
|
|
||||||
font-size: 18px;
|
|
||||||
}
|
|
||||||
.timestamp {
|
|
||||||
color: #888;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
.group-name {
|
|
||||||
color: #60a5fa;
|
|
||||||
font-size: 14px;
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
.content {
|
|
||||||
font-size: 20px;
|
|
||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
|
font-size: 24px;
|
||||||
}
|
}
|
||||||
.status {
|
.username {
|
||||||
position: fixed;
|
color: #00ff88;
|
||||||
top: 20px;
|
|
||||||
right: 20px;
|
|
||||||
background: rgba(0, 0, 0, 0.7);
|
|
||||||
color: #888;
|
|
||||||
font-size: 12px;
|
|
||||||
padding: 8px 12px;
|
|
||||||
border-radius: 20px;
|
|
||||||
backdrop-filter: blur(10px);
|
|
||||||
z-index: 1000;
|
|
||||||
}
|
}
|
||||||
|
.content {
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.new-message {
|
||||||
|
animation: slideInNew 0.6s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
.debug-btn {
|
.debug-btn {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
bottom: 20px;
|
bottom: 20px;
|
||||||
@@ -217,6 +198,16 @@ class ContextWebManager:
|
|||||||
transform: translateY(0);
|
transform: translateY(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@keyframes slideInNew {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(50px) scale(0.95);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0) scale(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
.no-messages {
|
.no-messages {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
color: #666;
|
color: #666;
|
||||||
@@ -227,7 +218,6 @@ class ContextWebManager:
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="status" id="status">正在连接...</div>
|
|
||||||
<a href="/debug" class="debug-btn">🔧 调试</a>
|
<a href="/debug" class="debug-btn">🔧 调试</a>
|
||||||
<div id="messages">
|
<div id="messages">
|
||||||
<div class="no-messages">暂无消息</div>
|
<div class="no-messages">暂无消息</div>
|
||||||
@@ -237,6 +227,7 @@ class ContextWebManager:
|
|||||||
<script>
|
<script>
|
||||||
let ws;
|
let ws;
|
||||||
let reconnectInterval;
|
let reconnectInterval;
|
||||||
|
let currentMessages = []; // 存储当前显示的消息
|
||||||
|
|
||||||
function connectWebSocket() {
|
function connectWebSocket() {
|
||||||
console.log('正在连接WebSocket...');
|
console.log('正在连接WebSocket...');
|
||||||
@@ -244,8 +235,6 @@ class ContextWebManager:
|
|||||||
|
|
||||||
ws.onopen = function() {
|
ws.onopen = function() {
|
||||||
console.log('WebSocket连接已建立');
|
console.log('WebSocket连接已建立');
|
||||||
document.getElementById('status').textContent = '✅ 已连接';
|
|
||||||
document.getElementById('status').style.color = '#00ff88';
|
|
||||||
if (reconnectInterval) {
|
if (reconnectInterval) {
|
||||||
clearInterval(reconnectInterval);
|
clearInterval(reconnectInterval);
|
||||||
reconnectInterval = null;
|
reconnectInterval = null;
|
||||||
@@ -264,8 +253,6 @@ class ContextWebManager:
|
|||||||
|
|
||||||
ws.onclose = function(event) {
|
ws.onclose = function(event) {
|
||||||
console.log('WebSocket连接关闭:', event.code, event.reason);
|
console.log('WebSocket连接关闭:', event.code, event.reason);
|
||||||
document.getElementById('status').textContent = '❌ 连接断开,正在重连...';
|
|
||||||
document.getElementById('status').style.color = '#ff6b6b';
|
|
||||||
|
|
||||||
if (!reconnectInterval) {
|
if (!reconnectInterval) {
|
||||||
reconnectInterval = setInterval(connectWebSocket, 3000);
|
reconnectInterval = setInterval(connectWebSocket, 3000);
|
||||||
@@ -274,8 +261,6 @@ class ContextWebManager:
|
|||||||
|
|
||||||
ws.onerror = function(error) {
|
ws.onerror = function(error) {
|
||||||
console.error('WebSocket错误:', error);
|
console.error('WebSocket错误:', error);
|
||||||
document.getElementById('status').textContent = '❌ 连接错误';
|
|
||||||
document.getElementById('status').style.color = '#ff6b6b';
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -284,30 +269,117 @@ class ContextWebManager:
|
|||||||
|
|
||||||
if (!contexts || contexts.length === 0) {
|
if (!contexts || contexts.length === 0) {
|
||||||
messagesDiv.innerHTML = '<div class="no-messages">暂无消息</div>';
|
messagesDiv.innerHTML = '<div class="no-messages">暂无消息</div>';
|
||||||
|
currentMessages = [];
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('收到新消息,数量:', contexts.length);
|
// 如果是第一次加载或者消息完全不同,进行完全重新渲染
|
||||||
|
if (currentMessages.length === 0) {
|
||||||
|
console.log('首次加载消息,数量:', contexts.length);
|
||||||
messagesDiv.innerHTML = '';
|
messagesDiv.innerHTML = '';
|
||||||
|
|
||||||
contexts.forEach(function(msg) {
|
contexts.forEach(function(msg) {
|
||||||
const messageDiv = document.createElement('div');
|
const messageDiv = createMessageElement(msg);
|
||||||
messageDiv.className = 'message';
|
|
||||||
messageDiv.innerHTML = `
|
|
||||||
<div class="user-info">
|
|
||||||
<div>
|
|
||||||
<span class="username">${escapeHtml(msg.user_name)}</span>
|
|
||||||
<span class="group-name">[${escapeHtml(msg.group_name)}]</span>
|
|
||||||
</div>
|
|
||||||
<span class="timestamp">${msg.timestamp}</span>
|
|
||||||
</div>
|
|
||||||
<div class="content">${escapeHtml(msg.content)}</div>
|
|
||||||
`;
|
|
||||||
messagesDiv.appendChild(messageDiv);
|
messagesDiv.appendChild(messageDiv);
|
||||||
});
|
});
|
||||||
|
|
||||||
// 滚动到最底部显示最新消息
|
currentMessages = [...contexts];
|
||||||
window.scrollTo(0, document.body.scrollHeight);
|
window.scrollTo(0, document.body.scrollHeight);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检测新消息 - 使用更可靠的方法
|
||||||
|
const newMessages = findNewMessages(contexts, currentMessages);
|
||||||
|
|
||||||
|
if (newMessages.length > 0) {
|
||||||
|
console.log('添加新消息,数量:', newMessages.length);
|
||||||
|
|
||||||
|
// 先检查是否需要移除老消息(保持DOM清洁)
|
||||||
|
const maxDisplayMessages = 15; // 比服务器端稍多一些,确保流畅性
|
||||||
|
const currentMessageElements = messagesDiv.querySelectorAll('.message');
|
||||||
|
const willExceedLimit = currentMessageElements.length + newMessages.length > maxDisplayMessages;
|
||||||
|
|
||||||
|
if (willExceedLimit) {
|
||||||
|
const removeCount = (currentMessageElements.length + newMessages.length) - maxDisplayMessages;
|
||||||
|
console.log('需要移除老消息数量:', removeCount);
|
||||||
|
|
||||||
|
for (let i = 0; i < removeCount && i < currentMessageElements.length; i++) {
|
||||||
|
const oldMessage = currentMessageElements[i];
|
||||||
|
oldMessage.style.transition = 'opacity 0.3s ease, transform 0.3s ease';
|
||||||
|
oldMessage.style.opacity = '0';
|
||||||
|
oldMessage.style.transform = 'translateY(-20px)';
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
if (oldMessage.parentNode) {
|
||||||
|
oldMessage.parentNode.removeChild(oldMessage);
|
||||||
|
}
|
||||||
|
}, 300);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加新消息
|
||||||
|
newMessages.forEach(function(msg) {
|
||||||
|
const messageDiv = createMessageElement(msg, true); // true表示是新消息
|
||||||
|
messagesDiv.appendChild(messageDiv);
|
||||||
|
|
||||||
|
// 移除动画类,避免重复动画
|
||||||
|
setTimeout(() => {
|
||||||
|
messageDiv.classList.remove('new-message');
|
||||||
|
}, 600);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 更新当前消息列表
|
||||||
|
currentMessages = [...contexts];
|
||||||
|
|
||||||
|
// 平滑滚动到底部
|
||||||
|
setTimeout(() => {
|
||||||
|
window.scrollTo({
|
||||||
|
top: document.body.scrollHeight,
|
||||||
|
behavior: 'smooth'
|
||||||
|
});
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function findNewMessages(contexts, currentMessages) {
|
||||||
|
// 如果当前消息为空,所有消息都是新的
|
||||||
|
if (currentMessages.length === 0) {
|
||||||
|
return contexts;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 找到最后一条当前消息在新消息列表中的位置
|
||||||
|
const lastCurrentMsg = currentMessages[currentMessages.length - 1];
|
||||||
|
let lastIndex = -1;
|
||||||
|
|
||||||
|
// 从后往前找,因为新消息通常在末尾
|
||||||
|
for (let i = contexts.length - 1; i >= 0; i--) {
|
||||||
|
const msg = contexts[i];
|
||||||
|
if (msg.user_id === lastCurrentMsg.user_id &&
|
||||||
|
msg.content === lastCurrentMsg.content &&
|
||||||
|
msg.timestamp === lastCurrentMsg.timestamp) {
|
||||||
|
lastIndex = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果找到了,返回之后的消息;否则返回所有消息(可能是完全刷新)
|
||||||
|
if (lastIndex >= 0) {
|
||||||
|
return contexts.slice(lastIndex + 1);
|
||||||
|
} else {
|
||||||
|
console.log('未找到匹配的最后消息,可能需要完全刷新');
|
||||||
|
return contexts.slice(Math.max(0, contexts.length - (currentMessages.length + 1)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function createMessageElement(msg, isNew = false) {
|
||||||
|
const messageDiv = document.createElement('div');
|
||||||
|
messageDiv.className = 'message' + (isNew ? ' new-message' : '');
|
||||||
|
messageDiv.innerHTML = `
|
||||||
|
<div class="message-line">
|
||||||
|
<span class="username">${escapeHtml(msg.user_name)}:</span><span class="content">${escapeHtml(msg.content)}</span>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
return messageDiv;
|
||||||
}
|
}
|
||||||
|
|
||||||
function escapeHtml(text) {
|
function escapeHtml(text) {
|
||||||
@@ -542,3 +614,4 @@ async def init_context_web_manager():
|
|||||||
manager = get_context_web_manager()
|
manager = get_context_web_manager()
|
||||||
await manager.start_server()
|
await manager.start_server()
|
||||||
return manager
|
return manager
|
||||||
|
|
||||||
|
|||||||
31
src/mais4u/mais4u_chat/loading.py
Normal file
31
src/mais4u/mais4u_chat/loading.py
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import asyncio
|
||||||
|
import json
|
||||||
|
import time
|
||||||
|
|
||||||
|
from src.chat.message_receive.message import MessageRecv
|
||||||
|
from src.llm_models.utils_model import LLMRequest
|
||||||
|
from src.common.logger import get_logger
|
||||||
|
from src.chat.utils.chat_message_builder import build_readable_messages, get_raw_msg_by_timestamp_with_chat_inclusive
|
||||||
|
from src.config.config import global_config
|
||||||
|
from src.chat.utils.prompt_builder import Prompt, global_prompt_manager
|
||||||
|
from src.manager.async_task_manager import AsyncTask, async_task_manager
|
||||||
|
from src.plugin_system.apis import send_api
|
||||||
|
|
||||||
|
async def send_loading(chat_id: str, content: str):
|
||||||
|
await send_api.custom_to_stream(
|
||||||
|
message_type="loading",
|
||||||
|
content=content,
|
||||||
|
stream_id=chat_id,
|
||||||
|
storage_message=False,
|
||||||
|
show_log=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
async def send_unloading(chat_id: str):
|
||||||
|
await send_api.custom_to_stream(
|
||||||
|
message_type="loading",
|
||||||
|
content="",
|
||||||
|
stream_id=chat_id,
|
||||||
|
storage_message=False,
|
||||||
|
show_log=True,
|
||||||
|
)
|
||||||
|
|
||||||
@@ -12,7 +12,8 @@ from src.common.message.api import get_global_api
|
|||||||
from src.chat.message_receive.storage import MessageStorage
|
from src.chat.message_receive.storage import MessageStorage
|
||||||
from .s4u_watching_manager import watching_manager
|
from .s4u_watching_manager import watching_manager
|
||||||
import json
|
import json
|
||||||
|
from src.person_info.relationship_builder_manager import relationship_builder_manager
|
||||||
|
from .loading import send_loading, send_unloading
|
||||||
|
|
||||||
logger = get_logger("S4U_chat")
|
logger = get_logger("S4U_chat")
|
||||||
|
|
||||||
@@ -29,6 +30,7 @@ class MessageSenderContainer:
|
|||||||
self._paused_event = asyncio.Event()
|
self._paused_event = asyncio.Event()
|
||||||
self._paused_event.set() # 默认设置为非暂停状态
|
self._paused_event.set() # 默认设置为非暂停状态
|
||||||
|
|
||||||
|
|
||||||
async def add_message(self, chunk: str):
|
async def add_message(self, chunk: str):
|
||||||
"""向队列中添加一个消息块。"""
|
"""向队列中添加一个消息块。"""
|
||||||
await self.queue.put(chunk)
|
await self.queue.put(chunk)
|
||||||
@@ -142,7 +144,7 @@ def get_s4u_chat_manager() -> S4UChatManager:
|
|||||||
|
|
||||||
|
|
||||||
class S4UChat:
|
class S4UChat:
|
||||||
_MESSAGE_TIMEOUT_SECONDS = 30 # 普通消息存活时间(秒)
|
_MESSAGE_TIMEOUT_SECONDS = 120 # 普通消息存活时间(秒)
|
||||||
|
|
||||||
def __init__(self, chat_stream: ChatStream):
|
def __init__(self, chat_stream: ChatStream):
|
||||||
"""初始化 S4UChat 实例。"""
|
"""初始化 S4UChat 实例。"""
|
||||||
@@ -150,6 +152,7 @@ class S4UChat:
|
|||||||
self.chat_stream = chat_stream
|
self.chat_stream = chat_stream
|
||||||
self.stream_id = chat_stream.stream_id
|
self.stream_id = chat_stream.stream_id
|
||||||
self.stream_name = get_chat_manager().get_stream_name(self.stream_id) or self.stream_id
|
self.stream_name = get_chat_manager().get_stream_name(self.stream_id) or self.stream_id
|
||||||
|
self.relationship_builder = relationship_builder_manager.get_or_create_builder(self.stream_id)
|
||||||
|
|
||||||
# 两个消息队列
|
# 两个消息队列
|
||||||
self._vip_queue = asyncio.PriorityQueue()
|
self._vip_queue = asyncio.PriorityQueue()
|
||||||
@@ -167,7 +170,7 @@ class S4UChat:
|
|||||||
self.gpt = S4UStreamGenerator()
|
self.gpt = S4UStreamGenerator()
|
||||||
self.interest_dict: Dict[str, float] = {} # 用户兴趣分
|
self.interest_dict: Dict[str, float] = {} # 用户兴趣分
|
||||||
self.at_bot_priority_bonus = 100.0 # @机器人的优先级加成
|
self.at_bot_priority_bonus = 100.0 # @机器人的优先级加成
|
||||||
self.normal_queue_max_size = 5 # 普通队列最大容量
|
self.recent_message_keep_count = 6 # 保留最近N条消息,超出范围的普通消息将被移除
|
||||||
logger.info(f"[{self.stream_name}] S4UChat with two-queue system initialized.")
|
logger.info(f"[{self.stream_name}] S4UChat with two-queue system initialized.")
|
||||||
|
|
||||||
def _get_priority_info(self, message: MessageRecv) -> dict:
|
def _get_priority_info(self, message: MessageRecv) -> dict:
|
||||||
@@ -211,6 +214,9 @@ class S4UChat:
|
|||||||
|
|
||||||
async def add_message(self, message: MessageRecv) -> None:
|
async def add_message(self, message: MessageRecv) -> None:
|
||||||
"""根据VIP状态和中断逻辑将消息放入相应队列。"""
|
"""根据VIP状态和中断逻辑将消息放入相应队列。"""
|
||||||
|
|
||||||
|
await self.relationship_builder.build_relation()
|
||||||
|
|
||||||
priority_info = self._get_priority_info(message)
|
priority_info = self._get_priority_info(message)
|
||||||
is_vip = self._is_vip(priority_info)
|
is_vip = self._is_vip(priority_info)
|
||||||
new_priority_score = self._calculate_base_priority_score(message, priority_info)
|
new_priority_score = self._calculate_base_priority_score(message, priority_info)
|
||||||
@@ -258,20 +264,46 @@ class S4UChat:
|
|||||||
await self._vip_queue.put(item)
|
await self._vip_queue.put(item)
|
||||||
logger.info(f"[{self.stream_name}] VIP message added to queue.")
|
logger.info(f"[{self.stream_name}] VIP message added to queue.")
|
||||||
else:
|
else:
|
||||||
# 应用普通队列的最大容量限制
|
|
||||||
if self._normal_queue.qsize() >= self.normal_queue_max_size:
|
|
||||||
# 队列已满,简单忽略新消息
|
|
||||||
# 更复杂的逻辑(如替换掉队列中优先级最低的)对于 asyncio.PriorityQueue 来说实现复杂
|
|
||||||
logger.debug(
|
|
||||||
f"[{self.stream_name}] Normal queue is full, ignoring new message from {message.message_info.user_info.user_id}"
|
|
||||||
)
|
|
||||||
return
|
|
||||||
|
|
||||||
await self._normal_queue.put(item)
|
await self._normal_queue.put(item)
|
||||||
|
|
||||||
self._entry_counter += 1
|
self._entry_counter += 1
|
||||||
self._new_message_event.set() # 唤醒处理器
|
self._new_message_event.set() # 唤醒处理器
|
||||||
|
|
||||||
|
def _cleanup_old_normal_messages(self):
|
||||||
|
"""清理普通队列中不在最近N条消息范围内的消息"""
|
||||||
|
if self._normal_queue.empty():
|
||||||
|
return
|
||||||
|
|
||||||
|
# 计算阈值:保留最近 recent_message_keep_count 条消息
|
||||||
|
cutoff_counter = max(0, self._entry_counter - self.recent_message_keep_count)
|
||||||
|
|
||||||
|
# 临时存储需要保留的消息
|
||||||
|
temp_messages = []
|
||||||
|
removed_count = 0
|
||||||
|
|
||||||
|
# 取出所有普通队列中的消息
|
||||||
|
while not self._normal_queue.empty():
|
||||||
|
try:
|
||||||
|
item = self._normal_queue.get_nowait()
|
||||||
|
neg_priority, entry_count, timestamp, message = item
|
||||||
|
|
||||||
|
# 如果消息在最近N条消息范围内,保留它
|
||||||
|
if entry_count >= cutoff_counter:
|
||||||
|
temp_messages.append(item)
|
||||||
|
else:
|
||||||
|
removed_count += 1
|
||||||
|
self._normal_queue.task_done() # 标记被移除的任务为完成
|
||||||
|
|
||||||
|
except asyncio.QueueEmpty:
|
||||||
|
break
|
||||||
|
|
||||||
|
# 将保留的消息重新放入队列
|
||||||
|
for item in temp_messages:
|
||||||
|
self._normal_queue.put_nowait(item)
|
||||||
|
|
||||||
|
if removed_count > 0:
|
||||||
|
logger.info(f"[{self.stream_name}] Cleaned up {removed_count} old normal messages outside recent {self.recent_message_keep_count} range.")
|
||||||
|
|
||||||
async def _message_processor(self):
|
async def _message_processor(self):
|
||||||
"""调度器:优先处理VIP队列,然后处理普通队列。"""
|
"""调度器:优先处理VIP队列,然后处理普通队列。"""
|
||||||
while True:
|
while True:
|
||||||
@@ -280,6 +312,9 @@ class S4UChat:
|
|||||||
await self._new_message_event.wait()
|
await self._new_message_event.wait()
|
||||||
self._new_message_event.clear()
|
self._new_message_event.clear()
|
||||||
|
|
||||||
|
# 清理普通队列中的过旧消息
|
||||||
|
self._cleanup_old_normal_messages()
|
||||||
|
|
||||||
# 优先处理VIP队列
|
# 优先处理VIP队列
|
||||||
if not self._vip_queue.empty():
|
if not self._vip_queue.empty():
|
||||||
neg_priority, entry_count, _, message = self._vip_queue.get_nowait()
|
neg_priority, entry_count, _, message = self._vip_queue.get_nowait()
|
||||||
@@ -335,20 +370,25 @@ class S4UChat:
|
|||||||
await asyncio.sleep(1)
|
await asyncio.sleep(1)
|
||||||
|
|
||||||
async def _generate_and_send(self, message: MessageRecv):
|
async def _generate_and_send(self, message: MessageRecv):
|
||||||
"""为单个消息生成文本和音频回复。整个过程可以被中断。"""
|
"""为单个消息生成文本回复。整个过程可以被中断。"""
|
||||||
self._is_replying = True
|
self._is_replying = True
|
||||||
|
|
||||||
|
await send_loading(self.stream_id, "......")
|
||||||
|
|
||||||
# 视线管理:开始生成回复时切换视线状态
|
# 视线管理:开始生成回复时切换视线状态
|
||||||
chat_watching = watching_manager.get_watching_by_chat_id(self.stream_id)
|
chat_watching = watching_manager.get_watching_by_chat_id(self.stream_id)
|
||||||
await chat_watching.on_reply_start()
|
await chat_watching.on_reply_start()
|
||||||
|
|
||||||
|
# 回复生成实时展示:开始生成
|
||||||
|
user_name = message.message_info.user_info.user_nickname
|
||||||
|
|
||||||
sender_container = MessageSenderContainer(self.chat_stream, message)
|
sender_container = MessageSenderContainer(self.chat_stream, message)
|
||||||
sender_container.start()
|
sender_container.start()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
logger.info(f"[S4U] 开始为消息生成文本和音频流: '{message.processed_plain_text[:30]}...'")
|
logger.info(f"[S4U] 开始为消息生成文本和音频流: '{message.processed_plain_text[:30]}...'")
|
||||||
|
|
||||||
# 1. 逐句生成文本、发送并播放音频
|
# 1. 逐句生成文本、发送
|
||||||
gen = self.gpt.generate_response(message, "")
|
gen = self.gpt.generate_response(message, "")
|
||||||
async for chunk in gen:
|
async for chunk in gen:
|
||||||
# 如果任务被取消,await 会在此处引发 CancelledError
|
# 如果任务被取消,await 会在此处引发 CancelledError
|
||||||
@@ -356,25 +396,25 @@ class S4UChat:
|
|||||||
# a. 发送文本块
|
# a. 发送文本块
|
||||||
await sender_container.add_message(chunk)
|
await sender_container.add_message(chunk)
|
||||||
|
|
||||||
# b. 为该文本块生成并播放音频
|
|
||||||
# if chunk.strip():
|
|
||||||
# audio_data = await self.audio_generator.generate(chunk)
|
|
||||||
# player = MockAudioPlayer(audio_data)
|
|
||||||
# await player.play()
|
|
||||||
|
|
||||||
# 等待所有文本消息发送完成
|
# 等待所有文本消息发送完成
|
||||||
await sender_container.close()
|
await sender_container.close()
|
||||||
await sender_container.join()
|
await sender_container.join()
|
||||||
logger.info(f"[{self.stream_name}] 所有文本和音频块处理完毕。")
|
|
||||||
|
|
||||||
|
logger.info(f"[{self.stream_name}] 所有文本块处理完毕。")
|
||||||
|
|
||||||
except asyncio.CancelledError:
|
except asyncio.CancelledError:
|
||||||
logger.info(f"[{self.stream_name}] 回复流程(文本或音频)被中断。")
|
logger.info(f"[{self.stream_name}] 回复流程(文本)被中断。")
|
||||||
raise # 将取消异常向上传播
|
raise # 将取消异常向上传播
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"[{self.stream_name}] 回复生成过程中出现错误: {e}", exc_info=True)
|
logger.error(f"[{self.stream_name}] 回复生成过程中出现错误: {e}", exc_info=True)
|
||||||
|
# 回复生成实时展示:清空内容(出错时)
|
||||||
finally:
|
finally:
|
||||||
self._is_replying = False
|
self._is_replying = False
|
||||||
|
|
||||||
|
await send_unloading(self.stream_id)
|
||||||
|
|
||||||
# 视线管理:回复结束时切换视线状态
|
# 视线管理:回复结束时切换视线状态
|
||||||
chat_watching = watching_manager.get_watching_by_chat_id(self.stream_id)
|
chat_watching = watching_manager.get_watching_by_chat_id(self.stream_id)
|
||||||
await chat_watching.on_reply_finished()
|
await chat_watching.on_reply_finished()
|
||||||
|
|||||||
Reference in New Issue
Block a user