Reapply "fix(render): fit Anthropic page to 1568×728 (~1.15 MP) for WYSIWYG glyphs"

This reverts commit 6eaa8bfcd0.
This commit is contained in:
teamchong
2026-07-01 19:18:20 -04:00
parent 6eaa8bfcd0
commit ed968a7495
8 changed files with 180 additions and 50 deletions
+1 -1
View File
@@ -33,7 +33,7 @@ import {
export const DEFAULT_EXPORT_MODEL = 'claude-sonnet-4-5';
// Chars-per-token for the reporting estimate now lives in transform.ts as
// REPORT_CHARS_PER_TOKEN (single source of truth for all token-estimate constants).
/** Default column width — dense content mode (384 cols = 1928 px). */
/** Default column width — dense content mode (312 cols = 1568 px). */
export const DEFAULT_EXPORT_COLS: number = DENSE_CONTENT_COLS;
// ---------------------------------------------------------------------------
+10 -2
View File
@@ -16,7 +16,15 @@
* PXPIPE_GPT_PROFILES='{"gpt-5.6":{"stripCols":176}}' # widen only
*/
import { MAX_HEIGHT_PX } from './render.js';
/**
* GPT strip height, DECOUPLED from render.ts's MAX_HEIGHT_PX (which is Anthropic's
* 1568-edge / ~1.15 MP clamp). OpenAI's pre-tokenize resize is different: fit within
* 2048×2048, then shortest side → 768. A 768-px-wide portrait strip up to 2048 px tall
* survives un-resampled, so GPT keeps the taller page. Every built-in cost number below
* (1190 / 1445 / 2372 / 1464 / 630 …) was calibrated at this height — do not re-link to
* the Anthropic constant.
*/
export const GPT_MAX_HEIGHT_PX = 1932;
/** Image-token cost model (mirrors OpenAI's mandatory pre-tokenize resize). */
export type GptVisionCost =
@@ -38,7 +46,7 @@ export interface GptModelProfile {
export const DEFAULT_GPT_STRIP_COLS = 152;
const C = DEFAULT_GPT_STRIP_COLS;
const H = MAX_HEIGHT_PX;
const H = GPT_MAX_HEIGHT_PX;
/**
* Conservative fallback for unrecognized models: tile 85/170 over-states cost,
+1 -1
View File
@@ -165,7 +165,7 @@ export interface RenderTextToImagesOptions {
readonly maxCharsPerImage?: number;
/** Render style. Default DENSE_RENDER_STYLE (bare 5×8 cell, anti-aliased). */
readonly style?: RenderStyle;
/** Max page height in px. Default MAX_HEIGHT_PX (1932). */
/** Max page height in px. Default MAX_HEIGHT_PX (728 — Anthropic 1568-edge / ~1.15 MP safe). */
readonly maxHeightPx?: number;
}
+5 -2
View File
@@ -21,7 +21,8 @@
* HistoryTurn list and the planner/renderer are shared.
*/
import { renderTextToPngs, reflow, neutralizeSentinel, MAX_HEIGHT_PX, type RenderedImage } from './render.js';
import { renderTextToPngs, reflow, neutralizeSentinel, type RenderedImage } from './render.js';
import { GPT_MAX_HEIGHT_PX } from './gpt-model-profiles.js';
import { countTokens as o200kCountTokens } from 'gpt-tokenizer/encoding/o200k_base';
/** Portrait-strip width for GPT history images. Mirrors GPT_STRIP_COLS in
@@ -101,7 +102,9 @@ export const GPT_HISTORY_DEFAULTS: GptHistoryOptions = {
collapseChunk: 10,
freezeChunk: 10,
sectionTokens: 2000,
maxHeightPx: MAX_HEIGHT_PX,
// GPT path: OpenAI's resize bounds (2048-bbox / 768 short side) permit the tall
// strip — do NOT re-link to render.ts MAX_HEIGHT_PX (Anthropic's 1568/1.15 MP clamp).
maxHeightPx: GPT_MAX_HEIGHT_PX,
maxImages: GPT_HISTORY_MAX_IMAGES,
reflow: true,
};
+20 -15
View File
@@ -23,21 +23,26 @@ import {
} from './atlas-gray.js';
import { encodeGrayPng, encodeRgbPng } from './png.js';
/** Page-height ceiling. 1932 px 384-col page = 69×69 = 4761 tokens, under both Fable/Opus 4.8
* limits (4784 tokens AND 2000 px/side for >20-image requests). Exported for transform.ts math. */
export const MAX_HEIGHT_PX = 1932;
/** Char budget for the static slab (system + tools + CLAUDE.md). ~50k leaves headroom for
* soft-wrap and dropped chars; pages stay well under the 1932 px ceiling at cols=313. */
export const READABLE_CHARS_PER_IMAGE = 50000;
/** Char budget for dense content (tool output, collapsed history). 384 cols × 240 rows = 92160 chars
* fills the full ~1932×1932 ceiling. NOTE: verbatim recall of imaged text is unreliable at any size. */
export const DENSE_CONTENT_CHARS_PER_IMAGE = 92160;
export const DENSE_CONTENT_COLS = 384;
/** Page-height ceiling. Measured (2026-07-01, count_tokens sweep, claude-sonnet-4-5 see
* /tmp/pxexp/LEVER1-findings.md): the API downscales any image to fit BOTH long-edge 1568
* AND ~1.15 MP (1,143,750 px), then bills px/750 (1525 tok cap, applied per-image).
* The old 1932×1932 page was billed at cap but resampled 0.555× 5×8 glyphs reached the
* encoder at ~2.8×4.4 px. New page shape 1568×728 = 1,141,504 px fits both bounds
* WYSIWYG for the vision encoder (also satisfies 2000 px/side for >20-image requests). */
export const MAX_HEIGHT_PX = 728;
/** Char budget for the static slab (system + tools + CLAUDE.md). Matches physical page
* capacity at 312 cols × 90 rows so image-count estimates track real pagination. */
export const READABLE_CHARS_PER_IMAGE = 28080;
/** Char budget for dense content (tool output, collapsed history). 312 cols × 90 rows = 28080
* chars fills the 1568×728 page. NOTE: verbatim recall of imaged text is unreliable at any size. */
export const DENSE_CONTENT_CHARS_PER_IMAGE = 28080;
export const DENSE_CONTENT_COLS = 312;
/** Bare 5×8 cell (no padding). A/B showed 5×8 beats 7×10 on dense JSON (4/5 vs 3/5 reads, 42% fewer tokens).
* Revert to {cellWBonus:2, cellHBonus:2} if misread rates rise. */
export const DENSE_RENDER_STYLE: RenderStyle = { cellWBonus: 0, cellHBonus: 0, aa: true };
/** Default columns for the static slab. 313 × 5 px + 8 px pad = 1573 px — under the 1932 px ceiling. */
const DEFAULT_COLS = 313;
/** Default columns for the static slab. 312 × 5 px + 8 px pad = 1568 px exactly the API's
* long-edge bound (313 cols = 1573 px would trigger a 0.997× resample, blurring every glyph). */
const DEFAULT_COLS = 312;
/** Horizontal padding (left + right each), px. Exported for transform.ts token-cost math. */
export const PAD_X = 4;
/** Vertical padding (top + bottom each), px. Exported for transform.ts token-cost math. */
@@ -732,9 +737,9 @@ export async function renderTextToPngs(
// OCR column ordering is the risk — gated behind an opt-in flag pending empirical eval.
const GUTTER_CELLS = 4;
// Width is capped at 1932 px, not 2000 px: a 1932-tall page = 69 patches, so width must
// also be ≤69 patches (4761 tokens ≤ 4784). A 2000-px width → 72×69 = 4968 > 4784 → REJECTED.
const MAX_WIDTH_PX = 1932;
// Width hard-capped at the API's long-edge bound: anything wider is resampled server-side
// (measured 2026-07-01: fit-within 1568-edge AND ~1.15 MP, then ≈px/750 billing).
const MAX_WIDTH_PX = 1568;
const GUTTER_DIVIDER_INK = 64; // pre-invert → 191 post-invert: light gray column separator
const GUTTER_DIVIDER_INSET_PX = 2; // keep divider clear of padding rows