File size: 2,118 Bytes
2e11c6a
5813a84
 
 
985e10f
5813a84
 
 
 
 
 
 
985e10f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5813a84
 
 
 
 
2e11c6a
5813a84
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2e11c6a
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
"""Localized context helpers for TraceFix-RL."""

from __future__ import annotations

import re
from typing import List, Optional

WINDOW_LINES: int = 10

MAX_CONTEXT_CHARS: int = 2_000


_TRACEBACK_FILE_LINE_RE = re.compile(r'File "([^"]+)", line (\d+)')
_SYNTAX_LINE_RE = re.compile(r"SyntaxError at line (\d+)")


def extract_error_line(traceback_str: str) -> Optional[int]:
    """
    Extract the most relevant crashing line number from sandbox output.

    Preference order:
    1) Last frame pointing to agent code pseudo-files (<agent_code>, <string>).
    2) Last traceback frame line number.
    3) "SyntaxError at line N" fallback.
    """
    if not traceback_str:
        return None

    matches = _TRACEBACK_FILE_LINE_RE.findall(traceback_str)
    if matches:
        preferred_files = {"<agent_code>", "<string>"}
        for file_name, line_str in reversed(matches):
            if file_name in preferred_files:
                return int(line_str)
        return int(matches[-1][1])

    syntax_match = _SYNTAX_LINE_RE.search(traceback_str)
    if syntax_match:
        return int(syntax_match.group(1))

    return None


def get_localized_context(
    code_lines: List[str],
    anchor_line: Optional[int],
    window: int = WINDOW_LINES,
) -> str:
    """Return a bounded ±window slice around the latest edited line."""
    if anchor_line is None or not code_lines:
        return ""

    total = len(code_lines)

    anchor_0 = max(0, min(anchor_line - 1, total - 1))
    start_0 = max(0, anchor_0 - window)
    end_0   = min(total - 1, anchor_0 + window)
    start_1 = start_0 + 1
    end_1   = end_0   + 1
    header  = f"[Showing lines {start_1}{end_1} of {total}, anchor ▶ line {anchor_line}]"

    body_lines = []
    for i in range(start_0, end_0 + 1):
        line_num = i + 1
        marker   = "▶" if i == anchor_0 else "|"
        body_lines.append(f"{line_num:>4} {marker} {code_lines[i]}")

    result = header + "\n" + "\n".join(body_lines)

    if len(result) > MAX_CONTEXT_CHARS:
        result = result[:MAX_CONTEXT_CHARS] + "\n... [context truncated]"

    return result