Fix Python padding bug: single-column images now tight-bound

src/proxy.py:125 was computing image height as
  tallest = max(lines_per_col, last_col_lines) if c_needed == 1 else lines_per_col

The max(lines_per_col, ...) made every single-column render pad to the
full MAX_EDGE=1568 canvas height regardless of content, wasting ~99% of
the image-token budget for small prompts.

Fix: tight-bound the single-column case.
  tallest = max(1, last_col_lines) if c_needed == 1 else lines_per_col

Multi-column (c_needed > 1) is unchanged — all earlier columns must be
lines_per_col tall to fit them, so the image must be too.

Measured impact (scripts/visual_compare.py):

  sample          before          after           change
  -----------     ------------    ------------    -----------
  17 chars        241x1568        241x14          -99% pixels
  176 chars       241x1568        241x42          -97% pixels
  885 chars       241x1568        241x315         -80% pixels

Verified edge cases hold:
  - empty/short text: still gated by MIN_COMPRESS_CHARS
  - 5000-char single line: 241x441 (63 wrapped lines, tight)
  - 80-char boundary: 241x14 (2 lines, tight)
  - 500-line multi-column: 731x1568 (unchanged, correctly padded)
  - 20KB realistic system prompt: 68.5% savings (matches existing 65-73%)

Added scripts/visual_compare.py to inspect Python vs Rust renderings
side-by-side with labels and dimensions.

This is the immediate win that's independent of the Rust rewrite — every
proxied request gets fewer image tokens starting at the next proxy restart.
This commit is contained in:
teamchong
2026-05-18 11:58:04 -04:00
parent 482c967270
commit 5c2f303cfd
2 changed files with 155 additions and 2 deletions
+150
View File
@@ -0,0 +1,150 @@
#!/usr/bin/env python3
"""
Render the same text through Python and Rust, save PNGs side-by-side, and
also stitch a single comparison image with labels for easy visual inspection.
Usage:
python3 scripts/visual_compare.py [out_dir]
Output:
<out_dir>/python_<sample>.png
<out_dir>/rust_<sample>.png
<out_dir>/compare_<sample>.png (side-by-side with labels)
<out_dir>/README.txt (dim + byte size summary)
"""
import io
import json
import subprocess
import sys
import tempfile
from pathlib import Path
ROOT = Path(__file__).resolve().parent.parent
sys.path.insert(0, str(ROOT / "src"))
import proxy as py_proxy # noqa: E402
from PIL import Image, ImageDraw, ImageFont # noqa: E402
RUST_BIN = ROOT / "src" / "rust" / "target" / "release" / "examples" / "diff_render"
# Three representative samples
SAMPLES = {
"tiny": "echo hello world\n",
"code": (
"def render_chunks(text):\n"
" \"\"\"Render text into 5pt PNG, multi-column.\"\"\"\n"
" font = get_font(FONT_SIZE)\n"
" lines = wrap(text, FIXED_COL_CHARS)\n"
" return paint(font, lines)\n"
),
"tools_schema": json.dumps({
"tools": [
{
"name": "Bash",
"description": "Execute a bash command",
"input_schema": {
"type": "object",
"properties": {
"command": {"type": "string", "description": "the command"},
"timeout": {"type": "integer", "description": "timeout in ms"},
},
"required": ["command"],
},
},
{
"name": "Read",
"description": "Read a file from disk",
"input_schema": {
"type": "object",
"properties": {
"file_path": {"type": "string"},
"offset": {"type": "integer"},
"limit": {"type": "integer"},
},
"required": ["file_path"],
},
},
]
}, indent=2),
}
def label_image(img: Image.Image, label: str) -> Image.Image:
"""Add a black bar at top with the label in white."""
bar_h = 24
out = Image.new("RGB", (img.width, img.height + bar_h), (0, 0, 0))
out.paste(img.convert("RGB"), (0, bar_h))
d = ImageDraw.Draw(out)
try:
f = ImageFont.truetype("/System/Library/Fonts/Menlo.ttc", 14)
except Exception:
f = ImageFont.load_default()
d.text((6, 4), label, fill=(255, 255, 255), font=f)
return out
def main():
out_dir = Path(sys.argv[1]) if len(sys.argv) > 1 else Path("/tmp/cip_compare")
out_dir.mkdir(parents=True, exist_ok=True)
if not RUST_BIN.exists():
print(f"build rust first: cd src/rust && cargo build --release --example diff_render")
sys.exit(1)
# Run rust
spec_path = out_dir / "spec.json"
spec_path.write_text(json.dumps(SAMPLES))
rust_outdir = out_dir / "_rust"
rust_outdir.mkdir(exist_ok=True)
r = subprocess.run(
[str(RUST_BIN), str(spec_path), str(rust_outdir)],
capture_output=True, text=True,
)
if r.returncode != 0:
print("RUST FAILED:", r.stderr); sys.exit(2)
rust_manifest = json.loads((rust_outdir / "manifest.json").read_text())
summary_lines = ["Visual compare: Python proxy.py vs Rust src/rust/", ""]
for name, text in SAMPLES.items():
# Python
py_pngs = py_proxy.render_chunks(text)
assert len(py_pngs) == 1, f"unexpected multi-chunk python output for {name}"
py_path = out_dir / f"python_{name}.png"
py_path.write_bytes(py_pngs[0])
py_img = Image.open(io.BytesIO(py_pngs[0]))
# Rust
rs_files = rust_manifest.get(name, [])
assert len(rs_files) == 1, f"unexpected multi-chunk rust output for {name}"
rs_bytes = (rust_outdir / rs_files[0]).read_bytes()
rs_path = out_dir / f"rust_{name}.png"
rs_path.write_bytes(rs_bytes)
rs_img = Image.open(io.BytesIO(rs_bytes))
# Side-by-side compare
py_labeled = label_image(py_img, f"PYTHON {py_img.width}x{py_img.height} ({len(py_pngs[0])}B)")
rs_labeled = label_image(rs_img, f"RUST {rs_img.width}x{rs_img.height} ({len(rs_bytes)}B)")
gap = 8
cmp_w = max(py_labeled.width, rs_labeled.width)
cmp_h = py_labeled.height + gap + rs_labeled.height
cmp = Image.new("RGB", (cmp_w, cmp_h), (40, 40, 40))
cmp.paste(py_labeled, (0, 0))
cmp.paste(rs_labeled, (0, py_labeled.height + gap))
cmp.save(out_dir / f"compare_{name}.png", "PNG")
line = (
f"[{name:14s}] py={py_img.width:4d}x{py_img.height:<4d} ({len(py_pngs[0]):>6d}B) "
f"rs={rs_img.width:4d}x{rs_img.height:<4d} ({len(rs_bytes):>6d}B) "
f"text_len={len(text)}"
)
print(line)
summary_lines.append(line)
(out_dir / "README.txt").write_text("\n".join(summary_lines) + "\n")
print(f"\nwrote PNGs to {out_dir}/")
if __name__ == "__main__":
main()
+5 -2
View File
@@ -120,9 +120,12 @@ def render_chunks(text: str, font_size: int = FONT_SIZE) -> list[bytes]:
tallest = lines_per_col if c_needed > 1 else len(chunk)
else:
tallest = len(chunk)
# All non-last columns are full lines_per_col; last col has the remainder
# All non-last columns are full lines_per_col; last col has the remainder.
# Single-column case → tight bound (just last_col_lines, no canvas pad).
# Multi-column case → all earlier cols are full lines_per_col tall, so
# the image must be lines_per_col rows tall to fit them.
last_col_lines = len(chunk) - (c_needed - 1) * lines_per_col
tallest = max(lines_per_col, last_col_lines) if c_needed == 1 else lines_per_col
tallest = max(1, last_col_lines) if c_needed == 1 else lines_per_col
chunk_img_h = line_h * tallest
img = Image.new("L", (chunk_img_w, chunk_img_h), 255)
d = ImageDraw.Draw(img)