From 9d2a4aa294b8dddccb8e4fa35b42bcead74a8045 Mon Sep 17 00:00:00 2001 From: Angela Ning Date: Sun, 27 Jul 2025 14:00:17 -0400 Subject: [PATCH 1/2] chore: refactor session naming into provider --- crates/goose/src/providers/base.rs | 59 +++++++++++++++++++++++++++++ crates/goose/src/session/storage.rs | 43 ++------------------- 2 files changed, 62 insertions(+), 40 deletions(-) diff --git a/crates/goose/src/providers/base.rs b/crates/goose/src/providers/base.rs index 9525486821b9..1474181ba5f4 100644 --- a/crates/goose/src/providers/base.rs +++ b/crates/goose/src/providers/base.rs @@ -5,6 +5,7 @@ use serde::{Deserialize, Serialize}; use super::errors::ProviderError; use crate::message::Message; use crate::model::ModelConfig; +use crate::utils::safe_truncate; use rmcp::model::Tool; use utoipa::ToSchema; @@ -338,6 +339,64 @@ pub trait Provider: Send + Sync { self.get_model_config().model_name } } + + /// Generate a session name/description based on the conversation history + /// + /// This method can be overridden by providers to implement custom session naming strategies. + /// The default implementation creates a prompt asking for a concise description in 4 words or less. + /// + /// # Arguments + /// * `messages` - The conversation history as a sequence of messages + /// + /// # Returns + /// A string containing the session name/description, sanitized and truncated to be safe for use + /// + /// # Errors + /// ProviderError if the session name generation fails + async fn generate_session_name(&self, messages: &[Message]) -> Result { + // Create a special message asking for a concise description + let mut description_prompt = "Based on the conversation so far, provide a concise description of this session in 4 words or less. This will be used for finding the session later in a UI with limited space - reply *ONLY* with the description".to_string(); + + // Get context from messages so far, limiting each message to 300 chars for security + let context: Vec = messages + .iter() + .filter(|m| m.role == rmcp::model::Role::User) + .take(3) // Use up to first 3 user messages for context + .map(|m| { + let text = m.as_concat_text(); + safe_truncate(&text, 300) + }) + .collect(); + + if !context.is_empty() { + description_prompt = format!( + "Here are the first few user messages:\n{}\n\n{}", + context.join("\n"), + description_prompt + ); + } + + // Generate the description + let message = Message::user().with_text(&description_prompt); + let result = self + .complete( + "Reply with only a description in four words or less", + &[message], + &[], + ) + .await?; + + let description = result.0.as_concat_text(); + + // Validate description length for security and usability + let sanitized_description = if description.chars().count() > 100 { + safe_truncate(&description, 100) + } else { + description + }; + + Ok(sanitized_description) + } } /// A message stream yields partial text content but complete tool calls, all within the Message object diff --git a/crates/goose/src/session/storage.rs b/crates/goose/src/session/storage.rs index 507e3872423a..5d50d1d83437 100644 --- a/crates/goose/src/session/storage.rs +++ b/crates/goose/src/session/storage.rs @@ -1294,52 +1294,15 @@ pub async fn generate_description_with_schedule_id( )); } - // Create a special message asking for a 3-word description - let mut description_prompt = "Based on the conversation so far, provide a concise description of this session in 4 words or less. This will be used for finding the session later in a UI with limited space - reply *ONLY* with the description".to_string(); - - // get context from messages so far, limiting each message to 300 chars for security - let context: Vec = messages - .iter() - .filter(|m| m.role == rmcp::model::Role::User) - .take(3) // Use up to first 3 user messages for context - .map(|m| { - let text = m.as_concat_text(); - safe_truncate(&text, 300) - }) - .collect(); - - if !context.is_empty() { - description_prompt = format!( - "Here are the first few user messages:\n{}\n\n{}", - context.join("\n"), - description_prompt - ); - } - - // Generate the description with error handling - let message = Message::user().with_text(&description_prompt); - let result = provider - .complete( - "Reply with only a description in four words or less", - &[message], - &[], - ) + // Use the provider's session naming capability + let sanitized_description = provider + .generate_session_name(messages) .await .map_err(|e| { tracing::error!("Failed to generate session description: {}", e); anyhow::anyhow!("Failed to generate session description") })?; - let description = result.0.as_concat_text(); - - // Validate description length for security - let sanitized_description = if description.chars().count() > 100 { - tracing::warn!("Generated description too long, truncating"); - safe_truncate(&description, 100) - } else { - description - }; - // Create metadata with proper working_dir or read existing and update let mut metadata = if secure_path.exists() { read_metadata(&secure_path)? From 7611bbee738bd44bf8ec0615527fa17c3ff72c90 Mon Sep 17 00:00:00 2001 From: Angela Ning Date: Mon, 28 Jul 2025 14:32:43 -0400 Subject: [PATCH 2/2] remove comments and truncation --- crates/goose/src/providers/base.rs | 24 ++++-------------------- 1 file changed, 4 insertions(+), 20 deletions(-) diff --git a/crates/goose/src/providers/base.rs b/crates/goose/src/providers/base.rs index 1474181ba5f4..ea108e3fb9f0 100644 --- a/crates/goose/src/providers/base.rs +++ b/crates/goose/src/providers/base.rs @@ -341,31 +341,18 @@ pub trait Provider: Send + Sync { } /// Generate a session name/description based on the conversation history - /// /// This method can be overridden by providers to implement custom session naming strategies. /// The default implementation creates a prompt asking for a concise description in 4 words or less. - /// - /// # Arguments - /// * `messages` - The conversation history as a sequence of messages - /// - /// # Returns - /// A string containing the session name/description, sanitized and truncated to be safe for use - /// - /// # Errors - /// ProviderError if the session name generation fails async fn generate_session_name(&self, messages: &[Message]) -> Result { - // Create a special message asking for a concise description + // Create a prompt for a concise description let mut description_prompt = "Based on the conversation so far, provide a concise description of this session in 4 words or less. This will be used for finding the session later in a UI with limited space - reply *ONLY* with the description".to_string(); - // Get context from messages so far, limiting each message to 300 chars for security + // Get context from the first 3 user messages let context: Vec = messages .iter() .filter(|m| m.role == rmcp::model::Role::User) - .take(3) // Use up to first 3 user messages for context - .map(|m| { - let text = m.as_concat_text(); - safe_truncate(&text, 300) - }) + .take(3) + .map(|m| m.as_concat_text()) .collect(); if !context.is_empty() { @@ -376,7 +363,6 @@ pub trait Provider: Send + Sync { ); } - // Generate the description let message = Message::user().with_text(&description_prompt); let result = self .complete( @@ -387,8 +373,6 @@ pub trait Provider: Send + Sync { .await?; let description = result.0.as_concat_text(); - - // Validate description length for security and usability let sanitized_description = if description.chars().count() > 100 { safe_truncate(&description, 100) } else {