diff --git a/crates/cloud_api_client/src/cloud_api_client.rs b/crates/cloud_api_client/src/cloud_api_client.rs index 52ed5970b4319d74d8ecb990d48d52a2f87e4b78..f0010a64cb95d35b56f35801b477a2fd6f665be1 100644 --- a/crates/cloud_api_client/src/cloud_api_client.rs +++ b/crates/cloud_api_client/src/cloud_api_client.rs @@ -206,6 +206,34 @@ impl CloudApiClient { Ok(()) } + + pub async fn submit_edit_prediction_feedback( + &self, + body: SubmitEditPredictionFeedbackBody, + ) -> Result<()> { + let request = self.build_request( + Request::builder().method(Method::POST).uri( + self.http_client + .build_zed_cloud_url("/client/feedback/edit_prediction")? + .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 edit prediction feedback.\nStatus: {:?}\nBody: {body}", + response.status() + ) + } + + Ok(()) + } } fn build_request( diff --git a/crates/cloud_api_types/src/cloud_api_types.rs b/crates/cloud_api_types/src/cloud_api_types.rs index 64de56c14b451d32c09218f72446ae25549fd83c..2b9ec825e7df30fa64f21db1af7612930277798a 100644 --- a/crates/cloud_api_types/src/cloud_api_types.rs +++ b/crates/cloud_api_types/src/cloud_api_types.rs @@ -65,3 +65,13 @@ pub struct SubmitAgentThreadFeedbackBody { pub rating: String, pub thread: serde_json::Value, } + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub struct SubmitEditPredictionFeedbackBody { + pub organization_id: Option, + pub request_id: String, + pub rating: String, + pub inputs: serde_json::Value, + pub output: Option, + pub feedback: String, +} diff --git a/crates/edit_prediction/Cargo.toml b/crates/edit_prediction/Cargo.toml index f1d0d30815273413922e60927059fe3481113577..ace898fb6004668fbde916ab4b0447d8e5b8a553 100644 --- a/crates/edit_prediction/Cargo.toml +++ b/crates/edit_prediction/Cargo.toml @@ -21,6 +21,7 @@ arrayvec.workspace = true brotli.workspace = true buffer_diff.workspace = true client.workspace = true +cloud_api_types.workspace = true cloud_llm_client.workspace = true collections.workspace = true copilot.workspace = true @@ -69,7 +70,6 @@ zstd.workspace = true [dev-dependencies] clock = { workspace = true, features = ["test-support"] } -cloud_api_types.workspace = true cloud_llm_client = { workspace = true, features = ["test-support"] } ctor.workspace = true gpui = { workspace = true, features = ["test-support"] } diff --git a/crates/edit_prediction/src/edit_prediction.rs b/crates/edit_prediction/src/edit_prediction.rs index f106561bab71dd814c9aa307da67578673aaa83d..0ff5af7e8d9445f1ff7978d55c1b91fcc36622dd 100644 --- a/crates/edit_prediction/src/edit_prediction.rs +++ b/crates/edit_prediction/src/edit_prediction.rs @@ -1,6 +1,7 @@ use anyhow::Result; use arrayvec::ArrayVec; use client::{Client, EditPredictionUsage, UserStore}; +use cloud_api_types::SubmitEditPredictionFeedbackBody; use cloud_llm_client::predict_edits_v3::{ PredictEditsV3Request, PredictEditsV3Response, RawCompletionRequest, RawCompletionResponse, }; @@ -2256,18 +2257,38 @@ impl EditPredictionStore { feedback: String, cx: &mut Context, ) { + let organization = self.user_store.read(cx).current_organization(); + self.rated_predictions.insert(prediction.id.clone()); - telemetry::event!( - "Edit Prediction Rated", - request_id = prediction.id.to_string(), - rating, - inputs = prediction.inputs, - output = prediction + + cx.background_spawn({ + let client = self.client.clone(); + let prediction_id = prediction.id.to_string(); + let inputs = serde_json::to_value(&prediction.inputs); + let output = prediction .edit_preview - .as_unified_diff(prediction.snapshot.file(), &prediction.edits), - feedback - ); - self.client.telemetry().flush_events().detach(); + .as_unified_diff(prediction.snapshot.file(), &prediction.edits); + async move { + client + .cloud_client() + .submit_edit_prediction_feedback(SubmitEditPredictionFeedbackBody { + organization_id: organization.map(|organization| organization.id.clone()), + request_id: prediction_id, + rating: match rating { + EditPredictionRating::Positive => "positive".to_string(), + EditPredictionRating::Negative => "negative".to_string(), + }, + inputs: inputs?, + output, + feedback, + }) + .await?; + + anyhow::Ok(()) + } + }) + .detach_and_log_err(cx); + cx.notify(); } }