Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 14 additions & 9 deletions agent/image_routing.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,13 @@
_IMAGE_EXT_PATTERN = "|".join(e.lstrip(".") for e in _IMAGE_EXTS)

# Absolute / home-relative local image path. Matches the same shape gateway's
# extract_local_files() uses: anchors to ``~/`` or ``/``, ignores matches inside
# URLs (the ``(?<![/:\w.])`` lookbehind), and case-insensitive on the extension.
# extract_local_files() uses: anchors to ``~/``, ``/``, or Windows drive-letter
# absolutes, ignores matches inside URLs (the ``(?<![/:\w.])`` lookbehind), and
# is case-insensitive on the extension.
_LOCAL_IMAGE_PATH_RE = re.compile(
r"(?<![/:\w.])(?:~/|/)(?:[\w.\-]+/)*[\w.\-]+\.(?:" + _IMAGE_EXT_PATTERN + r")\b",
r"(?<![/:\w.])(?:~/|/|[A-Za-z]:[/\\])(?:[\w.\-]+[/\\])*[\w.\-]+\.(?:"
+ _IMAGE_EXT_PATTERN
+ r")\b",
re.IGNORECASE,
)

Expand All @@ -84,9 +87,10 @@ def extract_image_refs(text: str) -> Tuple[List[str], List[str]]:

Returns ``(local_paths, urls)``:

* ``local_paths`` — absolute (``/``) or home-relative (``~/``) paths
whose suffix is an image extension AND whose expanded form exists
on disk as a file. Order-preserving, deduplicated.
* ``local_paths`` — absolute (``/`` or Windows drive-letter) or
home-relative (``~/``) paths whose suffix is an image extension AND
whose expanded form exists on disk as a file. Order-preserving,
deduplicated.
* ``urls`` — ``http(s)://…`` URLs whose path ends in an image
extension (a ``?query`` is allowed after the extension).
Order-preserving, deduplicated.
Expand Down Expand Up @@ -119,16 +123,17 @@ def _in_code(pos: int) -> bool:
if _in_code(match.start()):
continue
raw = match.group(0)
expanded = os.path.expanduser(raw)
expanded = os.path.normpath(os.path.expanduser(raw))
try:
if not os.path.isfile(expanded):
continue
except OSError:
# ENAMETOOLONG / EINVAL on pathological inputs — skip rather than crash.
continue
if expanded in seen_paths:
dedupe_key = os.path.normcase(expanded)
if dedupe_key in seen_paths:
continue
seen_paths.add(expanded)
seen_paths.add(dedupe_key)
local_paths.append(expanded)

urls: list[str] = []
Expand Down
38 changes: 38 additions & 0 deletions tests/agent/test_image_routing.py
Original file line number Diff line number Diff line change
Expand Up @@ -539,12 +539,50 @@ def test_finds_absolute_path(self, tmp_path: Path):
def test_finds_home_relative_path(self, tmp_path: Path, monkeypatch):
# Simulate ~/foo.png by pointing HOME at tmp_path and creating the file
monkeypatch.setenv("HOME", str(tmp_path))
monkeypatch.setenv("USERPROFILE", str(tmp_path))
img = tmp_path / "foo.png"
img.write_bytes(_png_bytes())
paths, urls = extract_image_refs("see ~/foo.png please")
assert paths == [str(img)]
assert urls == []

def test_finds_windows_drive_letter_paths(self):
first = "C:\\Users\\Hiro\\shot.png"
second = "D:/captures/current.JPG"

with patch("agent.image_routing.os.path.isfile", return_value=True):
paths, urls = extract_image_refs(f"Compare {first} against {second}.")

assert paths == [first, second]
assert urls == []

def test_windows_drive_letter_paths_are_case_insensitive(self):
path = "c:/captures/current.PNG"

with patch("agent.image_routing.os.path.isfile", return_value=True):
paths, urls = extract_image_refs(f"Check {path}")

assert paths == [path]
assert urls == []

def test_ignores_relative_windows_drive_paths(self):
with patch("agent.image_routing.os.path.isfile", return_value=True):
paths, urls = extract_image_refs(
"Do not attach C:relative.png or C:folder\\nested.png"
)

assert paths == []
assert urls == []

def test_does_not_match_windows_path_inside_url(self):
body = "Only the URL: https://example.com/files/C:/captures/current.png"

with patch("agent.image_routing.os.path.isfile", return_value=True):
paths, urls = extract_image_refs(body)

assert paths == []
assert urls == ["https://example.com/files/C:/captures/current.png"]

def test_skips_nonexistent_paths(self, tmp_path: Path):
# Path-shaped but no file on disk → skipped.
body = f"What's at {tmp_path}/never_created.png ?"
Expand Down