mirror of
https://github.com/teamchong/pxpipe.git
synced 2026-07-22 02:02:51 +02:00
482c967270
Functional reimplementation in Rust with the same render_chunks/transform
public surface as src/proxy.py. Compiles clean on darwin-arm64, smoke-
tested but NOT YET battle-tested against real Claude Code sessions.
Layout:
src/rust/
Cargo.toml hyper + tokio + reqwest + ab_glyph + png + clap
Cargo.lock committed (application crate, deterministic builds)
src/main.rs clap CLI matching bin/cli.js flags
src/lib.rs module roots
src/font.rs ab_glyph rasterization of bundled JetBrains Mono
src/render.rs newspaper-column text -> PNG (matches Python algo)
src/transform.rs request body transformation (system prompt -> image)
src/proxy.rs hyper HTTP server, upstream forwarding
src/stats.rs persisted savings counters mirroring proxy.py
assets/JetBrainsMono-Regular.ttf OFL-licensed, legally embeddable
examples/render_sample.rs smoke test: render fibonacci snippet
examples/diff_render.rs diff harness companion (paired with
scripts/diff_render.py)
Diff harness:
scripts/diff_render.py feeds same text through Python and Rust
render_chunks, decodes both PNGs, compares
pixel-by-pixel. First run found two divergences:
1. Rust font.rs:43 uses ab_glyph h_advance which returns ~3.4px ceiled
to 4 for Menlo-5pt 'M'; PIL's getlength('M') returns 3.0 exactly.
Every Rust column is 33% wider than Python's.
2. Python proxy.py:125 pads every single-column image to MAX_EDGE=1568
height regardless of content (already noted in baseline commit).
Neither is fixed yet — surfacing the divergences before any swap. Next:
visual inspect both PNGs to confirm Rust output is at least OCR-readable,
then decide whether to fix bug-for-bug compat or correct both sides.
bin/cli.js still spawns Python; this commit only adds the Rust tree, it
does NOT change runtime behavior. The live proxy at :47821 is unaffected.
137 lines
4.5 KiB
Python
137 lines
4.5 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Diff harness: compare Python render_chunks vs Rust render_chunks pixel-by-pixel.
|
|
|
|
Strategy:
|
|
1. For each sample text, call Python render_chunks() -> list[PNG bytes]
|
|
2. Call Rust diff_render binary -> writes PNGs to disk
|
|
3. Decode both sides' PNGs and compare pixel arrays
|
|
4. Report: pass / fail with first diff coordinates
|
|
|
|
Run:
|
|
python3 scripts/diff_render.py
|
|
"""
|
|
|
|
import hashlib
|
|
import json
|
|
import subprocess
|
|
import sys
|
|
import tempfile
|
|
from pathlib import Path
|
|
|
|
# Make src/proxy.py importable
|
|
ROOT = Path(__file__).resolve().parent.parent
|
|
sys.path.insert(0, str(ROOT / "src"))
|
|
|
|
import proxy as py_proxy # noqa: E402
|
|
from PIL import Image # noqa: E402
|
|
import io # noqa: E402
|
|
|
|
RUST_BIN = ROOT / "src" / "rust" / "target" / "release" / "examples" / "diff_render"
|
|
|
|
SAMPLES = {
|
|
"short_ascii": "hello world\n",
|
|
"multiline_code": (
|
|
"def fibonacci(n):\n"
|
|
" if n <= 1: return n\n"
|
|
" return fibonacci(n-1) + fibonacci(n-2)\n"
|
|
"print(fibonacci(10))\n"
|
|
),
|
|
"long_with_blanks": (
|
|
"# Header\n\n"
|
|
+ "\n".join(f"line {i}: " + "x" * (i % 60) for i in range(200))
|
|
+ "\n\n# Footer\n"
|
|
),
|
|
"many_short_lines": "\n".join(f"L{i}" for i in range(500)) + "\n",
|
|
"wide_lines": "\n".join("." * 80 for _ in range(100)) + "\n",
|
|
"tools_like_json": json.dumps(
|
|
{"tools": [{"name": f"tool_{i}", "input_schema": {"type": "object",
|
|
"properties": {f"p{j}": {"type": "string"} for j in range(5)}}}
|
|
for i in range(20)]}, indent=2),
|
|
}
|
|
|
|
|
|
def png_to_pixels(png_bytes: bytes):
|
|
"""Decode PNG to (width, height, raw_grayscale_bytes)."""
|
|
img = Image.open(io.BytesIO(png_bytes))
|
|
img = img.convert("L") # force 8-bit grayscale
|
|
return img.width, img.height, img.tobytes()
|
|
|
|
|
|
def run_rust(samples: dict, out_dir: Path) -> dict:
|
|
"""Invoke rust diff_render binary, returns name -> list[png_bytes]."""
|
|
spec = {name: text for name, text in samples.items()}
|
|
spec_path = out_dir / "spec.json"
|
|
spec_path.write_text(json.dumps(spec))
|
|
res = subprocess.run(
|
|
[str(RUST_BIN), str(spec_path), str(out_dir)],
|
|
capture_output=True, text=True,
|
|
)
|
|
if res.returncode != 0:
|
|
print("RUST FAILED:", res.stderr, file=sys.stderr)
|
|
sys.exit(2)
|
|
manifest = json.loads((out_dir / "manifest.json").read_text())
|
|
out = {}
|
|
for name, files in manifest.items():
|
|
out[name] = [(out_dir / f).read_bytes() for f in files]
|
|
return out
|
|
|
|
|
|
def diff_pixels(py_bytes: bytes, rs_bytes: bytes):
|
|
"""Return None if pixel-identical, else (reason, details)."""
|
|
pw, ph, pp = png_to_pixels(py_bytes)
|
|
rw, rh, rp = png_to_pixels(rs_bytes)
|
|
if (pw, ph) != (rw, rh):
|
|
return ("dim_mismatch", f"py={pw}x{ph} rs={rw}x{rh}")
|
|
if pp == rp:
|
|
return None
|
|
# Find first diff
|
|
for i, (a, b) in enumerate(zip(pp, rp)):
|
|
if a != b:
|
|
x = i % pw
|
|
y = i // pw
|
|
return ("pixel_mismatch", f"first diff at ({x},{y}): py={a} rs={b}")
|
|
return ("byte_len_mismatch", f"py_len={len(pp)} rs_len={len(rp)}")
|
|
|
|
|
|
def main():
|
|
if not RUST_BIN.exists():
|
|
print(f"build rust first: cd src/rust && cargo build --release --example diff_render")
|
|
print(f"missing: {RUST_BIN}")
|
|
sys.exit(1)
|
|
|
|
with tempfile.TemporaryDirectory() as td:
|
|
out_dir = Path(td)
|
|
rust_out = run_rust(SAMPLES, out_dir)
|
|
|
|
total = 0
|
|
fail = 0
|
|
for name, text in SAMPLES.items():
|
|
py_pngs = py_proxy.render_chunks(text)
|
|
rs_pngs = rust_out.get(name, [])
|
|
total += 1
|
|
print(f"\n[{name}] py_chunks={len(py_pngs)} rs_chunks={len(rs_pngs)}")
|
|
if len(py_pngs) != len(rs_pngs):
|
|
fail += 1
|
|
print(f" FAIL: chunk count differs")
|
|
continue
|
|
any_fail = False
|
|
for i, (a, b) in enumerate(zip(py_pngs, rs_pngs)):
|
|
ah = hashlib.sha256(a).hexdigest()[:12]
|
|
bh = hashlib.sha256(b).hexdigest()[:12]
|
|
diff = diff_pixels(a, b)
|
|
if diff is None:
|
|
print(f" chunk {i}: OK py={ah} rs={bh} bytes={len(a)}/{len(b)}")
|
|
else:
|
|
any_fail = True
|
|
print(f" chunk {i}: FAIL {diff[0]}: {diff[1]} py={ah} rs={bh}")
|
|
if any_fail:
|
|
fail += 1
|
|
|
|
print(f"\n=== {total - fail}/{total} samples pixel-identical ===")
|
|
sys.exit(0 if fail == 0 else 1)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|