From 598d425c288c3555b3a8b431ce03f02922f9c74c Mon Sep 17 00:00:00 2001 From: oudecheng <13802883547@139.com> Date: Thu, 28 May 2026 14:30:21 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E8=AF=9D=E9=A2=98=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E6=8F=8F=E8=BF=B0=E5=AD=97=E6=AE=B5=EF=BC=8C=E5=B7=A5=E5=85=B7?= =?UTF-8?q?=E6=B6=88=E6=81=AF=E6=8C=89=20tool=5Fcall=5Fid=20=E5=90=88?= =?UTF-8?q?=E5=B9=B6=E5=B1=95=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - TopicSummary 新增 description 字段,侧边栏优先显示描述 - ToolPanel 使用 toolCallId 将 tool_call 和 tool_result 配对合并展示 - 保存消息时同步更新 topics 表的 message_count 和 last_active_at - ChatMessage 新增 toolCallId 字段 Co-Authored-By: Claude Opus 4.7 --- src/command/handlers/list_topics.rs | 2 + src/command/handlers/session.rs | 1 + src/protocol/mod.rs | 2 + src/storage/mod.rs | 7 +++ web/src/components/Panel/ToolPanel.tsx | 65 +++++++++++++++++------- web/src/components/Sidebar/TopicList.tsx | 2 +- web/src/hooks/useChat.ts | 4 ++ web/src/types/protocol.ts | 2 + 8 files changed, 66 insertions(+), 19 deletions(-) diff --git a/src/command/handlers/list_topics.rs b/src/command/handlers/list_topics.rs index aaf681b..29088e8 100644 --- a/src/command/handlers/list_topics.rs +++ b/src/command/handlers/list_topics.rs @@ -13,6 +13,7 @@ pub struct TopicSummary { pub topic_id: String, pub session_id: String, pub title: String, + pub description: Option, pub message_count: i64, pub created_at: i64, pub last_active_at: i64, @@ -73,6 +74,7 @@ async fn handle_list_topics( topic_id: t.id, session_id: t.session_id, title: t.title, + description: t.description.filter(|d| !d.is_empty()), message_count: t.message_count, created_at: t.created_at, last_active_at: t.last_active_at, diff --git a/src/command/handlers/session.rs b/src/command/handlers/session.rs index 9872b12..fd5704f 100644 --- a/src/command/handlers/session.rs +++ b/src/command/handlers/session.rs @@ -107,6 +107,7 @@ async fn handle_create_session( topic_id: t.id, session_id: t.session_id, title: t.title, + description: t.description.filter(|d| !d.is_empty()), message_count: t.message_count, created_at: t.created_at, last_active_at: t.last_active_at, diff --git a/src/protocol/mod.rs b/src/protocol/mod.rs index 975ee61..918f4f0 100644 --- a/src/protocol/mod.rs +++ b/src/protocol/mod.rs @@ -32,6 +32,8 @@ pub struct TopicSummary { pub message_count: i64, pub created_at: i64, pub last_active_at: i64, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub description: Option, } #[derive(Debug, Clone, Serialize, Deserialize)] diff --git a/src/storage/mod.rs b/src/storage/mod.rs index 2927ac7..bb8fee6 100644 --- a/src/storage/mod.rs +++ b/src/storage/mod.rs @@ -579,6 +579,13 @@ impl SessionStore { params![session_id, now, if is_user_message { 1 } else { 0 }], )?; + if let Some(tid) = topic_id { + tx.execute( + "UPDATE topics SET message_count = message_count + 1, last_active_at = ?2 WHERE id = ?1", + params![tid, now], + )?; + } + tx.commit()?; Ok(()) } diff --git a/web/src/components/Panel/ToolPanel.tsx b/web/src/components/Panel/ToolPanel.tsx index 66325eb..104804f 100644 --- a/web/src/components/Panel/ToolPanel.tsx +++ b/web/src/components/Panel/ToolPanel.tsx @@ -1,5 +1,5 @@ import { ChevronDown, ChevronRight, Play, Check, AlertTriangle, Terminal } from 'lucide-react' -import { useState } from 'react' +import { useState, useMemo } from 'react' import type { ChatMessage } from '../../types/protocol' interface ToolPanelProps { @@ -7,25 +7,54 @@ interface ToolPanelProps { } interface ToolCallItem { - id: string + toolCallId: string toolName: string status: 'calling' | 'result' | 'pending' arguments?: unknown - content: string + resultContent: string + callContent: string +} + +function mergeToolMessages(messages: ChatMessage[]): ToolCallItem[] { + const map = new Map() + + for (const m of messages) { + if (m.role !== 'tool' || !m.type?.startsWith('tool_')) continue + + const key = m.toolCallId || m.id + let entry = map.get(key) + + if (!entry) { + entry = { + toolCallId: key, + toolName: m.toolName || 'Unknown', + status: 'calling', + arguments: undefined, + resultContent: '', + callContent: '', + } + map.set(key, entry) + } + + if (m.type === 'tool_call') { + entry.arguments = m.arguments + entry.callContent = m.content + } else if (m.type === 'tool_result') { + entry.status = 'result' + entry.resultContent = m.content + } else if (m.type === 'tool_pending') { + entry.status = 'pending' + entry.resultContent = m.content + } + } + + return Array.from(map.values()) } export function ToolPanel({ messages }: ToolPanelProps) { const [expandedTools, setExpandedTools] = useState>(new Set()) - const toolCalls: ToolCallItem[] = messages - .filter((m) => m.role === 'tool' && m.type && m.type.startsWith('tool_')) - .map((m) => ({ - id: m.id, - toolName: m.toolName || 'Unknown', - status: m.type === 'tool_call' ? 'calling' : m.type === 'tool_result' ? 'result' : 'pending', - arguments: m.arguments, - content: m.content, - })) + const toolCalls = useMemo(() => mergeToolMessages(messages), [messages]) const toggleExpand = (id: string) => { setExpandedTools((prev) => { @@ -42,7 +71,7 @@ export function ToolPanel({ messages }: ToolPanelProps) { const getStatusIcon = (status: ToolCallItem['status']) => { switch (status) { case 'calling': - return + return case 'result': return case 'pending': @@ -91,11 +120,11 @@ export function ToolPanel({ messages }: ToolPanelProps) {
{toolCalls.map((tool) => (
- {expandedTools.has(tool.id) && ( + {expandedTools.has(tool.toolCallId) && (
{tool.arguments ? (
@@ -122,7 +151,7 @@ export function ToolPanel({ messages }: ToolPanelProps) {
结果:
- {tool.content} + {tool.resultContent || tool.callContent}
diff --git a/web/src/components/Sidebar/TopicList.tsx b/web/src/components/Sidebar/TopicList.tsx index b9297d0..0514948 100644 --- a/web/src/components/Sidebar/TopicList.tsx +++ b/web/src/components/Sidebar/TopicList.tsx @@ -102,7 +102,7 @@ export function TopicList({
- {topic.title} + {topic.description || topic.title}
diff --git a/web/src/hooks/useChat.ts b/web/src/hooks/useChat.ts index c04fdb4..2a7c0a2 100644 --- a/web/src/hooks/useChat.ts +++ b/web/src/hooks/useChat.ts @@ -125,6 +125,7 @@ export function useChat(): UseChatReturn { id: t.topic_id, session_id: t.session_id, title: t.title, + description: t.description || undefined, message_count: Number(t.message_count), created_at: t.created_at, updated_at: t.last_active_at, @@ -165,6 +166,7 @@ export function useChat(): UseChatReturn { timestamp: Date.now(), type: 'tool_call', toolName: msg.tool_name, + toolCallId: msg.tool_call_id, arguments: msg.arguments, }, ]) @@ -182,6 +184,7 @@ export function useChat(): UseChatReturn { timestamp: Date.now(), type: 'tool_result', toolName: msg.tool_name, + toolCallId: msg.tool_call_id, }, ]) break @@ -198,6 +201,7 @@ export function useChat(): UseChatReturn { timestamp: Date.now(), type: 'tool_pending', toolName: msg.tool_name, + toolCallId: msg.tool_call_id, }, ]) break diff --git a/web/src/types/protocol.ts b/web/src/types/protocol.ts index d68df1f..d1c735c 100644 --- a/web/src/types/protocol.ts +++ b/web/src/types/protocol.ts @@ -123,6 +123,7 @@ export interface TopicSummary { topic_id: string session_id: string title: string + description?: string message_count: number created_at: number last_active_at: number @@ -249,6 +250,7 @@ export interface ChatMessage { timestamp: number type?: 'message' | 'tool_call' | 'tool_result' | 'tool_pending' toolName?: string + toolCallId?: string arguments?: unknown attachments?: Attachment[] }