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

Commit 33a5d47

Browse files
committed
safer parsing of search replace blocks, use state machine
prompt engineering
1 parent 41b7ac7 commit 33a5d47

File tree

1 file changed

+98
-65
lines changed
  • lib/ai_bot/artifact_update_strategies

1 file changed

+98
-65
lines changed

lib/ai_bot/artifact_update_strategies/diff.rb

Lines changed: 98 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -90,15 +90,45 @@ def apply_changes(changes)
9090

9191
def extract_search_replace_blocks(content)
9292
return nil if content.blank? || content.to_s.strip.downcase.match?(/^\(?no changes?\)?$/m)
93-
return [{ replace: content }] if !content.match?(/<<+\s*SEARCH/)
93+
return [{ replace: content }] if !content.include?("<<< SEARCH")
9494

9595
blocks = []
96-
remaining = content
96+
current_block = {}
97+
state = :initial
98+
search_lines = []
99+
replace_lines = []
100+
101+
content.each_line do |line|
102+
line = line.chomp
103+
104+
case state
105+
when :initial
106+
state = :collecting_search if line.match?(/^<<<* SEARCH/)
107+
when :collecting_search
108+
if line.start_with?("===")
109+
current_block[:search] = search_lines.join("\n").strip
110+
search_lines = []
111+
state = :collecting_replace
112+
else
113+
search_lines << line
114+
end
115+
when :collecting_replace
116+
if line.match?(/>>>* REPLACE/)
117+
current_block[:replace] = replace_lines.join("\n").strip
118+
replace_lines = []
119+
blocks << current_block
120+
current_block = {}
121+
state = :initial
122+
else
123+
replace_lines << line
124+
end
125+
end
126+
end
97127

98-
pattern = /<<+\s*SEARCH\s*\n(.*?)\n==+\s*(\n(.*?))?\n>>+\s*REPLACE/m
99-
while remaining =~ pattern
100-
blocks << { search: $1.strip, replace: $3.to_s.strip }
101-
remaining = $'
128+
# Handle any remaining block
129+
if state == :collecting_replace && !replace_lines.empty?
130+
current_block[:replace] = replace_lines.join("\n").strip
131+
blocks << current_block
102132
end
103133

104134
blocks.empty? ? nil : blocks
@@ -108,26 +138,50 @@ def system_prompt
108138
<<~PROMPT
109139
You are a web development expert generating precise search/replace changes for updating HTML, CSS, and JavaScript code.
110140
111-
Important rules:
141+
CRITICAL RULES:
112142
113143
1. Use EXACTLY this format for changes:
114144
<<<<<<< SEARCH
115-
(first line of code to replace)
116-
(other lines of code to avoid ambiguity)
117-
(last line of code to replace)
145+
(code to replace)
118146
=======
119147
(replacement code)
120148
>>>>>>> REPLACE
121-
2. DO NOT modify the markers or add spaces around them
122-
3. DO NOT add explanations or comments within sections
123-
4. ONLY include [HTML], [CSS], and [JavaScript] sections if they have changes
124-
5. HTML should not include <html>, <head>, or <body> tags, it is injected into a template
125-
6. When specifying a SEARCH block, ALWAYS keep it 8 lines or less, you will be interrupted and a retry will be required if you exceed this limit
126-
7. NEVER EVER ask followup questions, ALL changes must be performed in a single response, you are consumed via an API, there is no opportunity for humans in the loop
127-
8. When performing a non-contiguous search, ALWAYS use ... to denote the skipped lines
128-
9. Be mindful that ... non-contiguous search is not greedy, the following line will only match the first occurrence of the search block
129-
10. Never mix a full section replacement with a search/replace block in the same section
130-
11. ALWAYS skip sections you to not want to change, do not include them in the response
149+
150+
2. SEARCH blocks MUST be 8 lines or less. Break larger changes into multiple smaller search/replace blocks.
151+
152+
3. DO NOT modify the markers or add spaces around them.
153+
154+
4. DO NOT add explanations or comments within sections.
155+
156+
5. ONLY include [HTML], [CSS], and [JavaScript] sections if they have changes.
157+
158+
6. HTML should not include <html>, <head>, or <body> tags, it is injected into a template.
159+
160+
7. NEVER EVER ask followup questions, ALL changes must be performed in a single response.
161+
162+
8. When performing a non-contiguous search, ALWAYS use ... to denote the skipped lines.
163+
164+
9. Be mindful that ... non-contiguous search is not greedy, it will only match the first occurrence.
165+
166+
10. Never mix a full section replacement with a search/replace block in the same section.
167+
168+
11. ALWAYS skip sections you do not want to change, do not include them in the response.
169+
170+
HANDLING LARGE CHANGES:
171+
172+
- Break large HTML structures into multiple smaller search/replace blocks.
173+
- Use strategic anchor points like unique IDs or class names to target specific elements.
174+
- Consider replacing entire components rather than modifying complex internals.
175+
- When elements contain dynamic content, use precise context markers or replace entire containers.
176+
177+
VALIDATION CHECKLIST:
178+
- Each SEARCH block is 8 lines or less
179+
- Every SEARCH has exactly one matching REPLACE
180+
- All blocks are properly closed
181+
- No SEARCH/REPLACE blocks are nested
182+
- Each change is a complete, separate block with its own SEARCH/REPLACE markers
183+
184+
WARNING: Never nest search/replace blocks. Each change must be a complete sequence.
131185
132186
JavaScript libraries must be sourced from the following CDNs, otherwise CSP will reject it:
133187
#{AiArtifact::ALLOWED_CDN_SOURCES.join("\n")}
@@ -143,7 +197,7 @@ def system_prompt
143197
(changes or empty if no changes or entire JavaScript)
144198
[/JavaScript]
145199
146-
Example - Multiple changes in one file:
200+
EXAMPLE 1 - Multiple small changes in one file:
147201
148202
[JavaScript]
149203
<<<<<<< SEARCH
@@ -158,39 +212,35 @@ def system_prompt
158212
>>>>>>> REPLACE
159213
[/JavaScript]
160214
161-
Example - CSS with multiple blocks:
215+
EXAMPLE 2 - Breaking up large HTML changes:
162216
163-
[CSS]
217+
[HTML]
164218
<<<<<<< SEARCH
165-
.button { color: blue; }
219+
<div class="header">
220+
<div class="logo">
221+
<img src="old-logo.png">
222+
</div>
166223
=======
167-
.button { color: red; }
224+
<div class="header">
225+
<div class="logo">
226+
<img src="new-logo.png">
227+
</div>
168228
>>>>>>> REPLACE
229+
169230
<<<<<<< SEARCH
170-
.text { font-size: 12px; }
231+
<div class="navigation">
232+
<ul>
233+
<li>Home</li>
234+
<li>Products</li>
171235
=======
172-
.text { font-size: 16px; }
236+
<div class="navigation">
237+
<ul>
238+
<li>Home</li>
239+
<li>Services</li>
173240
>>>>>>> REPLACE
174-
[/CSS]
175-
176-
Example - Non contiguous search in CSS (replace most CSS with new CSS)
241+
[/HTML]
177242
178-
Original CSS:
179-
180-
[CSS]
181-
body {
182-
color: red;
183-
}
184-
.button {
185-
color: blue;
186-
}
187-
.alert {
188-
background-color: green;
189-
}
190-
.alert2 {
191-
background-color: green;
192-
}
193-
[/CSS]
243+
EXAMPLE 3 - Non-contiguous search in CSS:
194244
195245
[CSS]
196246
<<<<<<< SEARCH
@@ -203,37 +253,20 @@ def system_prompt
203253
color: red;
204254
}
205255
>>>>>>> REPLACE
206-
207-
RESULT:
208-
209-
[CSS]
210-
body {
211-
color: red;
212-
}
213-
.alert2 {
214-
background-color: green;
215-
}
216256
[/CSS]
217257
218-
Example - full HTML replacement:
258+
EXAMPLE 4 - Full HTML replacement:
219259
220260
[HTML]
221261
<div>something old</div>
222-
<div>another somethin old</div>
262+
<div>another something old</div>
223263
[/HTML]
224264
225265
output:
226266
227267
[HTML]
228268
<div>something new</div>
229269
[/HTML]
230-
231-
result:
232-
[HTML]
233-
<div>something new</div>
234-
[/HTML]
235-
236-
237270
PROMPT
238271
end
239272

0 commit comments

Comments
 (0)