/* Copyright (C) 2025 QuantumNous This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . For commercial licensing, please contact support@quantumnous.com */ import React, { useRef, useEffect } from 'react'; import { Typography, TextArea, Button } from '@douyinfe/semi-ui'; import MarkdownRenderer from '../common/markdown/MarkdownRenderer'; import ThinkingContent from './ThinkingContent'; import { Loader2, Check, X } from 'lucide-react'; import { useTranslation } from 'react-i18next'; const MessageContent = ({ message, className, styleState, onToggleReasoningExpansion, isEditing = false, onEditSave, onEditCancel, editValue, onEditValueChange, }) => { const { t } = useTranslation(); const previousContentLengthRef = useRef(0); const lastContentRef = useRef(''); const isThinkingStatus = message.status === 'loading' || message.status === 'incomplete'; useEffect(() => { if (!isThinkingStatus) { previousContentLengthRef.current = 0; lastContentRef.current = ''; } }, [isThinkingStatus]); if (message.status === 'error') { let errorText; if (Array.isArray(message.content)) { const textContent = message.content.find((item) => item.type === 'text'); errorText = textContent && textContent.text && typeof textContent.text === 'string' ? textContent.text : t('请求发生错误'); } else if (typeof message.content === 'string') { errorText = message.content; } else { errorText = t('请求发生错误'); } return (
{errorText}
); } let currentExtractedThinkingContent = null; let currentDisplayableFinalContent = ''; let thinkingSource = null; const getTextContent = (content) => { if (Array.isArray(content)) { const textItem = content.find((item) => item.type === 'text'); return textItem && textItem.text && typeof textItem.text === 'string' ? textItem.text : ''; } else if (typeof content === 'string') { return content; } return ''; }; currentDisplayableFinalContent = getTextContent(message.content); if (message.role === 'assistant') { let baseContentForDisplay = getTextContent(message.content); let combinedThinkingContent = ''; if (message.reasoningContent) { combinedThinkingContent = message.reasoningContent; thinkingSource = 'reasoningContent'; } if (baseContentForDisplay.includes('')) { const thinkTagRegex = /([\s\S]*?)<\/think>/g; let match; let thoughtsFromPairedTags = []; let replyParts = []; let lastIndex = 0; while ((match = thinkTagRegex.exec(baseContentForDisplay)) !== null) { replyParts.push( baseContentForDisplay.substring(lastIndex, match.index), ); thoughtsFromPairedTags.push(match[1]); lastIndex = match.index + match[0].length; } replyParts.push(baseContentForDisplay.substring(lastIndex)); if (thoughtsFromPairedTags.length > 0) { const pairedThoughtsStr = thoughtsFromPairedTags.join('\n\n---\n\n'); if (combinedThinkingContent) { combinedThinkingContent += '\n\n---\n\n' + pairedThoughtsStr; } else { combinedThinkingContent = pairedThoughtsStr; } thinkingSource = thinkingSource ? thinkingSource + ' & tags' : ' tags'; } baseContentForDisplay = replyParts.join(''); } if (isThinkingStatus) { const lastOpenThinkIndex = baseContentForDisplay.lastIndexOf(''); if (lastOpenThinkIndex !== -1) { const fragmentAfterLastOpen = baseContentForDisplay.substring(lastOpenThinkIndex); if (!fragmentAfterLastOpen.includes('')) { const unclosedThought = fragmentAfterLastOpen .substring(''.length) .trim(); if (unclosedThought) { if (combinedThinkingContent) { combinedThinkingContent += '\n\n---\n\n' + unclosedThought; } else { combinedThinkingContent = unclosedThought; } thinkingSource = thinkingSource ? thinkingSource + ' + streaming ' : 'streaming '; } baseContentForDisplay = baseContentForDisplay.substring( 0, lastOpenThinkIndex, ); } } } currentExtractedThinkingContent = combinedThinkingContent || null; currentDisplayableFinalContent = baseContentForDisplay .replace(/<\/?think>/g, '') .trim(); } const finalExtractedThinkingContent = currentExtractedThinkingContent; const finalDisplayableFinalContent = currentDisplayableFinalContent; if ( message.role === 'assistant' && isThinkingStatus && !finalExtractedThinkingContent && (!finalDisplayableFinalContent || finalDisplayableFinalContent.trim() === '') ) { return (
); } return (
{message.role === 'system' && (
S
{t('系统消息')}
)} {message.role === 'assistant' && ( )} {isEditing ? (