fix(vision): mkdir converted-PNG output dir; wire SVG pass-through to rasterizer; AUTHOR_MAP for #52688

- _normalize_to_supported_image: ensure cache/vision exists before writing
  the converted PNG (fresh HERMES_HOME had no dir -> FileNotFoundError).
- resolver: SVG passes through as image/svg+xml instead of erroring; the
  call sites rasterize to PNG via the salvaged normalize step (cairosvg /
  svglib / rsvg-convert / inkscape, best-effort with actionable error).
- normalization offloaded via asyncio.to_thread at both call sites.
- tests: resolver pass-through + rasterization + no-converter error paths.
- AUTHOR_MAP: jonathan@mintrx.com -> JAlmanzarMint (PR #52688 salvage).
This commit is contained in:
teknium1 2026-07-04 14:11:30 -07:00 committed by Teknium
parent ac94f2c8a1
commit 4ac8c7546b
3 changed files with 41 additions and 4 deletions

View file

@ -45,6 +45,7 @@ ACP_REGISTRY_MANIFEST = REPO_ROOT / "acp_registry" / "agent.json"
# Auto-extracted from noreply emails + manual overrides
AUTHOR_MAP = {
"jonathan@mintrx.com": "JAlmanzarMint", # PR #52688 salvage (vision: rasterize SVG / re-encode unsupported raster formats to PNG before embedding), folded into #57890
"al3060388206@gmail.com": "ooiuuii", # PR #58466/#58377 salvage (redact: fireworks fw-/fpk_ prefixes; telegram: redact bot tokens out of transport error strings)
"yingwaizhiying@gmail.com": "msh01", # PR #58250 salvage (telegram: wall-clock init timeout via daemon-thread deadline + abandon the shielded initialize task on timeout so the retry ladder advances instead of hanging on attempt 1/8 under s6 supervision; #58236). Also covers PR #58276 salvage (compression: preserve a real user turn after compaction; #55677).
"danilo@falcao.org": "danilofalcao", # PR #56674 salvage (update: skip unsupported platform.matrix lazy refresh on native Windows — python-olm has no Windows wheel)

View file

@ -223,3 +223,40 @@ class TestExecReadSafety:
with pytest.raises(isrc.SourceNotFound):
await isrc.resolve_image_source(
"/workspace/nope.png", isrc.ResolveContext(task_id="t1"))
class TestSvgNormalization:
"""SVG resolves end-to-end: the resolver passes it through as
image/svg+xml and the vision call sites rasterize it to PNG via
_normalize_to_supported_image (PR #52688, folded in)."""
@pytest.mark.asyncio
async def test_svg_rasterized_when_converter_available(self, tmp_path, monkeypatch):
from tools import vision_tools as vt
isrc = _reload(monkeypatch, tmp_path / "hermes")
monkeypatch.setenv("TERMINAL_ENV", "local")
svg = tmp_path / "art.svg"
svg.write_bytes(b'<svg xmlns="http://www.w3.org/2000/svg" width="4" height="4"/>')
def fake_rasterize(svg_path, out_path):
out_path.write_bytes(PNG)
return True
with patch.object(vt, "_rasterize_svg_to_png", side_effect=fake_rasterize):
res = await isrc.resolve_image_source(str(svg), isrc.ResolveContext())
assert res.mime == "image/svg+xml"
path, mime, err = vt._normalize_to_supported_image(svg, "image/svg+xml")
assert err is None
assert mime == "image/png"
assert path.read_bytes() == PNG
path.unlink()
def test_svg_actionable_error_when_no_converter(self, tmp_path, monkeypatch):
from tools import vision_tools as vt
_reload(monkeypatch, tmp_path / "hermes")
svg = tmp_path / "art.svg"
svg.write_bytes(b'<svg xmlns="http://www.w3.org/2000/svg"/>')
with patch.object(vt, "_rasterize_svg_to_png", return_value=False):
path, mime, err = vt._normalize_to_supported_image(svg, "image/svg+xml")
assert path is None
assert "rasterizer" in err

View file

@ -316,10 +316,9 @@ def _normalize_to_supported_image(
if detected_mime in _ANTHROPIC_SUPPORTED_MEDIA_TYPES:
return image_path, detected_mime, None
out_path = (
get_hermes_dir("cache/vision", "temp_vision_images")
/ f"converted_{uuid.uuid4()}.png"
)
out_dir = get_hermes_dir("cache/vision", "temp_vision_images")
out_dir.mkdir(parents=True, exist_ok=True)
out_path = out_dir / f"converted_{uuid.uuid4()}.png"
# SVG: needs a rasterizer (Pillow cannot render SVG).
if detected_mime == "image/svg+xml":