From 7828463fcb6917a344d26edf46b271442bdbc375 Mon Sep 17 00:00:00 2001 From: Ben Kunkle Date: Thu, 9 Jul 2026 08:28:59 -0400 Subject: [PATCH] edit_prediction: Report patch apply failures (#60637) # Objective - Report V4 edit prediction patch apply failures as cloud rejection events. - Closes EP-210 ## Solution - Added `PatchApplyFailed` as an edit prediction rejection reason. - Converted `prediction_edits_for_single_file_diff` errors into rejected prediction results so the existing rejection pipeline posts them to `/predict_edits/reject`. - Kept interpolation failures reported as `InterpolateFailed`. ## Testing - Ran `cargo fmt --check && cargo check -p edit_prediction`. ## Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [ ] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Release Notes: - N/A --- .../cloud_llm_client/src/cloud_llm_client.rs | 2 + crates/edit_prediction/src/prediction.rs | 28 +++++++++ crates/edit_prediction/src/zeta.rs | 59 ++++++++++++------- 3 files changed, 68 insertions(+), 21 deletions(-) diff --git a/crates/cloud_llm_client/src/cloud_llm_client.rs b/crates/cloud_llm_client/src/cloud_llm_client.rs index 2bc734cc10e..adbcffe9be8 100644 --- a/crates/cloud_llm_client/src/cloud_llm_client.rs +++ b/crates/cloud_llm_client/src/cloud_llm_client.rs @@ -198,6 +198,8 @@ pub enum EditPredictionRejectReason { InterpolatedEmpty, /// Edits returned, but could not be interpolated after buffer changes InterpolateFailed, + /// A patch was returned, but could not be applied to the buffer + PatchApplyFailed, /// The new prediction was preferred over the current one Replaced, /// The current prediction was preferred over the new one diff --git a/crates/edit_prediction/src/prediction.rs b/crates/edit_prediction/src/prediction.rs index 0f7896bc882..592d5e3b1af 100644 --- a/crates/edit_prediction/src/prediction.rs +++ b/crates/edit_prediction/src/prediction.rs @@ -104,6 +104,34 @@ impl EditPredictionResult { e2e_latency, } } + + pub fn new_rejected( + id: EditPredictionId, + edited_buffer: &Entity, + edited_buffer_snapshot: &BufferSnapshot, + inputs: EditPredictionInputs, + model_version: Option, + trigger: PredictEditsRequestTrigger, + e2e_latency: std::time::Duration, + reject_reason: EditPredictionRejectReason, + ) -> Self { + Self { + prediction: EditPrediction { + id, + edits: Arc::default(), + cursor_position: None, + editable_range: None, + snapshot: edited_buffer_snapshot.clone(), + edit_preview: EditPreview::unchanged(edited_buffer_snapshot), + inputs, + buffer: edited_buffer.clone(), + model_version, + trigger, + }, + reject_reason: Some(reject_reason), + e2e_latency, + } + } } #[derive(Clone)] diff --git a/crates/edit_prediction/src/zeta.rs b/crates/edit_prediction/src/zeta.rs index 009d91c5b57..54b324253ad 100644 --- a/crates/edit_prediction/src/zeta.rs +++ b/crates/edit_prediction/src/zeta.rs @@ -9,7 +9,9 @@ use crate::{ udiff::prediction_edits_for_single_file_diff, }; use anyhow::{Context as _, Result}; -use cloud_llm_client::{AcceptEditPredictionBody, predict_edits_v3::RawCompletionRequest}; +use cloud_llm_client::{ + AcceptEditPredictionBody, EditPredictionRejectReason, predict_edits_v3::RawCompletionRequest, +}; use edit_prediction_types::PredictedCursorPosition; use gpui::{App, AppContext as _, Entity, Task, TaskExt, WeakEntity, prelude::*}; use language::{ @@ -516,26 +518,41 @@ pub(crate) fn request_prediction_with_zeta( snapshot: fallback_snapshot, patch, } => { - let Some((buffer, snapshot, edits, cursor_position)) = - prediction_edits_for_single_file_diff(&patch, &project, cx).await? - else { - return Ok(Some( - EditPredictionResult::new( - id, - &fallback_buffer, - &fallback_snapshot, - Arc::new([]), - None, - None, - inputs, - model_version, - trigger, - request_duration, - cx, - ) - .await, - )); - }; + let (buffer, snapshot, edits, cursor_position) = + match prediction_edits_for_single_file_diff(&patch, &project, cx).await { + Ok(Some(edits)) => edits, + Ok(None) => { + return Ok(Some( + EditPredictionResult::new( + id, + &fallback_buffer, + &fallback_snapshot, + Arc::new([]), + None, + None, + inputs, + model_version, + trigger, + request_duration, + cx, + ) + .await, + )); + } + Err(error) => { + log::error!("failed to apply edit prediction patch: {error:?}"); + return Ok(Some(EditPredictionResult::new_rejected( + id, + &fallback_buffer, + &fallback_snapshot, + inputs, + model_version, + trigger, + request_duration, + EditPredictionRejectReason::PatchApplyFailed, + ))); + } + }; let editable_range_in_buffer = edits .iter()