- TopicSummary 新增 description 字段,侧边栏优先显示描述 - ToolPanel 使用 toolCallId 将 tool_call 和 tool_result 配对合并展示 - 保存消息时同步更新 topics 表的 message_count 和 last_active_at - ChatMessage 新增 toolCallId 字段 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
99 lines
2.8 KiB
Rust
99 lines
2.8 KiB
Rust
use crate::command::context::CommandContext;
|
|
use crate::command::handler::{CommandHandler, CommandMetadata};
|
|
use crate::command::response::{CommandError, CommandResponse, MessageKind};
|
|
use crate::command::Command;
|
|
use crate::storage::SessionStore;
|
|
use async_trait::async_trait;
|
|
use serde::{Deserialize, Serialize};
|
|
use std::sync::Arc;
|
|
|
|
/// Topic 摘要信息
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct TopicSummary {
|
|
pub topic_id: String,
|
|
pub session_id: String,
|
|
pub title: String,
|
|
pub description: Option<String>,
|
|
pub message_count: i64,
|
|
pub created_at: i64,
|
|
pub last_active_at: i64,
|
|
}
|
|
|
|
/// 列出 Session 的 Topics 命令处理器
|
|
pub struct ListTopicsCommandHandler {
|
|
store: Arc<SessionStore>,
|
|
}
|
|
|
|
impl ListTopicsCommandHandler {
|
|
pub fn new(store: Arc<SessionStore>) -> Self {
|
|
Self { store }
|
|
}
|
|
}
|
|
|
|
#[async_trait]
|
|
impl CommandHandler for ListTopicsCommandHandler {
|
|
fn can_handle(&self, cmd: &Command) -> bool {
|
|
matches!(cmd, Command::ListTopics { .. })
|
|
}
|
|
|
|
fn metadata(&self) -> Option<CommandMetadata> {
|
|
Some(CommandMetadata {
|
|
name: "topics",
|
|
description: "列出 Session 的所有 Topics",
|
|
usage: "/topics <session_id>",
|
|
})
|
|
}
|
|
|
|
async fn handle(
|
|
&self,
|
|
cmd: Command,
|
|
ctx: CommandContext,
|
|
) -> Result<CommandResponse, CommandError> {
|
|
match cmd {
|
|
Command::ListTopics { session_id } => {
|
|
handle_list_topics(self, session_id, ctx).await
|
|
}
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
}
|
|
|
|
async fn handle_list_topics(
|
|
handler: &ListTopicsCommandHandler,
|
|
session_id: String,
|
|
ctx: CommandContext,
|
|
) -> Result<CommandResponse, CommandError> {
|
|
let topics = handler
|
|
.store
|
|
.list_topics(&session_id)
|
|
.map_err(|e| CommandError::new("LIST_TOPICS_ERROR", e.to_string()))?;
|
|
|
|
let summaries: Vec<TopicSummary> = topics
|
|
.into_iter()
|
|
.map(|t| TopicSummary {
|
|
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,
|
|
})
|
|
.collect();
|
|
|
|
let topics_json = serde_json::to_string(&summaries)
|
|
.map_err(|e| CommandError::new("SERIALIZE_ERROR", e.to_string()))?;
|
|
|
|
let message = format!(
|
|
"Found {} topic(s) in session '{}'",
|
|
summaries.len(),
|
|
session_id
|
|
);
|
|
|
|
Ok(CommandResponse::success(ctx.request_id)
|
|
.with_message(MessageKind::Notification, &message)
|
|
.with_metadata("topics", &topics_json)
|
|
.with_metadata("session_id", &session_id)
|
|
.with_metadata("count", &summaries.len().to_string()))
|
|
}
|