Skip to content

Commit 6fcc528

Browse files
authored
fix: provide tolerance for apply_patch tool (#993)
As explained in detail in the doc comment for `ParseMode::Lenient`, we have observed that GPT-4.1 does not always generate a valid invocation of `apply_patch`. Fortunately, the error is predictable, so we introduce some new logic to the `codex-apply-patch` crate to recover from this error. Because we would like to avoid this becoming a de facto standard (as it would be incompatible if `apply_patch` were provided as an actual executable, unless we also introduced the lenient behavior in the executable, as well), we require passing `ParseMode::Lenient` to `parse_patch_text()` to make it clear that the caller is opting into supporting this special case. Note the analogous change to the TypeScript CLI was #930. In addition to changing the accepted input to `apply_patch`, it also introduced additional instructions for the model, which we include in this PR. Note that `apply-patch` does not depend on either `regex` or `regex-lite`, so some of the checks are slightly more verbose to avoid introducing this dependency. That said, this PR does not leverage the existing `extract_heredoc_body_from_apply_patch_command()`, which depends on `tree-sitter` and `tree-sitter-bash`: https://github.com/openai/codex/blob/5a5aa899143f9b9ef606692c401b010368b15bdb/codex-rs/apply-patch/src/lib.rs#L191-L246 though perhaps it should.
1 parent 5a5aa89 commit 6fcc528

File tree

6 files changed

+281
-35
lines changed

6 files changed

+281
-35
lines changed
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
To edit files, ALWAYS use the `shell` tool with `apply_patch` CLI. `apply_patch` effectively allows you to execute a diff/patch against a file, but the format of the diff specification is unique to this task, so pay careful attention to these instructions. To use the `apply_patch` CLI, you should call the shell tool with the following structure:
2+
3+
```bash
4+
{"cmd": ["apply_patch", "<<'EOF'\\n*** Begin Patch\\n[YOUR_PATCH]\\n*** End Patch\\nEOF\\n"], "workdir": "..."}
5+
```
6+
7+
Where [YOUR_PATCH] is the actual content of your patch, specified in the following V4A diff format.
8+
9+
*** [ACTION] File: [path/to/file] -> ACTION can be one of Add, Update, or Delete.
10+
For each snippet of code that needs to be changed, repeat the following:
11+
[context_before] -> See below for further instructions on context.
12+
- [old_code] -> Precede the old code with a minus sign.
13+
+ [new_code] -> Precede the new, replacement code with a plus sign.
14+
[context_after] -> See below for further instructions on context.
15+
16+
For instructions on [context_before] and [context_after]:
17+
- By default, show 3 lines of code immediately above and 3 lines immediately below each change. If a change is within 3 lines of a previous change, do NOT duplicate the first change’s [context_after] lines in the second change’s [context_before] lines.
18+
- If 3 lines of context is insufficient to uniquely identify the snippet of code within the file, use the @@ operator to indicate the class or function to which the snippet belongs. For instance, we might have:
19+
@@ class BaseClass
20+
[3 lines of pre-context]
21+
- [old_code]
22+
+ [new_code]
23+
[3 lines of post-context]
24+
25+
- If a code block is repeated so many times in a class or function such that even a single `@@` statement and 3 lines of context cannot uniquely identify the snippet of code, you can use multiple `@@` statements to jump to the right context. For instance:
26+
27+
@@ class BaseClass
28+
@@ def method():
29+
[3 lines of pre-context]
30+
- [old_code]
31+
+ [new_code]
32+
[3 lines of post-context]
33+
34+
Note, then, that we do not use line numbers in this diff format, as the context is enough to uniquely identify code. An example of a message that you might pass as "input" to this function, in order to apply a patch, is shown below.
35+
36+
```bash
37+
{"cmd": ["apply_patch", "<<'EOF'\\n*** Begin Patch\\n*** Update File: pygorithm/searching/binary_search.py\\n@@ class BaseClass\\n@@ def search():\\n- pass\\n+ raise NotImplementedError()\\n@@ class Subclass\\n@@ def search():\\n- pass\\n+ raise NotImplementedError()\\n*** End Patch\\nEOF\\n"], "workdir": "..."}
38+
```
39+
40+
File references can only be relative, NEVER ABSOLUTE. After the apply_patch command is run, it will always say "Done!", regardless of whether the patch was successfully applied or not. However, you can determine if there are issue and errors by looking at any warnings or logging lines printed BEFORE the "Done!" is output.

codex-rs/apply-patch/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ use tree_sitter::LanguageError;
1919
use tree_sitter::Parser;
2020
use tree_sitter_bash::LANGUAGE as BASH;
2121

22+
/// Detailed instructions for gpt-4.1 on how to use the `apply_patch` tool.
23+
pub const APPLY_PATCH_TOOL_INSTRUCTIONS: &str = include_str!("../apply_patch_tool_instructions.md");
24+
2225
#[derive(Debug, Error, PartialEq)]
2326
pub enum ApplyPatchError {
2427
#[error(transparent)]

0 commit comments

Comments
 (0)