edit_file: Let agent specify locations of edit chunks (#32628)
These changes help the agent edit files when `<old_text>` matches more than one location. First, the agent can specify an optional `<old_text line=XX>` parameter. When this is provided and multiple matches exist, we use this hint to identify the best match. Second, when there is ambiguity in matches, we now return the agent a more helpful message listing the line numbers of all possible matches. Together, these changes should reduce the number of misplaced edits and agent confusion. I have ensured the LLM Worker works with these prompt changes. Release Notes: - Agent: Improved locating edits
This commit is contained in:
parent
e8d495806f
commit
5d293ae8ac
6 changed files with 286 additions and 63 deletions
|
@ -286,7 +286,13 @@ impl EditAgent {
|
|||
_ => {
|
||||
let ranges = resolved_old_text
|
||||
.into_iter()
|
||||
.map(|text| text.range)
|
||||
.map(|text| {
|
||||
let start_line =
|
||||
(snapshot.offset_to_point(text.range.start).row + 1) as usize;
|
||||
let end_line =
|
||||
(snapshot.offset_to_point(text.range.end).row + 1) as usize;
|
||||
start_line..end_line
|
||||
})
|
||||
.collect();
|
||||
output_events
|
||||
.unbounded_send(EditAgentOutputEvent::AmbiguousEditRange(ranges))
|
||||
|
@ -429,25 +435,25 @@ impl EditAgent {
|
|||
let task = cx.background_spawn(async move {
|
||||
let mut matcher = StreamingFuzzyMatcher::new(snapshot);
|
||||
while let Some(edit_event) = edit_events.next().await {
|
||||
let EditParserEvent::OldTextChunk { chunk, done } = edit_event? else {
|
||||
let EditParserEvent::OldTextChunk {
|
||||
chunk,
|
||||
done,
|
||||
line_hint,
|
||||
} = edit_event?
|
||||
else {
|
||||
break;
|
||||
};
|
||||
|
||||
old_range_tx.send(matcher.push(&chunk))?;
|
||||
old_range_tx.send(matcher.push(&chunk, line_hint))?;
|
||||
if done {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let matches = matcher.finish();
|
||||
let best_match = matcher.select_best_match();
|
||||
|
||||
let old_range = if matches.len() == 1 {
|
||||
matches.first()
|
||||
} else {
|
||||
// No matches or multiple ambiguous matches
|
||||
None
|
||||
};
|
||||
old_range_tx.send(old_range.cloned())?;
|
||||
old_range_tx.send(best_match.clone())?;
|
||||
|
||||
let indent = LineIndent::from_iter(
|
||||
matcher
|
||||
|
@ -456,10 +462,18 @@ impl EditAgent {
|
|||
.unwrap_or(&String::new())
|
||||
.chars(),
|
||||
);
|
||||
let resolved_old_texts = matches
|
||||
.into_iter()
|
||||
.map(|range| ResolvedOldText { range, indent })
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let resolved_old_texts = if let Some(best_match) = best_match {
|
||||
vec![ResolvedOldText {
|
||||
range: best_match,
|
||||
indent,
|
||||
}]
|
||||
} else {
|
||||
matches
|
||||
.into_iter()
|
||||
.map(|range| ResolvedOldText { range, indent })
|
||||
.collect::<Vec<_>>()
|
||||
};
|
||||
|
||||
Ok((edit_events, resolved_old_texts))
|
||||
});
|
||||
|
@ -1374,10 +1388,12 @@ mod tests {
|
|||
&agent,
|
||||
indoc! {"
|
||||
<old_text>
|
||||
return 42;
|
||||
return 42;
|
||||
}
|
||||
</old_text>
|
||||
<new_text>
|
||||
return 100;
|
||||
return 100;
|
||||
}
|
||||
</new_text>
|
||||
"},
|
||||
&mut rng,
|
||||
|
@ -1407,7 +1423,7 @@ mod tests {
|
|||
|
||||
// And AmbiguousEditRange even should be emitted
|
||||
let events = drain_events(&mut events);
|
||||
let ambiguous_ranges = vec![17..31, 52..66, 87..101];
|
||||
let ambiguous_ranges = vec![2..3, 6..7, 10..11];
|
||||
assert!(
|
||||
events.contains(&EditAgentOutputEvent::AmbiguousEditRange(ambiguous_ranges)),
|
||||
"Should emit AmbiguousEditRange for non-unique text"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue