Session manager (#4648)
Co-authored-by: Douwe Osinga <douwe@squareup.com>
This commit is contained in:
+69
-19
@@ -18,8 +18,8 @@ use crate::commands::schedule::{
|
||||
use crate::commands::session::{handle_session_list, handle_session_remove};
|
||||
use crate::recipes::extract_from_cli::extract_recipe_info_from_cli;
|
||||
use crate::recipes::recipe::{explain_recipe, render_recipe_as_yaml};
|
||||
use crate::session;
|
||||
use crate::session::{build_session, SessionBuilderConfig, SessionSettings};
|
||||
use goose::session::SessionManager;
|
||||
use goose_bench::bench_config::BenchRunConfig;
|
||||
use goose_bench::runners::bench_runner::BenchRunner;
|
||||
use goose_bench::runners::eval_runner::EvalRunner;
|
||||
@@ -48,26 +48,45 @@ struct Identifier {
|
||||
)]
|
||||
name: Option<String>,
|
||||
|
||||
#[arg(
|
||||
long = "session-id",
|
||||
value_name = "SESSION_ID",
|
||||
help = "Session ID (e.g., '20250921_143022')",
|
||||
long_help = "Specify a session ID directly. When used with --resume, will resume this specific session if it exists."
|
||||
)]
|
||||
session_id: Option<String>,
|
||||
|
||||
#[arg(
|
||||
short,
|
||||
long,
|
||||
value_name = "PATH",
|
||||
help = "Path for the chat session (e.g., './playground.jsonl')",
|
||||
long_help = "Specify a path for your chat session. When used with --resume, will resume this specific session if it exists."
|
||||
help = "Legacy: Path for the chat session",
|
||||
long_help = "Legacy parameter for backward compatibility. Extracts session ID from the file path (e.g., '/path/to/20250325_200615.
|
||||
jsonl' -> '20250325_200615')."
|
||||
)]
|
||||
path: Option<PathBuf>,
|
||||
}
|
||||
|
||||
fn extract_identifier(identifier: Identifier) -> session::Identifier {
|
||||
if let Some(name) = identifier.name {
|
||||
session::Identifier::Name(name)
|
||||
async fn get_session_id(identifier: Identifier) -> Result<String> {
|
||||
if let Some(session_id) = identifier.session_id {
|
||||
Ok(session_id)
|
||||
} else if let Some(name) = identifier.name {
|
||||
let sessions = SessionManager::list_sessions().await?;
|
||||
|
||||
sessions
|
||||
.into_iter()
|
||||
.find(|s| s.description == name)
|
||||
.map(|s| s.id)
|
||||
.ok_or_else(|| anyhow::anyhow!("No session found with name '{}'", name))
|
||||
} else if let Some(path) = identifier.path {
|
||||
session::Identifier::Path(path)
|
||||
path.file_stem()
|
||||
.and_then(|s| s.to_str())
|
||||
.map(|s| s.to_string())
|
||||
.ok_or_else(|| anyhow::anyhow!("Could not extract session ID from path: {:?}", path))
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_key_val(s: &str) -> Result<(String, String), String> {
|
||||
match s.split_once('=') {
|
||||
Some((key, value)) => Ok((key.to_string(), value.to_string())),
|
||||
@@ -121,6 +140,14 @@ enum SessionCommand {
|
||||
long_help = "Path to save the exported Markdown. If not provided, output will be sent to stdout"
|
||||
)]
|
||||
output: Option<PathBuf>,
|
||||
|
||||
#[arg(
|
||||
long = "format",
|
||||
value_name = "FORMAT",
|
||||
help = "Output format (markdown, json, yaml)",
|
||||
default_value = "markdown"
|
||||
)]
|
||||
format: String,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -768,19 +795,24 @@ pub async fn cli() -> Result<()> {
|
||||
format,
|
||||
ascending,
|
||||
}) => {
|
||||
handle_session_list(verbose, format, ascending)?;
|
||||
handle_session_list(verbose, format, ascending).await?;
|
||||
Ok(())
|
||||
}
|
||||
Some(SessionCommand::Remove { id, regex }) => {
|
||||
handle_session_remove(id, regex)?;
|
||||
handle_session_remove(id, regex).await?;
|
||||
return Ok(());
|
||||
}
|
||||
Some(SessionCommand::Export { identifier, output }) => {
|
||||
Some(SessionCommand::Export {
|
||||
identifier,
|
||||
output,
|
||||
format,
|
||||
}) => {
|
||||
let session_identifier = if let Some(id) = identifier {
|
||||
extract_identifier(id)
|
||||
get_session_id(id).await?
|
||||
} else {
|
||||
// If no identifier is provided, prompt for interactive selection
|
||||
match crate::commands::session::prompt_interactive_session_selection() {
|
||||
match crate::commands::session::prompt_interactive_session_selection().await
|
||||
{
|
||||
Ok(id) => id,
|
||||
Err(e) => {
|
||||
eprintln!("Error: {}", e);
|
||||
@@ -789,7 +821,12 @@ pub async fn cli() -> Result<()> {
|
||||
}
|
||||
};
|
||||
|
||||
crate::commands::session::handle_session_export(session_identifier, output)?;
|
||||
crate::commands::session::handle_session_export(
|
||||
session_identifier,
|
||||
output,
|
||||
format,
|
||||
)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
None => {
|
||||
@@ -803,9 +840,15 @@ pub async fn cli() -> Result<()> {
|
||||
"Session started"
|
||||
);
|
||||
|
||||
let session_id = if let Some(id) = identifier {
|
||||
Some(get_session_id(id).await?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// Run session command by default
|
||||
let mut session: crate::Session = build_session(SessionBuilderConfig {
|
||||
identifier: identifier.map(extract_identifier),
|
||||
let mut session: crate::CliSession = build_session(SessionBuilderConfig {
|
||||
session_id,
|
||||
resume,
|
||||
no_session: false,
|
||||
extensions,
|
||||
@@ -841,6 +884,7 @@ pub async fn cli() -> Result<()> {
|
||||
|
||||
let (total_tokens, message_count) = session
|
||||
.get_metadata()
|
||||
.await
|
||||
.map(|m| (m.total_tokens.unwrap_or(0), m.message_count))
|
||||
.unwrap_or((0, 0));
|
||||
|
||||
@@ -994,9 +1038,14 @@ pub async fn cli() -> Result<()> {
|
||||
std::process::exit(1);
|
||||
}
|
||||
};
|
||||
let session_id = if let Some(id) = identifier {
|
||||
Some(get_session_id(id).await?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let mut session = build_session(SessionBuilderConfig {
|
||||
identifier: identifier.map(extract_identifier),
|
||||
session_id,
|
||||
resume,
|
||||
no_session,
|
||||
extensions,
|
||||
@@ -1048,6 +1097,7 @@ pub async fn cli() -> Result<()> {
|
||||
|
||||
let (total_tokens, message_count) = session
|
||||
.get_metadata()
|
||||
.await
|
||||
.map(|m| (m.total_tokens.unwrap_or(0), m.message_count))
|
||||
.unwrap_or((0, 0));
|
||||
|
||||
@@ -1172,7 +1222,7 @@ pub async fn cli() -> Result<()> {
|
||||
} else {
|
||||
// Run session command by default
|
||||
let mut session = build_session(SessionBuilderConfig {
|
||||
identifier: None,
|
||||
session_id: None,
|
||||
resume: false,
|
||||
no_session: false,
|
||||
extensions: Vec::new(),
|
||||
@@ -1188,7 +1238,7 @@ pub async fn cli() -> Result<()> {
|
||||
max_tool_repetitions: None,
|
||||
max_turns: None,
|
||||
scheduled_job_id: None,
|
||||
interactive: true, // Default case is always interactive
|
||||
interactive: true,
|
||||
quiet: false,
|
||||
sub_recipes: None,
|
||||
final_output_response: None,
|
||||
|
||||
Reference in New Issue
Block a user