Skip to content
This repository was archived by the owner on Jul 22, 2025. It is now read-only.

Commit 3d545c2

Browse files
committed
improve diff algorithm
1 parent 9bab74a commit 3d545c2

File tree

3 files changed

+81
-26
lines changed

3 files changed

+81
-26
lines changed

lib/ai_bot/tools/update_artifact.rb

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,18 @@ def self.unified_diff_tip
3131
<p>Some new text</p>
3232
</div>
3333
34-
If you need to supply multiple hunks for a diff use the @@@ separator between hunks.
34+
If you need to supply multiple hunks for a diff use a @@ separator, for example:
35+
36+
@@ -1,3 +1,3 @@
37+
- <p>Some text</p>
38+
+ <p>Some new text</p>
39+
@@ -5,3 +5,3 @@
40+
- </div>
41+
+ <p>more text</p>
42+
</div>
43+
44+
If you supply text without @@ seperators or + and - prefixes, the entire text will be appended to the artifact section.
45+
3546
TIP
3647
end
3748

@@ -76,6 +87,10 @@ def self.allow_partial_tool_calls?
7687
true
7788
end
7889

90+
def chain_next_response?
91+
@chain_next_response
92+
end
93+
7994
def partial_invoke
8095
@selected_tab = :html_diff
8196
if @prev_parameters
@@ -166,7 +181,8 @@ def success_response(artifact)
166181
end
167182

168183
def error_response(message)
169-
@chain_next_response = false
184+
@chain_next_response = true
185+
self.custom_raw = ""
170186

171187
{ status: "error", error: message }
172188
end

lib/utils/diff_utils.rb

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,19 @@ def initialize(original_text:, diff_text:, issue:)
8585
end
8686

8787
def self.apply_hunk(text, diff)
88+
89+
# we need to handle multiple hunks just in case
90+
if diff.match?(/^\@\@.*\@\@$\n/)
91+
hunks = diff.split(/^\@\@.*\@\@$\n/)
92+
if hunks.present?
93+
hunks.each do |hunk|
94+
next if hunk.blank?
95+
text = apply_hunk(text, hunk)
96+
end
97+
return text
98+
end
99+
end
100+
88101
text = text.encode(universal_newline: true)
89102
diff = diff.encode(universal_newline: true)
90103
# we need this for matching
@@ -94,6 +107,10 @@ def self.apply_hunk(text, diff)
94107

95108
validate_diff_format!(text, diff, diff_lines)
96109

110+
if diff_lines.all? { |marker, _| marker == " " }
111+
return text.strip + "\n" + diff.strip
112+
end
113+
97114
lines_to_match = diff_lines.select { |marker, _| ["-", " "].include?(marker) }.map(&:last)
98115
match_start, match_end = find_unique_match(text, lines_to_match, diff)
99116
new_hunk = diff_lines.select { |marker, _| ["+", " "].include?(marker) }.map(&:last).join
@@ -146,14 +163,6 @@ def self.apply_hunk(text, diff)
146163
if diff_lines.empty?
147164
raise MalformedDiffError.new(original_text: text, diff_text: diff, issue: "Diff is empty")
148165
end
149-
150-
unless diff_lines.any? { |marker, _| %w[- +].include?(marker) }
151-
raise MalformedDiffError.new(
152-
original_text: text,
153-
diff_text: diff,
154-
issue: "Diff must contain at least one change (+ or -)",
155-
)
156-
end
157166
end
158167

159168
private_class_method def self.find_unique_match(text, context_lines, diff)

spec/lib/utils/diff_utils_spec.rb

Lines changed: 46 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -175,22 +175,14 @@
175175
end
176176
end
177177
end
178+
end
179+
end
178180

179-
context "when missing changes" do
180-
let(:diff) { <<~DIFF }
181-
<div>
182-
<h1>Title</h1>
183-
</div>
184-
DIFF
185-
186-
it "raises a MalformedDiffError" do
187-
expect { apply_hunk }.to raise_error(
188-
DiscourseAi::Utils::DiffUtils::MalformedDiffError,
189-
) do |error|
190-
expect(error.context["Issue"]).to match(/must contain at least one change/)
191-
end
192-
end
193-
end
181+
context "without markers" do
182+
let(:original_text) { "hello" }
183+
let(:diff) { "world" }
184+
it "will append to the end" do
185+
expect(apply_hunk).to eq("hello\nworld")
194186
end
195187
end
196188

@@ -202,11 +194,49 @@
202194
+123
203195
DIFF
204196

205-
it "can append to file" do
197+
it "can append to end" do
206198
expect(apply_hunk).to eq("hello\nworld\n123")
207199
end
208200
end
209201

202+
context "it can apply multiple hunks to a file" do
203+
let(:original_text) do
204+
<<~TEXT
205+
1
206+
2
207+
3
208+
4
209+
5
210+
6
211+
7
212+
8
213+
TEXT
214+
end
215+
216+
let(:diff) do
217+
<<~DIFF
218+
@@ -1,4 +1,4 @@
219+
2
220+
- 3
221+
@@ -6,4 +6,4 @@
222+
- 7
223+
DIFF
224+
end
225+
226+
it "can apply multiple hunks" do
227+
expected = <<~TEXT
228+
1
229+
2
230+
4
231+
5
232+
6
233+
8
234+
TEXT
235+
expect(apply_hunk).to eq(expected.strip)
236+
end
237+
238+
end
239+
210240
context "with line ending variations" do
211241
let(:original_text) { "line1\r\nline2\nline3\r\n" }
212242
let(:diff) { <<~DIFF }

0 commit comments

Comments
 (0)