feat: 添加主题描述生成和更新功能,优化会话信息展示
This commit is contained in:
parent
5e04832f20
commit
0732b31e6b
@ -114,10 +114,21 @@ async fn handle_get_current_session(
|
||||
let last_active = format_time_ago(topic.last_active_at);
|
||||
let created_at = format_time_ago(topic.created_at);
|
||||
|
||||
let description_line = if let Some(ref desc) = topic.description {
|
||||
if !desc.is_empty() {
|
||||
format!("\n Description: {}", desc)
|
||||
} else {
|
||||
String::new()
|
||||
}
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
|
||||
let message = format!(
|
||||
"Current Topic:\n\n Topic ID: {}\n Title: {}\n Messages: {}\n Tokens: ~{} (系统提示词: ~{}, 用户消息: ~{})\n Created: {}\n Last Active: {}",
|
||||
"Current Topic:\n\n Topic ID: {}\n Title: {}{}\n Messages: {}\n Tokens: ~{} (系统提示词: ~{}, 用户消息: ~{})\n Created: {}\n Last Active: {}",
|
||||
topic.id,
|
||||
topic.title,
|
||||
description_line,
|
||||
actual_message_count,
|
||||
total_tokens,
|
||||
system_prompt_tokens,
|
||||
|
||||
@ -80,6 +80,13 @@ async fn handle_list_sessions(
|
||||
"{}. {}{} ({})",
|
||||
num, topic.title, marker, msg_count
|
||||
));
|
||||
|
||||
// 显示描述(如果有)
|
||||
if let Some(ref desc) = topic.description {
|
||||
if !desc.is_empty() {
|
||||
lines.push(format!(" {}", desc));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lines.push(String::new());
|
||||
|
||||
@ -17,8 +17,10 @@ use crate::command::handlers::session::SessionCommandHandler;
|
||||
use crate::command::handlers::switch_session::SwitchSessionCommandHandler;
|
||||
use crate::config::LLMProviderConfig;
|
||||
use crate::gateway::agent_prompt_provider::AgentPromptProvider;
|
||||
use crate::providers::{create_provider, ProviderRuntimeConfig};
|
||||
use crate::skills::SkillPromptProvider;
|
||||
use crate::storage::persistent_session_id;
|
||||
use crate::topic_description::generate_topic_description;
|
||||
|
||||
use super::session::{BusToolCallEmitter, SessionManager};
|
||||
|
||||
@ -27,7 +29,7 @@ pub struct InboundProcessor {
|
||||
bus: Arc<MessageBus>,
|
||||
session_manager: SessionManager,
|
||||
semaphore: Arc<Semaphore>,
|
||||
_provider_config: LLMProviderConfig,
|
||||
provider_config: LLMProviderConfig,
|
||||
command_router: Arc<CommandRouter>,
|
||||
}
|
||||
|
||||
@ -99,7 +101,7 @@ impl InboundProcessor {
|
||||
bus,
|
||||
session_manager,
|
||||
semaphore,
|
||||
_provider_config: provider_config,
|
||||
provider_config,
|
||||
command_router: Arc::new(command_router),
|
||||
}
|
||||
}
|
||||
@ -243,6 +245,37 @@ impl InboundProcessor {
|
||||
tracing::error!(error = %error, "Failed to publish outbound");
|
||||
}
|
||||
}
|
||||
|
||||
// 异步生成 topic 描述(仅第一条消息后触发一次)
|
||||
if let Some(ref topic_id) = current_topic {
|
||||
let store = self.session_manager.store();
|
||||
if let Ok(Some(topic)) = store.get_topic(topic_id) {
|
||||
if topic.description.is_none() || topic.description.as_ref().map(|d| d.is_empty()).unwrap_or(true) {
|
||||
let provider_config = self.provider_config.clone();
|
||||
let topic_id_clone = topic_id.clone();
|
||||
let first_message = inbound.content.clone();
|
||||
let store_clone = store.clone();
|
||||
|
||||
tokio::spawn(async move {
|
||||
let runtime_config: ProviderRuntimeConfig = provider_config.into();
|
||||
if let Ok(provider) = create_provider(runtime_config) {
|
||||
match generate_topic_description(provider.as_ref(), &first_message).await {
|
||||
Ok(description) => {
|
||||
if let Err(e) = store_clone.update_topic_description(&topic_id_clone, &description) {
|
||||
tracing::error!(error = %e, topic_id = %topic_id_clone, "Failed to update topic description");
|
||||
} else {
|
||||
tracing::info!(topic_id = %topic_id_clone, description = %description, "Topic description generated");
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::error!(error = %e, topic_id = %topic_id_clone, "Failed to generate topic description");
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(error) => {
|
||||
tracing::error!(error = %error, "Failed to handle message");
|
||||
|
||||
@ -18,4 +18,5 @@ pub mod scheduler;
|
||||
pub mod skills;
|
||||
pub mod storage;
|
||||
pub mod text;
|
||||
pub mod topic_description;
|
||||
pub mod tools;
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
use crate::domain::messages::{ContentBlock, ToolCall};
|
||||
use crate::domain::tools::Tool;
|
||||
use crate::config::LLMProviderConfig;
|
||||
use async_trait::async_trait;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
@ -18,6 +19,23 @@ pub struct ProviderRuntimeConfig {
|
||||
pub model_extra: HashMap<String, serde_json::Value>,
|
||||
}
|
||||
|
||||
impl From<LLMProviderConfig> for ProviderRuntimeConfig {
|
||||
fn from(config: LLMProviderConfig) -> Self {
|
||||
Self {
|
||||
provider_type: config.provider_type,
|
||||
name: config.name,
|
||||
base_url: config.base_url,
|
||||
api_key: config.api_key,
|
||||
extra_headers: config.extra_headers,
|
||||
llm_timeout_secs: config.llm_timeout_secs,
|
||||
model_id: config.model_id,
|
||||
temperature: config.temperature,
|
||||
max_tokens: config.max_tokens,
|
||||
model_extra: config.model_extra,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Message {
|
||||
pub role: String,
|
||||
|
||||
@ -462,6 +462,16 @@ impl SessionStore {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn update_topic_description(&self, topic_id: &str, description: &str) -> Result<(), StorageError> {
|
||||
let now = current_timestamp();
|
||||
let conn = self.conn.lock().expect("session db mutex poisoned");
|
||||
conn.execute(
|
||||
"UPDATE topics SET description = ?2, updated_at = ?3 WHERE id = ?1",
|
||||
params![topic_id, description, now],
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn delete_topic(&self, topic_id: &str) -> Result<(), StorageError> {
|
||||
let conn = self.conn.lock().expect("session db mutex poisoned");
|
||||
// Messages 的 topic_id 会被设为 NULL(ON DELETE SET NULL)
|
||||
|
||||
27
src/topic_description.rs
Normal file
27
src/topic_description.rs
Normal file
@ -0,0 +1,27 @@
|
||||
use crate::providers::{ChatCompletionRequest, LLMProvider, Message};
|
||||
|
||||
pub async fn generate_topic_description(
|
||||
provider: &dyn LLMProvider,
|
||||
first_user_message: &str,
|
||||
) -> Result<String, Box<dyn std::error::Error + Send + Sync>> {
|
||||
let prompt = format!(
|
||||
"请根据用户的第一句话,用简短的词语(不超过15字)描述这个对话的主题或意图。只输出描述内容,不要其他解释。\n\n用户消息:{}",
|
||||
first_user_message
|
||||
);
|
||||
|
||||
let request = ChatCompletionRequest {
|
||||
messages: vec![Message::user(prompt)],
|
||||
temperature: Some(0.3),
|
||||
max_tokens: Some(50),
|
||||
tools: None,
|
||||
};
|
||||
|
||||
let response = provider.chat(request).await?;
|
||||
let description = response.content.trim();
|
||||
|
||||
if description.len() > 50 {
|
||||
Ok(description.chars().take(50).collect())
|
||||
} else {
|
||||
Ok(description.to_string())
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user