Skip to content

Commit 155580f

Browse files
committed
Render snippet fallback in search XML when description is absent
Display truncated content preview from the new snippet field when no AI description is available, giving agents useful context for all search results.
1 parent 1234276 commit 155580f

3 files changed

Lines changed: 1097 additions & 661 deletions

File tree

src/tests/test_response_transformer.py

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,94 @@ def test_preserves_server_order(self):
305305
assert important_line < less_important_line
306306
assert less_important_line < another_line
307307

308+
def test_snippet_fallback_when_no_description(self):
309+
"""Test that snippet is rendered as truncated content when description is absent."""
310+
response = {
311+
"results": [
312+
{
313+
"kind": "Symbol",
314+
"identifier": "owner/repo::file.py::func",
315+
"location": {
316+
"path": "file.py",
317+
"range": {"start": {"line": 1}, "end": {"line": 5}}
318+
},
319+
"snippet": "def func(): return 42"
320+
}
321+
]
322+
}
323+
324+
result = transform_search_response_to_xml(response)
325+
326+
assert '<content truncated="true">' in result
327+
assert "def func(): return 42" in result
328+
assert "</content>" in result
329+
assert "<description>" not in result
330+
331+
def test_snippet_html_escaped(self):
332+
"""Test that snippet content is HTML-escaped."""
333+
response = {
334+
"results": [
335+
{
336+
"kind": "Symbol",
337+
"identifier": "owner/repo::file.py::func",
338+
"location": {
339+
"path": "file.py",
340+
"range": {"start": {"line": 1}, "end": {"line": 5}}
341+
},
342+
"snippet": 'if x < 10 && y > 5: return "<ok>"'
343+
}
344+
]
345+
}
346+
347+
result = transform_search_response_to_xml(response)
348+
349+
assert "&lt;" in result
350+
assert "&amp;" in result
351+
assert '<content truncated="true">' in result
352+
353+
def test_description_takes_precedence_over_snippet(self):
354+
"""Test that description wins when both description and snippet are present."""
355+
response = {
356+
"results": [
357+
{
358+
"kind": "Symbol",
359+
"identifier": "owner/repo::file.py::func",
360+
"location": {
361+
"path": "file.py",
362+
"range": {"start": {"line": 1}, "end": {"line": 5}}
363+
},
364+
"description": "A helper function",
365+
"snippet": "def func(): return 42"
366+
}
367+
]
368+
}
369+
370+
result = transform_search_response_to_xml(response)
371+
372+
assert "<description>A helper function</description>" in result
373+
assert '<content truncated="true">' not in result
374+
375+
def test_no_description_no_snippet_self_closing(self):
376+
"""Test that results without description and without snippet use self-closing tags."""
377+
response = {
378+
"results": [
379+
{
380+
"kind": "Symbol",
381+
"identifier": "owner/repo::file.py::func",
382+
"location": {
383+
"path": "file.py",
384+
"range": {"start": {"line": 1}, "end": {"line": 5}}
385+
}
386+
}
387+
]
388+
}
389+
390+
result = transform_search_response_to_xml(response)
391+
392+
assert "/>" in result
393+
assert "<description>" not in result
394+
assert "<content" not in result
395+
308396
def test_data_preservation_without_content(self):
309397
"""Test that all essential data is preserved."""
310398
response = {

src/utils/response_transformer.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,10 @@ def _build_result_info(result: Dict) -> Dict:
9999
if result.get("description"):
100100
result_info["description"] = result["description"]
101101

102+
# Add snippet if present (fallback content when description is missing)
103+
if result.get("snippet"):
104+
result_info["snippet"] = result["snippet"]
105+
102106
# Add contentByteSize if present
103107
if result.get("contentByteSize") is not None:
104108
result_info["contentByteSize"] = result["contentByteSize"]
@@ -118,10 +122,15 @@ def _build_xml_without_content(file_groups: OrderedDict) -> str:
118122
for result in results:
119123
attrs = _build_xml_attributes(path, result)
120124
description = result.get("description")
125+
snippet = result.get("snippet")
121126
if description:
122127
xml_parts.append(f' <search_result {" ".join(attrs)}>')
123128
xml_parts.append(f' <description>{html.escape(description)}</description>')
124129
xml_parts.append(f' </search_result>')
130+
elif snippet:
131+
xml_parts.append(f' <search_result {" ".join(attrs)}>')
132+
xml_parts.append(f' <content truncated="true">{html.escape(snippet)}</content>')
133+
xml_parts.append(f' </search_result>')
125134
else:
126135
xml_parts.append(f' <search_result {" ".join(attrs)} />')
127136

0 commit comments

Comments
 (0)