diff --git a/README.md b/README.md index f982257..77403b2 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,10 @@ npm run dev # http://localhost:5173 如需切换: ```bash -VITE_API_TARGET=http://localhost:4000 npm run dev +VITE_API_TARGET=http://localhost:4001 npm run dev + +# 或添加本地环境变量 .env.local +VITE_API_TARGET=http://localhost:4001 ``` ## 构建 diff --git a/src/pages/AgentEditor/components/capability/KnowledgeSettingsPanel.tsx b/src/pages/AgentEditor/components/capability/KnowledgeSettingsPanel.tsx index 5675b16..fb75138 100644 --- a/src/pages/AgentEditor/components/capability/KnowledgeSettingsPanel.tsx +++ b/src/pages/AgentEditor/components/capability/KnowledgeSettingsPanel.tsx @@ -47,10 +47,10 @@ export default function KnowledgeSettingsPanel({
上传文档以增强 AI 知识 - { const files = event.target.files; @@ -95,8 +95,11 @@ export default function KnowledgeSettingsPanel({ 索引中… ) : ( - - {STATUS_TAG[item.status || 'ready'].text} + + {STATUS_TAG[item.status as keyof typeof STATUS_TAG]?.text || item.status || '未知'} )} diff --git a/src/pages/chat/components/ChatBody.tsx b/src/pages/chat/components/ChatBody.tsx index 00a8b00..76f453d 100644 --- a/src/pages/chat/components/ChatBody.tsx +++ b/src/pages/chat/components/ChatBody.tsx @@ -1,10 +1,11 @@ import { Divider, Tag, Avatar } from 'antd'; +import { useState, useEffect } from 'react'; import type { Agent, BranchInfo, ChatMessage } from '../../../api'; import Markdown from '../../../components/Markdown'; import type { StreamingState } from '../hooks/useChatSender'; import type { CopyMode } from '../utils/copy'; import MessageItem from './messages/MessageItem'; -import { RetrievedView, ToolCallView } from './messages/MetaViews'; +import { ReasoningView, RetrievedView, ToolCallView } from './messages/MetaViews'; import './ChatBody.css'; const isImageUrl = (url: string) => url?.startsWith('http') || url?.startsWith('/'); @@ -26,6 +27,15 @@ export default function ChatBody(props: { }) { const { bodyRef, agent, agentList, currentAgentId, messages, branches, highlightId, sending, streaming, onRegenerate, onSwitchBranch, onCopy, isMobile } = props; + const [streamingReasoningExpanded, setStreamingReasoningExpanded] = useState(true); + + // 当开始新的回答时,默认展开推理过程 + useEffect(() => { + if (streaming.active && !streaming.answerText) { + setStreamingReasoningExpanded(true); + } + }, [streaming.active, !!streaming.answerText]); + return (
@@ -100,10 +110,13 @@ export default function ChatBody(props: {
)}
-
推理过程
- {streaming.reasoningText ? {streaming.reasoningText + '▍'} : 等待推理…} + setStreamingReasoningExpanded(!streamingReasoningExpanded)} + />
- + {streamingReasoningExpanded && }
正式回答
{streaming.answerText ? {streaming.answerText + '▍'} : 等待输出…} diff --git a/src/pages/chat/components/ChatPageWeb.tsx b/src/pages/chat/components/ChatPageWeb.tsx index f7063d1..1a57816 100644 --- a/src/pages/chat/components/ChatPageWeb.tsx +++ b/src/pages/chat/components/ChatPageWeb.tsx @@ -55,54 +55,8 @@ export default function ChatPageWeb({ logic }: { logic: ChatPageLogicOutput }) { } }, [messages.length]); - // 2. 监听滚动,自动更新大纲激活态 - useEffect(() => { - const bodyEl = bodyRef.current; - if (!bodyEl) return; - - const handleScroll = () => { - if (isAutoScrolling.current) return; - - const messageEls = bodyEl.querySelectorAll('.message-item-container[data-is-agent="true"]'); - if (messageEls.length === 0) return; - - // 检查是否滚动到底部 - const isAtBottom = bodyEl.scrollHeight - bodyEl.scrollTop <= bodyEl.clientHeight + 100; - if (isAtBottom) { - const agentMsgs = messages.filter(m => m.role === 'assistant' || m.role === 'agent' || m.speaker?.type === 'agent'); - const lastAgentMsgId = agentMsgs[agentMsgs.length - 1]?.id; - if (lastAgentMsgId && lastAgentMsgId !== highlightId) { - setHighlightId(lastAgentMsgId); - } - return; - } - - let currentActiveId = highlightId; - const containerRect = bodyEl.getBoundingClientRect(); - - // 找到当前视口中占据主要位置的 Agent 消息 - for (let i = 0; i < messageEls.length; i++) { - const el = messageEls[i] as HTMLElement; - const rect = el.getBoundingClientRect(); - - // 判定准则:消息头部进入视口顶部感应区 - if (rect.top >= containerRect.top - 20 && rect.top <= containerRect.top + 150) { - const idAttr = el.getAttribute('data-msg-id'); - if (idAttr) { - currentActiveId = idAttr; - break; - } - } - } - - if (currentActiveId && currentActiveId !== highlightId) { - setHighlightId(currentActiveId); - } - }; - - bodyEl.addEventListener('scroll', handleScroll); - return () => bodyEl.removeEventListener('scroll', handleScroll); - }, [messages, highlightId]); + // 2. 移除滚动监听逻辑,避免干扰正常滚动 + // 后续若需恢复,需重新设计非阻塞的判定算法 const handleJump = useCallback((msgId: string) => { isAutoScrolling.current = true; diff --git a/src/pages/chat/components/messages/MessageItem.css b/src/pages/chat/components/messages/MessageItem.css index 65fec0c..374450f 100644 --- a/src/pages/chat/components/messages/MessageItem.css +++ b/src/pages/chat/components/messages/MessageItem.css @@ -111,3 +111,45 @@ .bubble.assistant .markdown p { color: var(--color-text); } + +.message-reasoning-section { + display: flex; + flex-direction: column; +} + +.reasoning-container { + margin: 4px 0; + transition: all 0.3s ease; +} + +.reasoning-header { + padding: 4px 0; + border-radius: 4px; + transition: background-color 0.2s; +} + +.reasoning-header:hover { + background-color: var(--color-surface-2); +} + +.reasoning-content { + margin-top: 8px; + animation: reasoningFadeIn 0.3s ease-out; +} + +@keyframes reasoningFadeIn { + from { + opacity: 0; + transform: translateY(-4px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +.message-section-label { + font-size: 12px; + color: var(--color-text-tertiary); + margin-bottom: 6px; +} diff --git a/src/pages/chat/components/messages/MessageItem.tsx b/src/pages/chat/components/messages/MessageItem.tsx index fde8031..c7da77e 100644 --- a/src/pages/chat/components/messages/MessageItem.tsx +++ b/src/pages/chat/components/messages/MessageItem.tsx @@ -1,5 +1,5 @@ -import { Button, Dropdown, Space, Tag, Tooltip, Avatar } from 'antd'; -import { useMemo } from 'react'; +import { Button, Dropdown, Space, Tag, Tooltip, Avatar, Divider } from 'antd'; +import { useMemo, useState } from 'react'; import { CopyOutlined, SyncOutlined } from '@ant-design/icons'; import dayjs from 'dayjs'; import type { BranchInfo, ChatMessage } from '../../../../api'; @@ -24,6 +24,8 @@ export default function MessageItem(props: { }) { const { message, agentList, currentAgentId, highlighted, branch, busy, onRegenerate, onSwitchBranch, onCopy, isMobile } = props; + const [reasoningExpanded, setReasoningExpanded] = useState(false); + const formattedContent = useMemo(() => formatMessageContent(message.content), [message.content]); const speakerType = (message as any)?.speaker?.type as ('user' | 'agent' | undefined); @@ -85,7 +87,20 @@ export default function MessageItem(props: { __html: formattedContent.replace(/@([^\s]+)/g, '@$1') }} /> ) : ( - {formattedContent} +
+ {/* 如果有推理过程且不是用户消息,展示推理部分 */} + {!isUser && message.meta?.reasoning && ( +
+ setReasoningExpanded(!reasoningExpanded)} + /> + {reasoningExpanded && } +
+ )} + {formattedContent} +
)}
@@ -124,13 +139,14 @@ export default function MessageItem(props: { )}
- {/* {!isMobile && message.meta && !isUser && ( + {/* 展示 RAG 和 工具调用 */} + {!isMobile && message.meta && !isUser && (
{!!message.meta.reasoning && } {!!message.meta.retrieved?.length && } {!!message.meta.toolCalls?.length && }
- )} */} + )}
diff --git a/src/pages/chat/components/messages/MetaViews.tsx b/src/pages/chat/components/messages/MetaViews.tsx index 6e1e63e..9efdfb3 100644 --- a/src/pages/chat/components/messages/MetaViews.tsx +++ b/src/pages/chat/components/messages/MetaViews.tsx @@ -1,24 +1,52 @@ import { Collapse, Tag } from 'antd'; +import { RightOutlined } from '@ant-design/icons'; import type { RetrievedSnippet, ToolCallTrace } from '../../../../api'; import Markdown from '../../../../components/Markdown'; -export function ReasoningView({ reasoning }: { reasoning: string }) { +export function ReasoningView({ + reasoning, + expanded, + onToggle +}: { + reasoning: string; + expanded?: boolean; + onToggle?: () => void; +}) { return ( - 🧠 推理过程, - children: ( -
- {reasoning} -
- ) - } - ]} - /> +
+
+ + 已深度思考 + + +
+ {expanded && ( +
+ {reasoning} +
+ )} +
); }