From 877819197bc172bfad79044ca24fc9e4f2c8d84b Mon Sep 17 00:00:00 2001 From: Jarrod Sibbison Date: Fri, 19 Sep 2025 13:52:10 +1000 Subject: [PATCH] fix: Only metric a recipe run from ui on the first /reply call --- crates/goose-server/src/routes/reply.rs | 29 +++++++++++++++---------- crates/goose-server/src/state.rs | 15 ++++++++++++- 2 files changed, 32 insertions(+), 12 deletions(-) diff --git a/crates/goose-server/src/routes/reply.rs b/crates/goose-server/src/routes/reply.rs index 880f6746796d..68db8328c2ab 100644 --- a/crates/goose-server/src/routes/reply.rs +++ b/crates/goose-server/src/routes/reply.rs @@ -178,17 +178,24 @@ async fn reply_handler( "Session started" ); - if let Some(recipe_name) = &request.recipe_name { - let recipe_version = request.recipe_version.as_deref().unwrap_or("unknown"); - - tracing::info!( - counter.goose.recipe_runs = 1, - recipe_name = %recipe_name, - recipe_version = %recipe_version, - session_type = "app", - interface = "ui", - "Recipe execution started" - ); + if let (Some(recipe_name), Some(session_id)) = + (request.recipe_name.clone(), request.session_id.clone()) + { + if state.mark_recipe_run_if_absent(&session_id).await { + let recipe_version = request + .recipe_version + .clone() + .unwrap_or_else(|| "unknown".to_string()); + + tracing::info!( + counter.goose.recipe_runs = 1, + recipe_name = %recipe_name, + recipe_version = %recipe_version, + session_type = "app", + interface = "ui", + "Recipe execution started" + ); + } } let (tx, rx) = mpsc::channel(100); diff --git a/crates/goose-server/src/state.rs b/crates/goose-server/src/state.rs index a70cd47296b5..542745d05646 100644 --- a/crates/goose-server/src/state.rs +++ b/crates/goose-server/src/state.rs @@ -1,6 +1,6 @@ use goose::agents::Agent; use goose::scheduler_trait::SchedulerTrait; -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use std::path::PathBuf; use std::sync::atomic::AtomicUsize; use std::sync::Arc; @@ -15,6 +15,8 @@ pub struct AppState { pub scheduler: Arc>>>, pub recipe_file_hash_map: Arc>>, pub session_counter: Arc, + /// Tracks sessions that have already emitted recipe telemetry to prevent double counting. + recipe_session_tracker: Arc>>, } impl AppState { @@ -24,6 +26,7 @@ impl AppState { scheduler: Arc::new(RwLock::new(None)), recipe_file_hash_map: Arc::new(Mutex::new(HashMap::new())), session_counter: Arc::new(AtomicUsize::new(0)), + recipe_session_tracker: Arc::new(Mutex::new(HashSet::new())), }) } @@ -53,4 +56,14 @@ impl AppState { let mut agent = self.agent.write().await; *agent = Arc::new(Agent::new()); } + + pub async fn mark_recipe_run_if_absent(&self, session_id: &str) -> bool { + let mut sessions = self.recipe_session_tracker.lock().await; + if sessions.contains(session_id) { + false + } else { + sessions.insert(session_id.to_string()); + true + } + } }