diff --git a/crates/edit_prediction_cli/src/example.rs b/crates/edit_prediction_cli/src/example.rs index ea0d5a897ac3a83a18d8a75f1b50bcab74a90cdd..871175508e105650c78377c8584554b275d54ff6 100644 --- a/crates/edit_prediction_cli/src/example.rs +++ b/crates/edit_prediction_cli/src/example.rs @@ -243,14 +243,23 @@ pub fn sort_examples_by_repo_and_rev(examples: &mut [Example]) { } pub fn group_examples_by_repo(examples: Vec) -> VecDeque> { - let mut examples_by_repo = HashMap::default(); + let mut examples_by_repo: HashMap> = HashMap::default(); + let mut ungrouped = Vec::new(); for example in examples { - examples_by_repo - .entry(example.spec.repository_url.clone()) - .or_insert_with(Vec::new) - .push(example); + if example.spec.repository_url.is_empty() { + ungrouped.push(example); + } else { + examples_by_repo + .entry(example.spec.repository_url.clone()) + .or_insert_with(Vec::new) + .push(example); + } + } + let mut result: VecDeque> = examples_by_repo.into_values().collect(); + for example in ungrouped { + result.push_back(vec![example]); } - examples_by_repo.into_values().collect() + result } fn parse_markdown_example(input: &str) -> Result { diff --git a/crates/edit_prediction_cli/src/format_prompt.rs b/crates/edit_prediction_cli/src/format_prompt.rs index fff56cac05fea47c8ebdf544fe377b7d63d95fc6..d7d5fa75b55182bee7035e01d3e030f3a34af565 100644 --- a/crates/edit_prediction_cli/src/format_prompt.rs +++ b/crates/edit_prediction_cli/src/format_prompt.rs @@ -168,6 +168,7 @@ impl TeacherPrompt { pub(crate) const EDITABLE_REGION_START: &str = "<|editable_region_start|>\n"; pub(crate) const EDITABLE_REGION_END: &str = "\n<|editable_region_end|>"; pub(crate) const USER_CURSOR_MARKER: &str = "<|user_cursor|>"; + pub(crate) const NO_EDITS: &str = "NO_EDITS"; /// Truncate edit history to this number of last lines const MAX_HISTORY_LINES: usize = 128; @@ -193,6 +194,12 @@ impl TeacherPrompt { // Extract updated (new) editable region from the model response. // The model may include editable region markers in its output, so we need to strip them. let new_editable_region = extract_last_codeblock(response); + + // Check if the model indicated no edits are needed + if new_editable_region.trim() == Self::NO_EDITS { + return Ok(String::new()); + } + let mut new_editable_region = Self::extract_editable_region(&new_editable_region)?; let old_editable_region = Self::extract_editable_region( &example @@ -551,6 +558,19 @@ mod tests { ); } + #[test] + fn test_parse_no_edits_response() { + let response = indoc::indoc! {" + The code is already complete. There is no clear next edit to make. + + ````` + NO_EDITS + ````` + "}; + let codeblock = extract_last_codeblock(response); + assert_eq!(codeblock.trim(), TeacherPrompt::NO_EDITS); + } + #[test] fn test_extract_editable_region_strips_cursor_marker() { let text = indoc::indoc! {" diff --git a/crates/edit_prediction_cli/src/prompts/teacher.md b/crates/edit_prediction_cli/src/prompts/teacher.md index 112bf48953a07b4a9c089451933531b38b9bf84f..4a47bebdad02e3afc4fc863bffd3602d6c738a22 100644 --- a/crates/edit_prediction_cli/src/prompts/teacher.md +++ b/crates/edit_prediction_cli/src/prompts/teacher.md @@ -14,12 +14,18 @@ You are an edit prediction assistant in a code editor. Your task is to predict t ## Rules +- **NEVER undo or revert the user's recent edits.** Examine the diff in the edit history carefully: + - If a line was removed (starts with `-`), do NOT restore that content—even if the code now appears incomplete or broken without it + - If a line was added (starts with `+`), do NOT delete or significantly modify it + - If code appears broken or incomplete after the user's edit, output `NO_EDITS` rather than "fixing" it by reverting + - Only add NEW content that extends the user's work forward; never restore what they removed + - **Key test**: if your prediction would make the code more similar to what it was BEFORE the user's edit, output `NO_EDITS` instead + - **Never assume a deletion was accidental.** Even if removing content breaks the code, breaks a pattern, or leaves text looking "incomplete", respect it. The user may be mid-rewrite. Do NOT "complete" partial text by restoring what was deleted. - Do not just mechanically apply patterns - reason about what changes make sense given the context and the programmer's apparent goals. - Do not just fix syntax errors - look for the broader refactoring pattern and apply it systematically throughout the code. - Keep existing formatting unless it's absolutely necessary - When edit history and surrounding code suggest different edits, prioritize the most recent edits in the history as they best reflect current intent. - When uncertain, predict only the minimal, high-confidence portion of the edit. Prefer a small, correct prediction over a large, speculative one -- Do not delete or remove text that was just added in the edit history. If a recent edit introduces incomplete or incorrect code, finish or fix it in place, or simply do nothing rather than removing it. Only remove a recent edit if the history explicitly shows the user undoing it themselves. - Treat partial text at or near the cursor as the beginning of something the user is actively typing. Complete the code the user appears to be creating based on context. # Input Format @@ -36,8 +42,12 @@ You will be provided with: - Briefly explain the user's current intent based on the edit history and their current cursor location. - Output a markdown codeblock containing **only** the editable region with your predicted edits applied. The codeblock must start with `<|editable_region_start|>` and end with `<|editable_region_end|>`. Do not include any content before or after these tags. +- If no edit is needed (the code is already complete and correct, or there is no clear next edit to make), output a codeblock containing only `NO_EDITS`: + ````` + NO_EDITS + ````` - If the next edit has some uncertainty, you may still predict the surrounding code (such as a function definition, `for` loop, etc) and place the `<|user_cursor|>` within it for the user to fill in. - -e.g. if a user is typing `func<|user_cursor|>`, but you don't know what the function name should be, you can predict `function <|user_cursor|>() {}` + - e.g. if a user is typing `func<|user_cursor|>`, but you don't know what the function name should be, you can predict `function <|user_cursor|>() {}` ## Example 1 @@ -133,9 +143,78 @@ The user is clearly starting to type `eprintln!()`, however, what they intend to fn handle_close_button_click(modal_state: &mut ModalState, evt: &Event) { modal_state.close(); eprintln!("<|user_cursor|>"); + modal_state.dismiss(); <|editable_region_end|> ````` +## Example 3 + +The code is already complete and there is no clear next edit to make. You should output NO_EDITS. + +### User Edit History + +````` +--- a/src/utils.rs ++++ b/src/utils.rs +@@ -10,7 +10,7 @@ + fn add(a: i32, b: i32) -> i32 { +- a - b ++ a + b + } +````` + +### Current File + +`````src/utils.rs +<|editable_region_start|> +fn add(a: i32, b: i32) -> i32 { + a + b<|user_cursor|> +} +<|editable_region_end|> +````` + +### Output + +The user just fixed a bug in the `add` function, changing subtraction to addition. The code is now correct and complete. There is no clear next edit to make. + +````` +NO_EDITS +````` + +## Example 4 + +The user just deleted code, leaving behind what looks incomplete. You must NOT "complete" it by restoring deleted content—that would undo their edit. Output NO_EDITS. **This is the correct response even though the code appears broken.** + +### User Edit History + +````` +--- a/config.nix ++++ b/config.nix +@@ -10,7 +10,7 @@ + # /etc/modular/crashdb needs to be mutable +- ln -s /tmp/crashdb $out/etc/modular/crashdb ++ ln -s /tmp/cr $out/etc/modular/crashdb + ''; +````` + +### Current File + +`````config.nix +<|editable_region_start|> + # /etc/modular/crashdb needs to be mutable + ln -s /tmp/cr<|user_cursor|> $out/etc/modular/crashdb + ''; +<|editable_region_end|> +````` + +### Output + +The user deleted `ashdb` from `/tmp/crashdb`, leaving `/tmp/cr`. Although this looks like incomplete text that I could "complete", doing so would restore deleted content. The user intentionally removed that text—I must not undo their deletion. + +````` +NO_EDITS +````` + # Your task: