feat: add webchat Word-by-word output

fix:webchat on message stream bug
This commit is contained in:
Dong_master
2025-08-02 01:42:22 +08:00
parent 0ce81a2df2
commit 52280d7a05
2 changed files with 84 additions and 32 deletions
@@ -42,9 +42,24 @@ export default function DebugDialog({
const inputRef = useRef<HTMLInputElement>(null); const inputRef = useRef<HTMLInputElement>(null);
const popoverRef = useRef<HTMLDivElement>(null); const popoverRef = useRef<HTMLDivElement>(null);
const scrollToBottom = () => { // const scrollToBottom = () => {
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); // messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
}; // };
const scrollToBottom = useCallback(() => {
// 使用setTimeout确保在DOM更新后执行滚动
setTimeout(() => {
const scrollArea = document.querySelector('.scroll-area') as HTMLElement;
if (scrollArea) {
scrollArea.scrollTo({
top: scrollArea.scrollHeight,
behavior: 'smooth',
});
}
// 同时确保messagesEndRef也滚动到视图
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
}, 0);
}, []);
const loadMessages = useCallback( const loadMessages = useCallback(
async (pipelineId: string) => { async (pipelineId: string) => {
@@ -60,10 +75,10 @@ export default function DebugDialog({
}, },
[sessionType], [sessionType],
); );
// 在useEffect中监听messages变化时滚动
useEffect(() => { useEffect(() => {
scrollToBottom(); scrollToBottom();
}, [messages]); }, [messages, scrollToBottom]);
useEffect(() => { useEffect(() => {
if (open) { if (open) {
@@ -175,7 +190,7 @@ export default function DebugDialog({
const botMessage: Message = { const botMessage: Message = {
id: -1, id: -1,
role: 'assistant', role: 'assistant',
content: '', content: '生成中...',
timestamp: new Date().toISOString(), timestamp: new Date().toISOString(),
message_chain: [{ type: 'Plain', text: '' }], message_chain: [{ type: 'Plain', text: '' }],
}; };
@@ -191,8 +206,9 @@ export default function DebugDialog({
setHasAt(false); setHasAt(false);
try { try {
const botMessageId = botMessage.id; let fullContent = ''; // 保存完整内容
let accumulatedContent = ''; let displayContent = ''; // 当前显示内容
let typingInterval: NodeJS.Timeout;
await httpClient.sendStreamingWebChatMessage( await httpClient.sendStreamingWebChatMessage(
sessionType, sessionType,
@@ -201,40 +217,76 @@ export default function DebugDialog({
(data) => { (data) => {
// 处理流式响应数据 // 处理流式响应数据
if (data.message) { if (data.message) {
accumulatedContent += data.message.content; // 更新完整内容
fullContent = data.message.content;
// 更新bot消息 // 清除之前的打字效果
setMessages((prevMessages) => { if (typingInterval) {
const updatedMessages = [...prevMessages]; clearInterval(typingInterval);
// const botMessageIndex = updatedMessages.findIndex( }
// (msg) =>
// msg.id === botMessageId && msg.role === 'assistant',
// );
// 使用索引来更新消息,而不是id匹配
const botMessageIndex = updatedMessages.length - 1;
if (botMessageIndex !== -1) { // 开始新的打字效果
const updatedBotMessage = { let currentPos = displayContent.length;
...updatedMessages[botMessageIndex], const targetContent = fullContent;
content: accumulatedContent,
message_chain: [ typingInterval = setInterval(() => {
{ type: 'Plain', text: accumulatedContent }, if (currentPos < targetContent.length) {
], displayContent = targetContent.substring(0, currentPos + 1);
}; currentPos++;
updatedMessages[botMessageIndex] = updatedBotMessage;
// 更新bot消息
setMessages((prevMessages) => {
const updatedMessages = [...prevMessages];
const botMessageIndex = updatedMessages.length - 1;
if (botMessageIndex !== -1) {
const updatedBotMessage = {
...updatedMessages[botMessageIndex],
content: displayContent,
message_chain: [
{ type: 'Plain', text: displayContent },
],
};
updatedMessages[botMessageIndex] = updatedBotMessage;
}
setTimeout(scrollToBottom, 0); // 确保在状态更新后滚动
return updatedMessages;
});
} else {
clearInterval(typingInterval);
} }
}, 30); // 调整这个值可以改变打字速度
return updatedMessages;
});
} }
}, },
() => { () => {
// 流传输完成 // 流传输完成
console.log('Streaming completed'); console.log('Streaming completed');
if (typingInterval) {
clearInterval(typingInterval);
}
// 确保最终内容完全显示
setMessages((prevMessages) => {
const updatedMessages = [...prevMessages];
const botMessageIndex = updatedMessages.length - 1;
if (botMessageIndex !== -1) {
const updatedBotMessage = {
...updatedMessages[botMessageIndex],
content: fullContent,
message_chain: [{ type: 'Plain', text: fullContent }],
};
updatedMessages[botMessageIndex] = updatedBotMessage;
}
setTimeout(scrollToBottom, 0); // 确保在状态更新后滚动
return updatedMessages;
});
}, },
(error) => { (error) => {
// 处理错误 // 处理错误
console.error('Streaming error:', error); console.error('Streaming error:', error);
if (typingInterval) {
clearInterval(typingInterval);
}
if (sessionType === 'person') { if (sessionType === 'person') {
toast.error(t('pipelines.debugDialog.sendFailed')); toast.error(t('pipelines.debugDialog.sendFailed'));
} }
+2 -2
View File
@@ -380,12 +380,12 @@ class HttpClient {
// 处理完整的JSON对象 // 处理完整的JSON对象
const lines = buffer.split('\n\n'); const lines = buffer.split('\n\n');
buffer = ''; buffer = lines.pop() || '';
for (const line of lines) { for (const line of lines) {
if (line.startsWith('data:')) { if (line.startsWith('data:')) {
try { try {
const data = JSON.parse(line.slice(6)); const data = JSON.parse(line.slice(5));
if (data.type === 'end') { if (data.type === 'end') {
// 流传输结束 // 流传输结束