|
|
|
|
@ -47,6 +47,13 @@ function ChatConversation() {
|
|
|
|
|
const [typingMessage, setTypingMessage] = useState('')
|
|
|
|
|
const [isTyping, setIsTyping] = useState(false)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const [streamingContent, setStreamingContent] = useState('')
|
|
|
|
|
const [isStreaming, setIsStreaming] = useState(false)
|
|
|
|
|
const [currentStreamingMessageId, setCurrentStreamingMessageId] = useState(null)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 流式输出状态 - 已移除
|
|
|
|
|
|
|
|
|
|
const messagesEndRef = useRef(null)
|
|
|
|
|
@ -127,6 +134,41 @@ function ChatConversation() {
|
|
|
|
|
typeNextWords()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 模拟流式效果
|
|
|
|
|
const simulateStreamingEffect = (fullContent, messageId) => {
|
|
|
|
|
console.log('开始流式效果:', { fullContent: fullContent.substring(0, 100) + '...', messageId, length: fullContent.length })
|
|
|
|
|
setIsStreaming(true)
|
|
|
|
|
setStreamingContent('')
|
|
|
|
|
setCurrentStreamingMessageId(messageId)
|
|
|
|
|
|
|
|
|
|
let currentIndex = 0
|
|
|
|
|
const chunkSize = 3 // 每次显示3个字符
|
|
|
|
|
const delay = 50 // 50ms间隔,可以调整速度
|
|
|
|
|
|
|
|
|
|
const streamNext = () => {
|
|
|
|
|
if (currentIndex < fullContent.length) {
|
|
|
|
|
const nextIndex = Math.min(currentIndex + chunkSize, fullContent.length)
|
|
|
|
|
const currentContent = fullContent.substring(0, nextIndex)
|
|
|
|
|
|
|
|
|
|
console.log('流式进度:', { currentIndex, nextIndex, currentContent: currentContent.substring(0, 50) + '...' })
|
|
|
|
|
|
|
|
|
|
setStreamingContent(currentContent)
|
|
|
|
|
conversationStore.updateMessage(messageId, currentContent)
|
|
|
|
|
|
|
|
|
|
currentIndex = nextIndex
|
|
|
|
|
setTimeout(streamNext, delay)
|
|
|
|
|
} else {
|
|
|
|
|
// 流式效果完成
|
|
|
|
|
console.log('流式效果完成')
|
|
|
|
|
setIsStreaming(false)
|
|
|
|
|
setStreamingContent('')
|
|
|
|
|
setCurrentStreamingMessageId(null)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
streamNext()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 创建新对话
|
|
|
|
|
const createNewConversation = () => {
|
|
|
|
|
@ -183,7 +225,7 @@ function ChatConversation() {
|
|
|
|
|
setLoading(true)
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
// 调用本地后端接口(非流式版本)
|
|
|
|
|
// 调用本地后端接口
|
|
|
|
|
const result = await callLocalChatAPI(messageText)
|
|
|
|
|
console.log('API result:', result)
|
|
|
|
|
|
|
|
|
|
@ -200,9 +242,12 @@ function ChatConversation() {
|
|
|
|
|
console.log('Final content:', finalContent)
|
|
|
|
|
|
|
|
|
|
if (finalContent.trim()) {
|
|
|
|
|
// 暂时直接添加消息,不使用打字机效果
|
|
|
|
|
console.log('Adding message directly:', finalContent)
|
|
|
|
|
conversationStore.addMessage('assistant', finalContent)
|
|
|
|
|
// 先添加一个空的助手消息
|
|
|
|
|
const assistantMessageId = conversationStore.addMessage('assistant', '')
|
|
|
|
|
setCurrentStreamingMessageId(assistantMessageId)
|
|
|
|
|
|
|
|
|
|
// 开始模拟流式效果
|
|
|
|
|
simulateStreamingEffect(finalContent, assistantMessageId)
|
|
|
|
|
} else {
|
|
|
|
|
console.log('No content, adding fallback message')
|
|
|
|
|
conversationStore.addMessage('assistant', '抱歉,我无法生成回复内容。')
|
|
|
|
|
@ -270,14 +315,22 @@ function ChatConversation() {
|
|
|
|
|
// 渲染单条消息 ==================================
|
|
|
|
|
const renderMessage = (msg, index) => {
|
|
|
|
|
const isUser = msg.role === 'user'
|
|
|
|
|
const displayContent = msg.content || ''
|
|
|
|
|
|
|
|
|
|
// 如果是当前正在流式输出的消息,显示流式内容
|
|
|
|
|
const isCurrentStreaming = isStreaming &&
|
|
|
|
|
currentStreamingMessageId === msg.id &&
|
|
|
|
|
msg.role === 'assistant'
|
|
|
|
|
|
|
|
|
|
const displayContent = isCurrentStreaming ? streamingContent : (msg.content || '')
|
|
|
|
|
|
|
|
|
|
// 调试信息
|
|
|
|
|
if (msg.role === 'assistant') {
|
|
|
|
|
console.log('Rendering assistant message:', {
|
|
|
|
|
index,
|
|
|
|
|
content: msg.content,
|
|
|
|
|
displayContent
|
|
|
|
|
displayContent,
|
|
|
|
|
isCurrentStreaming,
|
|
|
|
|
streamingContent
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -346,11 +399,67 @@ function ChatConversation() {
|
|
|
|
|
{/* 回答框 */}
|
|
|
|
|
<div className='ds-message-content'>
|
|
|
|
|
{displayContent ? (
|
|
|
|
|
<ReactMarkdown
|
|
|
|
|
remarkPlugins={[remarkGfm]}
|
|
|
|
|
>
|
|
|
|
|
{displayContent}
|
|
|
|
|
</ReactMarkdown>
|
|
|
|
|
<div>
|
|
|
|
|
{deepThinkingEnabled && displayContent.includes('\n\n') ? (
|
|
|
|
|
// 当开启深度思考且有思考内容时,分别渲染
|
|
|
|
|
(() => {
|
|
|
|
|
const parts = displayContent.split('\n\n')
|
|
|
|
|
const thought = parts[0]
|
|
|
|
|
const message = parts.slice(1).join('\n\n')
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<>
|
|
|
|
|
{/* 思考过程 */}
|
|
|
|
|
<div
|
|
|
|
|
style={{
|
|
|
|
|
fontSize: '14px',
|
|
|
|
|
color: '#666',
|
|
|
|
|
padding: '5px',
|
|
|
|
|
backgroundColor: '#f5f5f5',
|
|
|
|
|
borderRadius: '4px',
|
|
|
|
|
marginBottom: '10px',
|
|
|
|
|
// fontStyle: 'italic',
|
|
|
|
|
borderLeft: '3px solid #d9d9d9'
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<strong>思考过程:</strong>
|
|
|
|
|
<ReactMarkdown
|
|
|
|
|
remarkPlugins={[remarkGfm]}
|
|
|
|
|
components={{
|
|
|
|
|
p: ({children}) => <span style={{fontSize: '14px'}}>{children}</span>,
|
|
|
|
|
strong: ({children}) => <strong style={{fontSize: '14px'}}>{children}</strong>,
|
|
|
|
|
em: ({children}) => <em style={{fontSize: '14px'}}>{children}</em>,
|
|
|
|
|
code: ({children}) => <code style={{fontSize: '14px'}}>{children}</code>,
|
|
|
|
|
pre: ({children}) => <pre style={{fontSize: '14px'}}>{children}</pre>
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
{thought}
|
|
|
|
|
</ReactMarkdown>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* 正式回答 */}
|
|
|
|
|
{message && (
|
|
|
|
|
<div>
|
|
|
|
|
{/* <strong style={{fontSize: '12px', color: '#333', marginBottom: '4px', display: 'block'}}>回答:</strong> */}
|
|
|
|
|
<ReactMarkdown
|
|
|
|
|
remarkPlugins={[remarkGfm]}
|
|
|
|
|
>
|
|
|
|
|
{message}
|
|
|
|
|
</ReactMarkdown>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</>
|
|
|
|
|
)
|
|
|
|
|
})()
|
|
|
|
|
) : (
|
|
|
|
|
// 普通渲染
|
|
|
|
|
<ReactMarkdown
|
|
|
|
|
remarkPlugins={[remarkGfm]}
|
|
|
|
|
>
|
|
|
|
|
{displayContent}
|
|
|
|
|
</ReactMarkdown>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
) : (
|
|
|
|
|
<div style={{ color: '#999', fontStyle: 'italic' }}>
|
|
|
|
|
暂无内容
|
|
|
|
|
|