Return bounded source excerpts instead of rejecting oversized reads

This commit is contained in:
Nico committed 2026-10-20 21:12:03 +02:00
1 parent ab31924b79
commit ecec96ce14
5 files changed
+232 -6

No files matched your search

@@ -110,6 +110,11 @@ def evidence_page(entry, index=0):
result = capture['result']
item = {'query':capture['query'], **{k:result[k] for k in
('coverage','truncated','interpretation') if k in result}}
if capture['query'].get('kind') == 'source.read' and 'line_limit' in result:
item.update({k:result[k] for k in ('requested_start_line','requested_end_line',
'requested_line_count','line_limit','line_limit_exceeded','returned_start_line',
'returned_end_line','returned_line_count','requested_range_complete','next_start_line','eof')
if k in result})
if capture['query'].get('kind') == 'database.relations':
item['nodes'] = [{k:node[k] for k in ('node','table','complete','upstream_complete',
'local_truncated','returned_rows','distribution','skip_reason') if k in node}
@@ -404,6 +404,35 @@
},
"selection": {
"type": "object"
},
"requested_line_count": {
"type": "integer",
"minimum": 1
},
"line_limit": {
"type": "integer",
"minimum": 1
},
"line_limit_exceeded": {
"type": "boolean"
},
"returned_line_count": {
"type": "integer",
"minimum": 0
},
"requested_range_complete": {
"type": "boolean"
},
"next_start_line": {
"type": [
"integer",
"null"
],
"minimum": 1
},
"read_notice": {
"type": "string",
"maxLength": 300
}
}
}
+15 -6
View File
@@ -69,8 +69,7 @@ class GitSourceProvider:
if args["path"] not in self.inventory():
raise ToolError("PROJECT_SOURCE_NOT_FOUND")
if (any(type(args[k]) is not int for k in ('start_line', 'end_line'))
or args['start_line'] < 1 or args["end_line"] < args["start_line"]
or args["end_line"]-args["start_line"]+1 > self.limits.max_lines):
or args['start_line'] < 1 or args["end_line"] < args["start_line"]):
raise ToolError("PROJECT_SOURCE_RANGE")
elif "path_prefix" in args:
safe_path(args["path_prefix"], empty=True)
@@ -204,14 +203,24 @@ class GitSourceProvider:
if kind == "source.read":
text, source = self._read(args["path"])
lines = text.splitlines()
end = min(args['end_line'], len(lines))
return {"items": [{**source, "line": i+1, "text": lines[i][:2048], "text_truncated":len(lines[i])>2048}
requested_count = args['end_line'] - args['start_line'] + 1
end = min(args['end_line'], args['start_line'] + self.limits.max_lines - 1, len(lines))
truncated = end < min(args['end_line'], len(lines))
result = {"items": [{**source, "line": i+1, "text": lines[i][:2048], "text_truncated":len(lines[i])>2048}
for i in range(args["start_line"]-1, end)],
"coverage":"selected_lines", "truncated":False,
"total_lines":len(lines), "eof":args['end_line'] >= len(lines),
"coverage":"selected_lines", "truncated":truncated,
"total_lines":len(lines), "eof":end >= len(lines),
"requested_start_line":args['start_line'], "requested_end_line":args['end_line'],
"returned_start_line":args['start_line'] if args['start_line'] <= end else None,
"returned_end_line":end if args['start_line'] <= end else None}
if requested_count > self.limits.max_lines:
result.update(requested_line_count=requested_count, line_limit=self.limits.max_lines,
line_limit_exceeded=True, returned_line_count=len(result['items']),
requested_range_complete=not truncated,
next_start_line=end+1 if truncated else None,
read_notice=f"Requested {requested_count} lines; at most {self.limits.max_lines} allowed. "
f"Returned {len(result['items'])} lines; inspect returned range and EOF before drawing conclusions.")
return result
return self._search(args)
@staticmethod