refactor(apply_patch): remove deprecated patch mode, keep edits-only

Drop the legacy unified-diff patch parameter and all related parsing/
generation logic (_parse_patch, _generate_patch, _apply_hunks, etc.).
The tool now accepts only the structured `edits` array, eliminating the
intermediate diff-string round-trip.

Also update file_edit_events tracking and tests to work exclusively
with edits.

Benchmark (zhipu glm-5.1, edits mode): 15/15 cases passed.
This commit is contained in:
chengyongru
2026-05-22 17:25:45 +08:00
committed by Xubin Ren
parent effc1efd92
commit 3d9f50a0cc
4 changed files with 459 additions and 640 deletions
+13 -29
View File
@@ -89,23 +89,18 @@ def test_apply_patch_prepares_trackers_for_each_touched_file(tmp_path: Path) ->
delete_me = tmp_path / "src" / "delete_me.py"
delete_me.write_text("gone\n", encoding="utf-8")
patch = """*** Begin Patch
*** Add File: src/new.py
+fresh
*** Update File: src/existing.py
@@
-old
+new
keep
*** Delete File: src/delete_me.py
*** End Patch"""
edits = [
{"path": "src/new.py", "action": "add", "new_text": "fresh"},
{"path": "src/existing.py", "action": "replace", "old_text": "old", "new_text": "new"},
{"path": "src/delete_me.py", "action": "delete", "old_text": "gone\n"},
]
trackers = prepare_file_edit_trackers(
call_id="call-patch",
tool_name="apply_patch",
tool=None,
workspace=tmp_path,
params={"patch": patch},
params={"edits": edits},
)
assert [tracker.display_path for tracker in trackers] == [
@@ -118,7 +113,7 @@ def test_apply_patch_prepares_trackers_for_each_touched_file(tmp_path: Path) ->
existing.write_text("new\nkeep\n", encoding="utf-8")
delete_me.unlink()
events = [build_file_edit_end_event(tracker, {"patch": patch}) for tracker in trackers]
events = [build_file_edit_end_event(tracker, {"edits": edits}) for tracker in trackers]
by_path = {event["path"]: event for event in events}
assert (by_path["src/new.py"]["added"], by_path["src/new.py"]["deleted"]) == (1, 0)
assert (by_path["src/existing.py"]["added"], by_path["src/existing.py"]["deleted"]) == (1, 1)
@@ -135,12 +130,9 @@ def test_apply_patch_dry_run_does_not_prepare_file_edit_trackers(tmp_path: Path)
workspace=tmp_path,
params={
"dry_run": True,
"patch": """*** Begin Patch
*** Update File: file.txt
@@
-old
+new
*** End Patch""",
"edits": [
{"path": "file.txt", "action": "replace", "old_text": "old", "new_text": "new"}
],
},
)
@@ -221,14 +213,8 @@ def test_streaming_apply_patch_tracker_emits_live_counts_per_file(tmp_path: Path
"call_id": "call-patch",
"name": "apply_patch",
"arguments_delta": (
'{"patch":"*** Begin Patch\\n'
'*** Update File: src/existing.py\\n'
'@@\\n'
'-old\\n'
'+new\\n'
' keep\\n'
'*** Add File: src/new.py\\n'
'+fresh\\n'
'{"edits":[{"path":"src/existing.py","action":"replace","old_text":"old","new_text":"new"}'
',{"path":"src/new.py","action":"add","new_text":"fresh"}]}'
),
})
@@ -255,9 +241,7 @@ def test_streaming_apply_patch_tracker_skips_dry_run(tmp_path: Path) -> None:
"call_id": "call-patch",
"name": "apply_patch",
"arguments_delta": (
'{"dry_run":true,"patch":"*** Begin Patch\\n'
'*** Add File: dry.md\\n'
'+preview\\n'
'{"dry_run":true,"edits":[{"path":"dry.md","action":"add","new_text":"preview"}]}'
),
})