Make code block eval resilient to indentation (#29633)

This reduces spurious failures in the eval.

Release Notes:

- N/A
This commit is contained in:
Richard Feldman 2025-04-29 22:13:13 -04:00 committed by GitHub
parent 75a9ed164b
commit d566864891
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 34 additions and 21 deletions

View file

@ -174,7 +174,6 @@ impl Tool for EditFileTool {
"The `old_string` and `new_string` are identical, so no changes would be made." "The `old_string` and `new_string` are identical, so no changes would be made."
)); ));
} }
let old_string = input.old_string.clone();
let result = cx let result = cx
.background_spawn(async move { .background_spawn(async move {
@ -214,21 +213,6 @@ impl Tool for EditFileTool {
input.path.display() input.path.display()
) )
} else { } else {
let old_string_with_buffer = format!(
"old_string:\n\n{}\n\n-------file-------\n\n{}",
&old_string,
buffer.text()
);
let path = {
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
let mut hasher = DefaultHasher::new();
old_string_with_buffer.hash(&mut hasher);
PathBuf::from(format!("failed_tool_{}.txt", hasher.finish()))
};
std::fs::write(path, old_string_with_buffer).unwrap();
anyhow!("Failed to match the provided `old_string`") anyhow!("Failed to match the provided `old_string`")
} }
})?; })?;

View file

@ -101,8 +101,13 @@ impl Example for CodeBlockCitations {
let content = let content =
&text[(citation.len() + 1)..content_len - (citation.len() + 1)]; &text[(citation.len() + 1)..content_len - (citation.len() + 1)];
// deindent (trim the start of each line) because sometimes the model
// chooses to deindent its code snippets for the sake of readability,
// which in markdown is not only reasonable but usually desirable.
cx.assert( cx.assert(
buffer_text.contains(&content), deindent(&buffer_text)
.trim()
.contains(deindent(&content).trim()),
"Code block content was found in file", "Code block content was found in file",
) )
.ok(); .ok();
@ -129,10 +134,16 @@ impl Example for CodeBlockCitations {
.to_string(); .to_string();
} }
// deindent (trim the start of each line) because sometimes the model
// chooses to deindent its code snippets for the sake of readability,
// which in markdown is not only reasonable but usually desirable.
cx.assert_eq( cx.assert_eq(
snippet.as_str(), deindent(snippet.as_str()).trim(),
content, deindent(content).trim(),
"Code block snippet was at specified line/col", format!(
"Code block was at {:?}-{:?}",
range.start, range.end
),
) )
.ok(); .ok();
} }
@ -189,3 +200,12 @@ impl Example for CodeBlockCitations {
] ]
} }
} }
fn deindent(as_str: impl AsRef<str>) -> String {
as_str
.as_ref()
.lines()
.map(|line| line.trim_start())
.collect::<Vec<&str>>()
.join("\n")
}

View file

@ -6,12 +6,21 @@ pub struct PathWithRange {
pub range: Option<Range<LineCol>>, pub range: Option<Range<LineCol>>,
} }
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Clone, Copy, PartialEq, Eq)]
pub struct LineCol { pub struct LineCol {
pub line: u32, pub line: u32,
pub col: Option<u32>, pub col: Option<u32>,
} }
impl std::fmt::Debug for LineCol {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self.col {
Some(col) => write!(f, "L{}:{}", self.line, col),
None => write!(f, "L{}", self.line),
}
}
}
impl LineCol { impl LineCol {
pub fn new(str: impl AsRef<str>) -> Option<Self> { pub fn new(str: impl AsRef<str>) -> Option<Self> {
let str = str.as_ref(); let str = str.as_ref();