Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 59 additions & 0 deletions crates/goose/src/providers/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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<String, ProviderError> {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

below and possibly above: I have my quest to delete comments that don't help, can you make it so :) ?

// 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<String> = 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)
Comment thread
angelahning marked this conversation as resolved.
Outdated
})
.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
Expand Down
43 changes: 3 additions & 40 deletions crates/goose/src/session/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<String> = 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)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

much better! still not really where it should be called from, but we can clean that up some other time

.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)?
Expand Down
Loading