Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
133 changes: 102 additions & 31 deletions crates/goose-cli/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ use crate::commands::schedule::{
handle_schedule_sessions,
};
use crate::commands::session::{handle_session_list, handle_session_remove};
use crate::logging::setup_logging;
use crate::recipes::extract_from_cli::extract_recipe_info_from_cli;
use crate::recipes::recipe::{explain_recipe, render_recipe_as_yaml};
use crate::session;
Expand Down Expand Up @@ -718,7 +717,7 @@ pub async fn cli() -> Result<()> {
};

tracing::info!(
monotonic_counter.goose.cli_commands = 1,
counter.goose.cli_commands = 1,
command = command_name,
"CLI command executed"
);
Expand Down Expand Up @@ -779,6 +778,16 @@ pub async fn cli() -> Result<()> {
Ok(())
}
None => {
let session_start = std::time::Instant::now();
let session_type = if resume { "resumed" } else { "new" };

tracing::info!(
counter.goose.session_starts = 1,
session_type,
interactive = true,
"Session started"
);

// Run session command by default
let mut session: crate::Session = build_session(SessionBuilderConfig {
identifier: identifier.map(extract_identifier),
Expand All @@ -804,21 +813,46 @@ pub async fn cli() -> Result<()> {
retry_config: None,
})
.await;
setup_logging(
session
.session_file()
.as_ref()
.and_then(|p| p.file_stem())
.and_then(|s| s.to_str()),
None,
)?;

// Render previous messages if resuming a session and history flag is set
if resume && history {
session.render_message_history();
}

let _ = session.interactive(None).await;
let result = session.interactive(None).await;

let session_duration = session_start.elapsed();
let exit_type = if result.is_ok() { "normal" } else { "error" };

let (total_tokens, message_count) = session
.get_metadata()
.map(|m| (m.total_tokens.unwrap_or(0), m.message_count))
.unwrap_or((0, 0));

tracing::info!(
counter.goose.session_completions = 1,
session_type,
exit_type,
duration_ms = session_duration.as_millis() as u64,
total_tokens,
message_count,
"Session completed"
);

tracing::info!(
counter.goose.session_duration_ms = session_duration.as_millis() as u64,
session_type,
"Session duration"
);

if total_tokens > 0 {
tracing::info!(
counter.goose.session_tokens = total_tokens,
session_type,
"Session tokens"
);
}

Ok(())
}
};
Expand Down Expand Up @@ -897,8 +931,13 @@ pub async fn cli() -> Result<()> {
(input_config, None)
}
(_, _, Some(recipe_name)) => {
tracing::info!(monotonic_counter.goose.recipe_runs = 1,
recipe_name = %recipe_name,
let recipe_display_name = std::path::Path::new(&recipe_name)
.file_name()
.and_then(|name| name.to_str())
.unwrap_or(&recipe_name);

tracing::info!(counter.goose.recipe_runs = 1,
recipe_name = %recipe_display_name,
"Recipe execution started"
);

Expand Down Expand Up @@ -952,19 +991,59 @@ pub async fn cli() -> Result<()> {
})
.await;

setup_logging(
session
.session_file()
.as_ref()
.and_then(|p| p.file_stem())
.and_then(|s| s.to_str()),
None,
)?;

if interactive {
let _ = session.interactive(input_config.contents).await;
} else if let Some(contents) = input_config.contents {
let _ = session.headless(contents).await;
let session_start = std::time::Instant::now();
let session_type = if recipe_info.is_some() {
"recipe"
} else {
"run"
};

tracing::info!(
counter.goose.session_starts = 1,
session_type,
interactive = false,
"Headless session started"
);

let result = session.headless(contents).await;

let session_duration = session_start.elapsed();
let exit_type = if result.is_ok() { "normal" } else { "error" };

let (total_tokens, message_count) = session
.get_metadata()
.map(|m| (m.total_tokens.unwrap_or(0), m.message_count))
.unwrap_or((0, 0));

tracing::info!(
counter.goose.session_completions = 1,
session_type,
exit_type,
duration_ms = session_duration.as_millis() as u64,
total_tokens,
message_count,
interactive = false,
"Headless session completed"
);

tracing::info!(
counter.goose.session_duration_ms = session_duration.as_millis() as u64,
session_type,
"Headless session duration"
);

if total_tokens > 0 {
tracing::info!(
counter.goose.session_tokens = total_tokens,
session_type,
"Headless session tokens"
);
}

result?;
} else {
eprintln!("Error: no text provided for prompt in headless mode");
std::process::exit(1);
Expand Down Expand Up @@ -1083,14 +1162,6 @@ pub async fn cli() -> Result<()> {
retry_config: None,
})
.await;
setup_logging(
session
.session_file()
.as_ref()
.and_then(|p| p.file_stem())
.and_then(|s| s.to_str()),
None,
)?;
if let Err(e) = session.interactive(None).await {
eprintln!("Session ended with error: {}", e);
}
Expand Down
29 changes: 28 additions & 1 deletion crates/goose-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,32 @@ use goose_cli::cli::cli;

#[tokio::main]
async fn main() -> Result<()> {
cli().await
if let Err(e) = goose_cli::logging::setup_logging(None, None) {
eprintln!("Warning: Failed to initialize telemetry: {}", e);
}

let result = cli().await;

// Only wait for telemetry flush if OTLP is configured
if std::env::var("OTEL_EXPORTER_OTLP_ENDPOINT").is_ok() {
// Use a shorter, dynamic wait with max timeout
let max_wait = tokio::time::Duration::from_millis(500);
let start = tokio::time::Instant::now();

// Give telemetry a chance to flush, but don't wait too long
while start.elapsed() < max_wait {
tokio::time::sleep(tokio::time::Duration::from_millis(50)).await;

// In future, we could check if there are pending spans/metrics here
// For now, we just do a quick wait to allow batch exports to complete
if start.elapsed() >= tokio::time::Duration::from_millis(200) {
break; // Most exports should complete within 200ms
}
}

// Then shutdown the providers
goose::tracing::shutdown_otlp();
}

result
}
40 changes: 37 additions & 3 deletions crates/goose-cli/src/session/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -851,7 +851,8 @@ impl Session {
pub async fn headless(&mut self, prompt: String) -> Result<()> {
let message = Message::user().with_text(&prompt);
self.process_message(message, CancellationToken::default())
.await
.await?;
Ok(())
}

async fn process_agent_response(
Expand Down Expand Up @@ -1023,12 +1024,45 @@ impl Session {
for content in &message.content {
if let MessageContent::ToolRequest(tool_request) = content {
if let Ok(tool_call) = &tool_request.tool_call {
tracing::info!(monotonic_counter.goose.tool_calls = 1,
tracing::info!(counter.goose.tool_calls = 1,
tool_name = %tool_call.name,
"Tool call executed"
"Tool call started"
);
}
}
if let MessageContent::ToolResponse(tool_response) = content {
let tool_name = self.messages
.iter()
.rev()
.find_map(|msg| {
msg.content.iter().find_map(|c| {
if let MessageContent::ToolRequest(req) = c {
if req.id == tool_response.id {
if let Ok(tool_call) = &req.tool_call {
Some(tool_call.name.clone())
} else {
None
}
} else {
None
}
} else {
None
}
})
})
.unwrap_or_else(|| "unknown".to_string());

let success = tool_response.tool_result.is_ok();
let result_status = if success { "success" } else { "error" };

tracing::info!(
counter.goose.tool_completions = 1,
tool_name = %tool_name,
result = %result_status,
"Tool call completed"
);
}
}

push_message(&mut self.messages, message.clone());
Expand Down
2 changes: 1 addition & 1 deletion crates/goose-server/src/commands/agent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use tracing::info;
use goose::providers::pricing::initialize_pricing_cache;

pub async fn run() -> Result<()> {
// Initialize logging
// Initialize logging and telemetry
crate::logging::setup_logging(Some("goosed"))?;

let settings = configuration::Settings::new()?;
Expand Down
Loading
Loading