diff --git a/crates/agent_ui/src/acp/thread_view/active_thread.rs b/crates/agent_ui/src/acp/thread_view/active_thread.rs index c30692066017c59757cf7ac312fae1e4ba409078..b7655c51fad0ba5821dcb154cd166dc93b8f959c 100644 --- a/crates/agent_ui/src/acp/thread_view/active_thread.rs +++ b/crates/agent_ui/src/acp/thread_view/active_thread.rs @@ -1,3 +1,4 @@ +use cloud_api_types::SubmitAgentThreadFeedbackBody; use gpui::{Corner, List}; use language_model::LanguageModelEffortLevel; use settings::update_settings_file; @@ -23,6 +24,11 @@ impl ThreadFeedbackState { return; }; + let project = thread.read(cx).project().read(cx); + let client = project.client(); + let user_store = project.user_store(); + let organization = user_store.read(cx).current_organization(); + if self.feedback == Some(feedback) { return; } @@ -45,13 +51,18 @@ impl ThreadFeedbackState { }; cx.background_spawn(async move { let thread = task.await?; - telemetry::event!( - "Agent Thread Rated", - agent = agent_telemetry_id, - session_id = session_id, - rating = rating, - thread = thread - ); + + client + .cloud_client() + .submit_agent_feedback(SubmitAgentThreadFeedbackBody { + organization_id: organization.map(|organization| organization.id.clone()), + agent: agent_telemetry_id.to_string(), + session_id: session_id.to_string(), + rating: rating.to_string(), + thread, + }) + .await?; + anyhow::Ok(()) }) .detach_and_log_err(cx); diff --git a/crates/client/src/user.rs b/crates/client/src/user.rs index 156739b1ab1f0ecedac1a4579f27b799d1911196..6cb38e7da99fb37940ab4ccd15da5d7a0413e0e7 100644 --- a/crates/client/src/user.rs +++ b/crates/client/src/user.rs @@ -670,6 +670,10 @@ impl UserStore { self.current_user.borrow().clone() } + pub fn current_organization(&self) -> Option> { + self.current_organization.clone() + } + pub fn plan(&self) -> Option { #[cfg(debug_assertions)] if let Ok(plan) = std::env::var("ZED_SIMULATE_PLAN").as_ref() { diff --git a/crates/cloud_api_client/src/cloud_api_client.rs b/crates/cloud_api_client/src/cloud_api_client.rs index 9206e5e7efe51e99e4d57b708f09c682283612ed..52ed5970b4319d74d8ecb990d48d52a2f87e4b78 100644 --- a/crates/cloud_api_client/src/cloud_api_client.rs +++ b/crates/cloud_api_client/src/cloud_api_client.rs @@ -181,6 +181,31 @@ impl CloudApiClient { } } } + + pub async fn submit_agent_feedback(&self, body: SubmitAgentThreadFeedbackBody) -> Result<()> { + let request = self.build_request( + Request::builder().method(Method::POST).uri( + self.http_client + .build_zed_cloud_url("/client/feedback/agent_thread")? + .as_ref(), + ), + AsyncBody::from(serde_json::to_string(&body)?), + )?; + + let mut response = self.http_client.send(request).await?; + + if !response.status().is_success() { + let mut body = String::new(); + response.body_mut().read_to_string(&mut body).await?; + + anyhow::bail!( + "Failed to submit agent feedback.\nStatus: {:?}\nBody: {body}", + response.status() + ) + } + + Ok(()) + } } fn build_request( diff --git a/crates/cloud_api_types/Cargo.toml b/crates/cloud_api_types/Cargo.toml index 3ca12021181d28daabd20df45801521c4492cc27..13f6f0833fcd073235e4fdb26ea4751a9127ce36 100644 --- a/crates/cloud_api_types/Cargo.toml +++ b/crates/cloud_api_types/Cargo.toml @@ -17,6 +17,7 @@ chrono.workspace = true ciborium.workspace = true cloud_llm_client.workspace = true serde.workspace = true +serde_json.workspace = true strum.workspace = true [dev-dependencies] diff --git a/crates/cloud_api_types/src/cloud_api_types.rs b/crates/cloud_api_types/src/cloud_api_types.rs index 7cbf42eacdd56a61c71677f7ae3c123d680851e5..64de56c14b451d32c09218f72446ae25549fd83c 100644 --- a/crates/cloud_api_types/src/cloud_api_types.rs +++ b/crates/cloud_api_types/src/cloud_api_types.rs @@ -56,3 +56,12 @@ pub struct LlmToken(pub String); pub struct CreateLlmTokenResponse { pub token: LlmToken, } + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub struct SubmitAgentThreadFeedbackBody { + pub organization_id: Option, + pub agent: String, + pub session_id: String, + pub rating: String, + pub thread: serde_json::Value, +}