zeta2: Include a single unified diff for the edit history (#40400)

Instead of producing multiple code blocks for each edit history event,
we now produce a continuous unified diff.

Release Notes:

- N/A

---------

Co-authored-by: Oleksiy Syvokon <oleksiy.syvokon@gmail.com>
This commit is contained in:
Agus Zubiaga
2025-10-16 16:03:15 +00:00
committed by GitHub
co-authored by Oleksiy Syvokon
parent 923e880150
commit 23a0b6503c
4 changed files with 123 additions and 46 deletions
Generated
+1
View File
@@ -3384,6 +3384,7 @@ version = "0.1.0"
dependencies = [
"anyhow",
"chrono",
"indoc",
"pretty_assertions",
"serde",
"serde_json",
+1
View File
@@ -25,3 +25,4 @@ workspace-hack.workspace = true
[dev-dependencies]
pretty_assertions.workspace = true
indoc.workspace = true
@@ -1,6 +1,7 @@
use chrono::Duration;
use serde::{Deserialize, Serialize};
use std::{
fmt::Display,
ops::{Add, Range, Sub},
path::{Path, PathBuf},
sync::Arc,
@@ -91,6 +92,38 @@ pub enum Event {
},
}
impl Display for Event {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Event::BufferChange {
path,
old_path,
diff,
predicted,
} => {
let new_path = path.as_deref().unwrap_or(Path::new("untitled"));
let old_path = old_path.as_deref().unwrap_or(new_path);
if *predicted {
write!(
f,
"// User accepted prediction:\n--- a/{}\n+++ b/{}\n{diff}",
old_path.display(),
new_path.display()
)
} else {
write!(
f,
"--- a/{}\n+++ b/{}\n{diff}",
old_path.display(),
new_path.display()
)
}
}
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Signature {
pub text: String,
@@ -204,3 +237,82 @@ impl Sub for Line {
Self(self.0 - rhs.0)
}
}
#[cfg(test)]
mod tests {
use super::*;
use indoc::indoc;
use pretty_assertions::assert_eq;
#[test]
fn test_event_display() {
let ev = Event::BufferChange {
path: None,
old_path: None,
diff: "@@ -1,2 +1,2 @@\n-a\n-b\n".into(),
predicted: false,
};
assert_eq!(
ev.to_string(),
indoc! {"
--- a/untitled
+++ b/untitled
@@ -1,2 +1,2 @@
-a
-b
"}
);
let ev = Event::BufferChange {
path: Some(PathBuf::from("foo/bar.txt")),
old_path: Some(PathBuf::from("foo/bar.txt")),
diff: "@@ -1,2 +1,2 @@\n-a\n-b\n".into(),
predicted: false,
};
assert_eq!(
ev.to_string(),
indoc! {"
--- a/foo/bar.txt
+++ b/foo/bar.txt
@@ -1,2 +1,2 @@
-a
-b
"}
);
let ev = Event::BufferChange {
path: Some(PathBuf::from("abc.txt")),
old_path: Some(PathBuf::from("123.txt")),
diff: "@@ -1,2 +1,2 @@\n-a\n-b\n".into(),
predicted: false,
};
assert_eq!(
ev.to_string(),
indoc! {"
--- a/123.txt
+++ b/abc.txt
@@ -1,2 +1,2 @@
-a
-b
"}
);
let ev = Event::BufferChange {
path: Some(PathBuf::from("abc.txt")),
old_path: Some(PathBuf::from("123.txt")),
diff: "@@ -1,2 +1,2 @@\n-a\n-b\n".into(),
predicted: true,
};
assert_eq!(
ev.to_string(),
indoc! {"
// User accepted prediction:
--- a/123.txt
+++ b/abc.txt
@@ -1,2 +1,2 @@
-a
-b
"}
);
}
}
@@ -1,9 +1,7 @@
//! Zeta2 prompt planning and generation code shared with cloud.
use anyhow::{Context as _, Result, anyhow};
use cloud_llm_client::predict_edits_v3::{
self, Event, Line, Point, PromptFormat, ReferencedDeclaration,
};
use cloud_llm_client::predict_edits_v3::{self, Line, Point, PromptFormat, ReferencedDeclaration};
use indoc::indoc;
use ordered_float::OrderedFloat;
use rustc_hash::{FxHashMap, FxHashSet};
@@ -419,7 +417,7 @@ impl<'a> PlannedPrompt<'a> {
};
if self.request.events.is_empty() {
prompt.push_str("No edits yet.\n\n");
prompt.push_str("(No edit history)\n\n");
} else {
prompt.push_str(
"The following are the latest edits made by the user, from earlier to later.\n\n",
@@ -465,50 +463,15 @@ impl<'a> PlannedPrompt<'a> {
}
fn push_events(output: &mut String, events: &[predict_edits_v3::Event]) {
if events.is_empty() {
return;
};
writeln!(output, "`````diff").unwrap();
for event in events {
match event {
Event::BufferChange {
path,
old_path,
diff,
predicted,
} => {
if let Some(old_path) = &old_path
&& let Some(new_path) = &path
{
if old_path != new_path {
writeln!(
output,
"User renamed {} to {}\n\n",
old_path.display(),
new_path.display()
)
.unwrap();
}
}
let path = path
.as_ref()
.map_or_else(|| "untitled".to_string(), |path| path.display().to_string());
if *predicted {
writeln!(
output,
"User accepted prediction {:?}:\n`````diff\n{}\n`````\n",
path, diff
)
.unwrap();
} else {
writeln!(
output,
"User edited {:?}:\n`````diff\n{}\n`````\n",
path, diff
)
.unwrap();
}
}
}
writeln!(output, "{}", event).unwrap();
}
writeln!(output, "`````\n").unwrap();
}
fn push_file_snippets(