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.
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.
Token-saving HTTP proxy for Claude Code that renders system prompt + tools
as 5pt Menlo monospace images. Achieves 65-73% token reduction with 100%
reasoning quality preserved on Opus 4.7.
Production state at this commit:
- src/proxy.py: 972 lines, battle-tested with 46+ real Claude Code sessions
observed (~551k tokens saved, ~$8.27 in stats.json as of commit time)
- bin/cli.js: Node wrapper that spawns Python proxy on port 47821
- scripts/gen_atlas.py: generates the bundled Menlo 5pt glyph atlas
- scripts/install.js: postinstall — verifies Python + Pillow + httpx
- src/zig/: earlier Zig 0.16 renderer (kept for reference, not built)
- CLAUDE.md, HANDOFF.md: contributor notes
- package.json: npm entry point, version 0.1.0
Known issue (deferred for separate fix):
src/proxy.py:125 — `tallest = max(lines_per_col, last_col_lines) if
c_needed == 1 else lines_per_col` pads every single-column image to
full MAX_EDGE=1568 height regardless of content, wasting image-token
budget for small inputs. Tight bounding would improve savings further.
This is the rollback point. Any subsequent changes (Rust port, Python
fixes, etc.) build on this proven baseline.