|
|
@@ -64,6 +64,91 @@ const renderMarkdown = (text) => {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+const splitThinkContent = (text) => {
|
|
|
+ const source = String(text || '')
|
|
|
+ const lower = source.toLowerCase()
|
|
|
+ const thoughts = []
|
|
|
+ const answers = []
|
|
|
+ let cursor = 0
|
|
|
+ let thinking = false
|
|
|
+ while (cursor < source.length) {
|
|
|
+ if (thinking) {
|
|
|
+ const end = lower.indexOf('</think>', cursor)
|
|
|
+ if (end === -1) {
|
|
|
+ thoughts.push(source.slice(cursor))
|
|
|
+ cursor = source.length
|
|
|
+ } else {
|
|
|
+ thoughts.push(source.slice(cursor, end))
|
|
|
+ cursor = end + 8
|
|
|
+ thinking = false
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ const start = lower.indexOf('<think>', cursor)
|
|
|
+ if (start === -1) {
|
|
|
+ answers.push(source.slice(cursor))
|
|
|
+ cursor = source.length
|
|
|
+ } else {
|
|
|
+ answers.push(source.slice(cursor, start))
|
|
|
+ cursor = start + 7
|
|
|
+ thinking = true
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return {
|
|
|
+ thought: thoughts.join('').trim(),
|
|
|
+ answer: answers.join('').trim(),
|
|
|
+ thinking,
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const renderAssistantMessage = (message) => {
|
|
|
+ return <AssistantMessageContent message={message} />
|
|
|
+}
|
|
|
+
|
|
|
+function AssistantMessageContent({ message }) {
|
|
|
+ const parsed = splitThinkContent(message?.content || '')
|
|
|
+ const answer = parsed.answer || (!parsed.thought ? message?.content : '')
|
|
|
+ const thinking = message?.thinking || parsed.thinking
|
|
|
+ const prevThinkingRef = useRef(thinking)
|
|
|
+ const [thinkOpen, setThinkOpen] = useState(thinking)
|
|
|
+
|
|
|
+ useEffect(() => {
|
|
|
+ if (thinking) {
|
|
|
+ setThinkOpen(true)
|
|
|
+ } else if (prevThinkingRef.current) {
|
|
|
+ setThinkOpen(false)
|
|
|
+ }
|
|
|
+ prevThinkingRef.current = thinking
|
|
|
+ }, [thinking])
|
|
|
+
|
|
|
+ return (
|
|
|
+ <>
|
|
|
+ {parsed.thought && (
|
|
|
+ <details
|
|
|
+ className={`execute-msg-think ${thinking ? 'is-thinking' : ''}`}
|
|
|
+ open={thinkOpen}
|
|
|
+ onToggle={e => setThinkOpen(e.currentTarget.open)}
|
|
|
+ >
|
|
|
+ <summary className="execute-msg-think-title">
|
|
|
+ <span>{thinking ? '思考中' : '思考过程'}</span>
|
|
|
+ {/* <span className="execute-msg-think-toggle">展开/收起</span> */}
|
|
|
+ </summary>
|
|
|
+ <div
|
|
|
+ className="markdown-body execute-msg-think-body"
|
|
|
+ dangerouslySetInnerHTML={{ __html: renderMarkdown(parsed.thought) }}
|
|
|
+ />
|
|
|
+ </details>
|
|
|
+ )}
|
|
|
+ {answer && (
|
|
|
+ <div
|
|
|
+ className="markdown-body execute-msg-answer"
|
|
|
+ dangerouslySetInnerHTML={{ __html: renderMarkdown(answer) }}
|
|
|
+ />
|
|
|
+ )}
|
|
|
+ </>
|
|
|
+ )
|
|
|
+}
|
|
|
+
|
|
|
const STREAM_WAITING_TEXT = '内容生成中'
|
|
|
const AUTO_SCROLL_THRESHOLD = 24
|
|
|
|
|
|
@@ -840,7 +925,7 @@ function AgentExecute() {
|
|
|
if (streamId) {
|
|
|
const updateMessages = prev => prev.map(m =>
|
|
|
m.id === streamId
|
|
|
- ? { ...m, content: m.pending ? answer : (m.content || '') + answer, pending: false }
|
|
|
+ ? { ...m, content: m.pending ? answer : (m.content || '') + answer, pending: false, thinking: splitThinkContent(m.pending ? answer : (m.content || '') + answer).thinking }
|
|
|
: m
|
|
|
)
|
|
|
if (viewingHistoryRef.current && activeChatSnapshotRef.current) {
|
|
|
@@ -867,7 +952,7 @@ function AgentExecute() {
|
|
|
if (streamId) {
|
|
|
const updateMessages = prev => prev.map(m =>
|
|
|
m.id === streamId
|
|
|
- ? { ...m, streaming: false, pending: false, metadata: evtMetadata, messageId: evtMessageId }
|
|
|
+ ? { ...m, streaming: false, pending: false, thinking: false, metadata: evtMetadata, messageId: evtMessageId }
|
|
|
: m
|
|
|
)
|
|
|
if (viewingHistoryRef.current && activeChatSnapshotRef.current) {
|
|
|
@@ -918,7 +1003,7 @@ function AgentExecute() {
|
|
|
if (streamId) {
|
|
|
const updateMessages = prev => prev.map(m =>
|
|
|
m.id === streamId
|
|
|
- ? { ...m, content: data?.answer || '' }
|
|
|
+ ? { ...m, content: data?.answer || '', thinking: splitThinkContent(data?.answer || '').thinking }
|
|
|
: m
|
|
|
)
|
|
|
if (viewingHistoryRef.current && activeChatSnapshotRef.current) {
|
|
|
@@ -1560,6 +1645,8 @@ function AgentExecute() {
|
|
|
<div className="execute-msg-bubble">
|
|
|
{m.pending ? (
|
|
|
<span className="execute-msg-generating">{m.content}</span>
|
|
|
+ ) : m.role === 'assistant' ? (
|
|
|
+ renderAssistantMessage(m)
|
|
|
) : (
|
|
|
<div
|
|
|
className="markdown-body"
|