get_text_context
This commit is contained in:
parent
03e47bad42
commit
504794f9a8
@ -21,6 +21,7 @@ from .read_metadata import handle as read_png_metadata_handler
|
|||||||
from .contact_sheet import handle as contact_sheet_handler
|
from .contact_sheet import handle as contact_sheet_handler
|
||||||
from .list_directory import handle as list_directory_details_handler
|
from .list_directory import handle as list_directory_details_handler
|
||||||
from .preview_image import handle as preview_image_handler
|
from .preview_image import handle as preview_image_handler
|
||||||
|
from .get_text_context import handle as get_text_context_handler
|
||||||
|
|
||||||
# Central registry of all available tools
|
# Central registry of all available tools
|
||||||
TOOL_REGISTRY = [
|
TOOL_REGISTRY = [
|
||||||
@ -87,4 +88,19 @@ TOOL_REGISTRY = [
|
|||||||
},
|
},
|
||||||
handler=preview_image_handler
|
handler=preview_image_handler
|
||||||
),
|
),
|
||||||
|
Tool(
|
||||||
|
name="get_text_context",
|
||||||
|
description="Extracts specific sections of a file with absolute line numbers and surrounding context. This tool MUST be called immediately before edit_file to verify the exact line numbers and content of the block being replaced, preventing misalignment errors.",
|
||||||
|
schema={
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"path": {"type": "string", "description": "Path to the file"},
|
||||||
|
"pattern": {"type": "string", "description": "Regex pattern to search for"},
|
||||||
|
"lines": {"type": "string", "description": "1-based indices or ranges (e.g., '10-20, 45')"},
|
||||||
|
"context": {"type": "integer", "description": "Number of lines of context to show above and below", "default": 1},
|
||||||
|
},
|
||||||
|
"required": ["path"],
|
||||||
|
},
|
||||||
|
handler=get_text_context_handler
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
|||||||
86
tools/get_text_context.py
Normal file
86
tools/get_text_context.py
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
import re
|
||||||
|
import logging
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Any, Dict, List, Set
|
||||||
|
from tools.utils import ToolError, parse_indices
|
||||||
|
|
||||||
|
logger = logging.getLogger("MattCP")
|
||||||
|
|
||||||
|
async def handle(args: Dict[str, Any]):
|
||||||
|
"""
|
||||||
|
Extracts specific sections of a file with absolute line numbers and surrounding context.
|
||||||
|
This tool MUST be called immediately before edit_file to verify the exact line numbers
|
||||||
|
and content of the block being replaced, preventing misalignment errors.
|
||||||
|
"""
|
||||||
|
path_str = args.get("path")
|
||||||
|
pattern = args.get("pattern")
|
||||||
|
lines_str = args.get("lines")
|
||||||
|
context = int(args.get("context", 1))
|
||||||
|
|
||||||
|
if not path_str:
|
||||||
|
raise ToolError("Missing path argument")
|
||||||
|
|
||||||
|
path = Path(path_str)
|
||||||
|
if not path.is_file():
|
||||||
|
raise ToolError(f"{path_str} is not a file.")
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open(path, "r", encoding="utf-8", errors="replace") as f:
|
||||||
|
all_lines = f.readlines()
|
||||||
|
except Exception as e:
|
||||||
|
raise ToolError(f"Error reading file {path_str}: {e}")
|
||||||
|
|
||||||
|
total_lines = len(all_lines)
|
||||||
|
|
||||||
|
# Case 1: No pattern and no lines provided -> Dump whole file
|
||||||
|
if not pattern and not lines_str:
|
||||||
|
output = [f"{i+1}: {line}" for i, line in enumerate(all_lines)]
|
||||||
|
return {"text": "".join(output)}
|
||||||
|
|
||||||
|
# Determine target line numbers (1-based)
|
||||||
|
target_lines: Set[int] = set()
|
||||||
|
|
||||||
|
if pattern:
|
||||||
|
try:
|
||||||
|
regex = re.compile(pattern)
|
||||||
|
for i, line in enumerate(all_lines):
|
||||||
|
if regex.search(line):
|
||||||
|
target_lines.add(i + 1)
|
||||||
|
except re.error as e:
|
||||||
|
raise ToolError(f"Invalid regex pattern: {e}")
|
||||||
|
|
||||||
|
if lines_str:
|
||||||
|
try:
|
||||||
|
indices = parse_indices(lines_str)
|
||||||
|
for idx in indices:
|
||||||
|
if 1 <= idx <= total_lines:
|
||||||
|
target_lines.add(idx)
|
||||||
|
except ToolError as e:
|
||||||
|
raise e
|
||||||
|
except Exception as e:
|
||||||
|
raise ToolError(f"Error parsing line indices: {e}")
|
||||||
|
|
||||||
|
if not target_lines:
|
||||||
|
return {"text": "No matching lines found."}
|
||||||
|
|
||||||
|
# Expand targets with context
|
||||||
|
lines_to_show: Set[int] = set()
|
||||||
|
for line in target_lines:
|
||||||
|
for offset in range(-context, context + 1):
|
||||||
|
ln = line + offset
|
||||||
|
if 1 <= ln <= total_lines:
|
||||||
|
lines_to_show.add(ln)
|
||||||
|
|
||||||
|
# Sort and format output
|
||||||
|
sorted_lines = sorted(list(lines_to_show))
|
||||||
|
output = []
|
||||||
|
last_line = None
|
||||||
|
|
||||||
|
for ln in sorted_lines:
|
||||||
|
if last_line is not None and ln > last_line + 1:
|
||||||
|
output.append("...\n")
|
||||||
|
|
||||||
|
output.append(f"{ln}: {all_lines[ln-1]}")
|
||||||
|
last_line = ln
|
||||||
|
|
||||||
|
return {"text": "".join(output)}
|
||||||
@ -6,36 +6,10 @@ import datetime
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Dict, List, Tuple
|
from typing import Any, Dict, List, Tuple
|
||||||
from PIL import Image, ImageOps
|
from PIL import Image, ImageOps
|
||||||
from tools.utils import format_relative_time, ToolError, get_file_info_list, sort_file_list
|
from tools.utils import format_relative_time, ToolError, get_file_info_list, sort_file_list, parse_indices
|
||||||
|
|
||||||
logger = logging.getLogger("MattCP")
|
logger = logging.getLogger("MattCP")
|
||||||
|
|
||||||
def parse_indices(indices_str: str) -> List[int]:
|
|
||||||
"""
|
|
||||||
Parses a string like '11, 31-33, 44' into a list of 1-based indices.
|
|
||||||
Preserves the order specified in the string.
|
|
||||||
"""
|
|
||||||
indices = []
|
|
||||||
parts = [p.strip() for p in indices_str.split(',')]
|
|
||||||
for part in parts:
|
|
||||||
if not part:
|
|
||||||
continue
|
|
||||||
if '-' in part:
|
|
||||||
try:
|
|
||||||
start, end = map(int, part.split('-'))
|
|
||||||
# Expand range in order. Handle reverse ranges as well.
|
|
||||||
step = 1 if start <= end else -1
|
|
||||||
for i in range(start, end + step, step):
|
|
||||||
indices.append(i)
|
|
||||||
except ValueError:
|
|
||||||
raise ToolError(f"Invalid range format: {part}")
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
indices.append(int(part))
|
|
||||||
except ValueError:
|
|
||||||
raise ToolError(f"Invalid index format: {part}")
|
|
||||||
return indices
|
|
||||||
|
|
||||||
def calculate_patch_dimensions(orig_w: int, orig_h: int, target_tokens: int = 70):
|
def calculate_patch_dimensions(orig_w: int, orig_h: int, target_tokens: int = 70):
|
||||||
"""
|
"""
|
||||||
Calculates optimal pixel dimensions to hit a token budget of at least target_tokens.
|
Calculates optimal pixel dimensions to hit a token budget of at least target_tokens.
|
||||||
|
|||||||
@ -96,3 +96,28 @@ def get_paginated_list(items: List[Any], page: int, page_size: int) -> List[Any]
|
|||||||
start_idx = (page - 1) * page_size
|
start_idx = (page - 1) * page_size
|
||||||
end_idx = start_idx + page_size
|
end_idx = start_idx + page_size
|
||||||
return items[start_idx:end_idx]
|
return items[start_idx:end_idx]
|
||||||
|
|
||||||
|
def parse_indices(indices_str: str) -> List[int]:
|
||||||
|
"""
|
||||||
|
Parses a string like '11, 31-33, 44' into a list of 1-based indices.
|
||||||
|
Preserves the order specified in the string.
|
||||||
|
"""
|
||||||
|
indices = []
|
||||||
|
parts = [p.strip() for p in indices_str.split(',')]
|
||||||
|
for part in parts:
|
||||||
|
if not part:
|
||||||
|
continue
|
||||||
|
if '-' in part:
|
||||||
|
try:
|
||||||
|
start, end = map(int, part.split('-'))
|
||||||
|
step = 1 if start <= end else -1
|
||||||
|
for i in range(start, end + step, step):
|
||||||
|
indices.append(i)
|
||||||
|
except ValueError:
|
||||||
|
raise ToolError(f"Invalid range format: {part}")
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
indices.append(int(part))
|
||||||
|
except ValueError:
|
||||||
|
raise ToolError(f"Invalid index format: {part}")
|
||||||
|
return indices
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user