@oh-my-pi/snapcompact 15.12.4 → 15.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +22 -33
- package/dist/types/snapcompact.d.ts +56 -32
- package/package.json +4 -4
- package/src/snapcompact.ts +69 -45
package/CHANGELOG.md
CHANGED
|
@@ -2,16 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
### Changed
|
|
8
|
-
|
|
9
|
-
- `serializeConversation` now skips tool call/result pairs whose result is flagged contextually useless (`useless: true`, non-error), so archived frames stop carrying zero-match searches and timed-out waits
|
|
5
|
+
### Breaking Changes
|
|
10
6
|
|
|
11
|
-
|
|
7
|
+
- Renamed every export to drop the `snapcompact`/`Snapcompact`/`SNAPCOMPACT_` qualifier — the package is meant to be consumed via `import * as snapcompact from "@oh-my-pi/snapcompact"`. Functions: `snapcompactCompact` → `compact`, `renderSnapcompactFrame` → `render`, `snapcompactGeometry` → `geometry`, `normalizeForSnapcompact` → `normalize`, `serializeSnapcompactConversation` → `serializeConversation`, `snapcompactImages` → `images`, `getPreservedSnapcompactArchive` → `getPreservedArchive`, `isSnapcompactShape` → `isShape`, `resolveSnapcompactShape` → `resolveShape`, `createSnapcompactFileOps` → `createFileOps`, `computeSnapcompactFileLists` → `computeFileLists`, `upsertSnapcompactFileOperations` → `upsertFileOperations`. Types: `SnapcompactShape` → `Shape`, `SnapcompactFrame` → `Frame`, `SnapcompactArchive` → `Archive`, `SnapcompactGeometry` → `Geometry`, `SnapcompactOptions` → `Options`, `SnapcompactSerializeOptions` → `SerializeOptions`, `SnapcompactFileOperations` → `FileOperations`, `SnapcompactCompactionDetails`/`Preparation`/`Result` → `CompactionDetails`/`CompactionPreparation`/`CompactionResult`, `SnapcompactConvertToLlm` → `ConvertToLlm`. Constants: `SNAPCOMPACT_X` → `X` (`SHAPES`, `FRAME_SIZE`, `MAX_FRAMES`, `FRAME_TOKEN_ESTIMATE`, `PRESERVE_KEY`, `TOOL_RESULT_MAX_CHARS`, `TOOL_ARG_MAX_CHARS`, `TOOL_CALL_MAX_CHARS`, `TRUNCATE_HEAD_RATIO`, `DIM_ON`, `DIM_OFF`).
|
|
8
|
+
- Changed `renderSnapcompactFrame` output from `png: Uint8Array` to `data: string` base64, requiring consumers to read frame payloads from `frame.data`
|
|
12
9
|
|
|
13
10
|
### Added
|
|
14
11
|
|
|
12
|
+
- Added two spacing-tuned frame variants to `SHAPE_VARIANTS`: `8on22-bw` (8x13 glyphs on a 22px pitch — extra line spacing) and `11on16-bw` (8x13 glyphs on an 11px advance — extra letter spacing). Both pin the indexed `stretch: false` path, so the native renderer draws natural-size glyphs on the padded cell box (the Rust path already advances by `cellWidth` and the new variants validate horizontal padding)
|
|
15
13
|
- Added `SHAPE_VARIANTS`, the catalog of research-eval frame variants the native renderer reproduces faithfully (`8x8r`/`8x8u`/`6x6u`/`5x8` × `sent`/`bw`), with `ShapeVariantName`, `SHAPE_VARIANT_NAMES`, and the `isShapeVariantName` guard
|
|
16
14
|
- `resolveShape(api, variant?)` now accepts an explicit variant name (or `"auto"`); forced variants keep their geometry but are re-priced for the target provider's image billing (token estimate and OpenAI `original` detail hint)
|
|
17
15
|
- Added the six research-eval winning frame variants to `SHAPE_VARIANTS`: `6x12-dim` (Claude fable), `8x13-bw` (Opus), `8on16-bw` (GPT grid runner-up), `doc-8on16-bw` (GPT), `doc-8on16-sent` (GLM), and `doc-8on16-sent-dim` (Gemini/Kimi), backed by new `Shape` fields `stretch` (disable Lanczos stretch: natural glyphs on a larger cell pitch), `columns` (two word-wrapped newspaper columns), `stopwordDim`, and the X.org `6x12`/`8x13` fonts
|
|
@@ -20,34 +18,7 @@
|
|
|
20
18
|
- `resolveShape` now also resolves an ideal **frame size** per model line, and billing estimates come from verified per-family formulas instead of flat 1568px constants: Anthropic bills 28px patches capped at 4,784 visual tokens (+5% margin), Gemini 3.x bills a fixed 1,120-token `media_resolution` budget per image at any pixel size, and OpenAI bills 32px patches × 1.2 under the 10,000-patch `detail: "original"` budget. High-res Claude lines (Opus 4.7+, Fable, Mythos — native 2576px-edge ingestion) get 1932px frames (same recall and cost, a third fewer frames); Gemini gets 2048px frames (+70% chars per frame at the same bill); GPT and Kimi stay at 1568px (area-proportional billing and a model-side 1792px processor cap, respectively). `idealShapeVariant` now returns an `IdealShape` (`{ variant, frameSize? }`)
|
|
21
19
|
- Added per-provider image-count budgets: `PROVIDER_IMAGE_BUDGETS`, `DEFAULT_PROVIDER_IMAGE_BUDGET`, `providerImageBudget()`, and `providerFrameBudget()` (the image budget clamped to `MAX_FRAMES`). OpenRouter is capped at its measured hard limit of 8 images per request (excess images are silently dropped with no error); unknown providers get a safe floor of 5
|
|
22
20
|
- Added `Archive.textTail`: archive content past the frame budget is no longer dropped — `compact()` stops rendering at the budget and keeps the newest unframed slice as verbatim text on the summary (capped at two frame capacities with middle elision, counted into `truncatedChars` when elided). The tail persists in `preserveData` and is folded back into frames by the next compaction
|
|
23
|
-
|
|
24
|
-
### Changed
|
|
25
|
-
|
|
26
|
-
- Frames are no longer padded to a square: the native renderer clips each PNG's height to the text rows actually printed, so a partially filled frame (typically the newest) bills only the pixel rows it uses
|
|
27
|
-
- **Changed the OpenAI default shape from `6x6u-sent` to `8on16-bw`.** A production-regime mono eval (gpt-5.5, the full 800k-char SQuAD flow in one request, n=50) scored the old dense default f1 .602 vs .851 for `8on16-bw` rendered by the production pipeline, at near-equal total cost (the dense cells burned the frame savings on reasoning tokens); chunked exp14 had already scored `8on16-bw` .906. `SHAPES.openaiDense` is renamed to `SHAPES.openai`
|
|
28
|
-
- **Changed the Google default shape from `8x8r-sent` to `doc-8on16-sent-dim`.** Production-rendered mono eval on gemini-3.5-flash (400k chars, one request, n=25): f1 .900 vs .853 for the repeated grid at lower cost, agreeing with the chunked round-2 winner
|
|
29
|
-
- **Changed the Anthropic default shape from `8x8r-bw` to `6x12-dim`.** Production mono eval on claude-fable (400k chars, one request, n=25): f1 .840 vs .877 for the repeated grid — within noise — at 37% lower cost (12 frames instead of 21 per 400k chars), with clean completions in every probe; opus reads the same trade (.800 vs .833 at 42% lower cost)
|
|
30
|
-
- `normalize()` now keeps line structure: whitespace runs containing a line break collapse to `NEWLINE_GLYPH` (U+2588 FULL BLOCK, drawn by the native renderer as a pitch-black cell one character wide) instead of a plain space; leading/trailing breaks are trimmed, and the frame-reading prompt explains the marker
|
|
31
|
-
- `normalize()` now skips characters the fonts cannot render instead of printing `?` blanks: whole ANSI escape sequences are stripped, and bare control characters, zero-width format characters (ZWSP, BOM, directional marks), combining marks, and lone surrogates are dropped without occupying a cell; `?` remains the fallback for unsupported graphic characters only
|
|
32
|
-
|
|
33
|
-
## [15.11.4] - 2026-06-12
|
|
34
|
-
|
|
35
|
-
### Breaking Changes
|
|
36
|
-
|
|
37
|
-
- Renamed every export to drop the `snapcompact`/`Snapcompact`/`SNAPCOMPACT_` qualifier — the package is meant to be consumed via `import * as snapcompact from "@oh-my-pi/snapcompact"`. Functions: `snapcompactCompact` → `compact`, `renderSnapcompactFrame` → `render`, `snapcompactGeometry` → `geometry`, `normalizeForSnapcompact` → `normalize`, `serializeSnapcompactConversation` → `serializeConversation`, `snapcompactImages` → `images`, `getPreservedSnapcompactArchive` → `getPreservedArchive`, `isSnapcompactShape` → `isShape`, `resolveSnapcompactShape` → `resolveShape`, `createSnapcompactFileOps` → `createFileOps`, `computeSnapcompactFileLists` → `computeFileLists`, `upsertSnapcompactFileOperations` → `upsertFileOperations`. Types: `SnapcompactShape` → `Shape`, `SnapcompactFrame` → `Frame`, `SnapcompactArchive` → `Archive`, `SnapcompactGeometry` → `Geometry`, `SnapcompactOptions` → `Options`, `SnapcompactSerializeOptions` → `SerializeOptions`, `SnapcompactFileOperations` → `FileOperations`, `SnapcompactCompactionDetails`/`Preparation`/`Result` → `CompactionDetails`/`CompactionPreparation`/`CompactionResult`, `SnapcompactConvertToLlm` → `ConvertToLlm`. Constants: `SNAPCOMPACT_X` → `X` (`SHAPES`, `FRAME_SIZE`, `MAX_FRAMES`, `FRAME_TOKEN_ESTIMATE`, `PRESERVE_KEY`, `TOOL_RESULT_MAX_CHARS`, `TOOL_ARG_MAX_CHARS`, `TOOL_CALL_MAX_CHARS`, `TRUNCATE_HEAD_RATIO`, `DIM_ON`, `DIM_OFF`).
|
|
38
|
-
|
|
39
|
-
### Added
|
|
40
|
-
|
|
41
21
|
- Added `renderMany()` for paging arbitrary text into snapcompact PNG frames as LLM image blocks, and `frames()` for predicting the frame count without rendering
|
|
42
|
-
|
|
43
|
-
## [15.11.0] - 2026-06-10
|
|
44
|
-
|
|
45
|
-
### Breaking Changes
|
|
46
|
-
|
|
47
|
-
- Changed `renderSnapcompactFrame` output from `png: Uint8Array` to `data: string` base64, requiring consumers to read frame payloads from `frame.data`
|
|
48
|
-
|
|
49
|
-
### Added
|
|
50
|
-
|
|
51
22
|
- Added new serialization options `toolResultMaxChars`, `toolArgMaxChars`, `toolCallMaxChars`, `truncateHeadRatio`, and `dimToolResults` to `snapcompactCompact`/`serializeSnapcompactConversation` so callers can tune how tool results and arguments are archived
|
|
52
23
|
- Added exported default constants `SNAPCOMPACT_TOOL_RESULT_MAX_CHARS`, `SNAPCOMPACT_TOOL_ARG_MAX_CHARS`, `SNAPCOMPACT_TOOL_CALL_MAX_CHARS`, and `SNAPCOMPACT_TRUNCATE_HEAD_RATIO` for reuse when configuring truncation limits
|
|
53
24
|
- Added provider-specific snapcompact frame-shape presets and shape helpers (`SNAPCOMPACT_SHAPES`, `resolveSnapcompactShape`, `isSnapcompactShape`) so callers can consistently select validated image-frame geometry for archive renders
|
|
@@ -58,6 +29,14 @@
|
|
|
58
29
|
|
|
59
30
|
### Changed
|
|
60
31
|
|
|
32
|
+
- **Changed the per-provider default shapes to the spacing-tuned cells.** The previous shapes were tuned on the SQuAD *prose* eval, where dense cells won; a new tool-result legibility benchmark (`research/toolbench.py` — real `search`/`read`/`find` output with structure-sensitive QA) showed the prose-era density erases the line numbers and indentation that code/search output depends on. Anthropic moves from `6x12-dim` to `11on16-bw` (opus-4.8 f1 .806 vs .755 for plain `8on16-bw` and .351 for `6x12-dim`, which fell below the OCR ~16px/char floor and abstained); OpenAI and Google move to `8on22-bw` (gemini-3.5-flash f1 .934 vs .807 for `8on16-bw` and .287 for `doc-8on16-sent-dim`; same leading win on gpt-5.5/gpt-5.4-mini). Kimi and GLM keep their measured `8on16-bw`. The bigger cells pack fewer chars per frame, so inline frame-swapping now breaks even at a larger tool-result size
|
|
33
|
+
- `serializeConversation` now skips tool call/result pairs whose result is flagged contextually useless (`useless: true`, non-error), so archived frames stop carrying zero-match searches and timed-out waits
|
|
34
|
+
- Frames are no longer padded to a square: the native renderer clips each PNG's height to the text rows actually printed, so a partially filled frame (typically the newest) bills only the pixel rows it uses
|
|
35
|
+
- **Changed the OpenAI default shape from `6x6u-sent` to `8on16-bw`.** A production-regime mono eval (gpt-5.5, the full 800k-char SQuAD flow in one request, n=50) scored the old dense default f1 .602 vs .851 for `8on16-bw` rendered by the production pipeline, at near-equal total cost (the dense cells burned the frame savings on reasoning tokens); chunked exp14 had already scored `8on16-bw` .906. `SHAPES.openaiDense` is renamed to `SHAPES.openai`
|
|
36
|
+
- **Changed the Google default shape from `8x8r-sent` to `doc-8on16-sent-dim`.** Production-rendered mono eval on gemini-3.5-flash (400k chars, one request, n=25): f1 .900 vs .853 for the repeated grid at lower cost, agreeing with the chunked round-2 winner
|
|
37
|
+
- **Changed the Anthropic default shape from `8x8r-bw` to `6x12-dim`.** Production mono eval on claude-fable (400k chars, one request, n=25): f1 .840 vs .877 for the repeated grid — within noise — at 37% lower cost (12 frames instead of 21 per 400k chars), with clean completions in every probe; opus reads the same trade (.800 vs .833 at 42% lower cost)
|
|
38
|
+
- `normalize()` now keeps line structure: whitespace runs containing a line break collapse to `NEWLINE_GLYPH` (U+2588 FULL BLOCK, drawn by the native renderer as a pitch-black cell one character wide) instead of a plain space; leading/trailing breaks are trimmed, and the frame-reading prompt explains the marker
|
|
39
|
+
- `normalize()` now skips characters the fonts cannot render instead of printing `?` blanks: whole ANSI escape sequences are stripped, and bare control characters, zero-width format characters (ZWSP, BOM, directional marks), combining marks, and lone surrogates are dropped without occupying a cell; `?` remains the fallback for unsupported graphic characters only
|
|
61
40
|
- Changed truncation in archived tool output to keep both the beginning and end of long text using a configurable head/tail ratio instead of a single hard cut
|
|
62
41
|
- Changed tool-result text rendering so archived tool results are shown in dim gray ink by default and the summary prompt notes that dim text is archived tool output
|
|
63
42
|
- Changed `RenderedFrame` visible-character accounting so `chars` no longer includes invisible dim-control markers
|
|
@@ -67,3 +46,13 @@
|
|
|
67
46
|
|
|
68
47
|
- Fixed frame rendering at archive chunk boundaries to reopen dim spans when a chunk ends inside a dimmed tool-result segment
|
|
69
48
|
- Fixed message serialization to strip user- and assistant-provided dim markers so only renderer-generated dim spans can be applied
|
|
49
|
+
|
|
50
|
+
## [15.13.0] - 2026-06-14
|
|
51
|
+
|
|
52
|
+
## [15.12.1] - 2026-06-12
|
|
53
|
+
|
|
54
|
+
## [15.11.7] - 2026-06-12
|
|
55
|
+
|
|
56
|
+
## [15.11.4] - 2026-06-12
|
|
57
|
+
|
|
58
|
+
## [15.11.0] - 2026-06-10
|
|
@@ -7,25 +7,28 @@
|
|
|
7
7
|
* reader. Frames are `frameSize` wide; their height hugs the text rows
|
|
8
8
|
* actually printed, so a partially filled frame never bills blank rows.
|
|
9
9
|
*
|
|
10
|
-
* The frame shape is provider-aware
|
|
11
|
-
* (`packages/snapcompact`, 200k-token monolithic runs)
|
|
10
|
+
* The frame shape is provider-aware. Original choices came from the SQuAD
|
|
11
|
+
* prose evals (`packages/snapcompact`, 200k-token monolithic runs); the
|
|
12
|
+
* spacing choices below come from the tool-result legibility bench
|
|
13
|
+
* (`research/toolbench.py`, real search/read/find output with structure QA),
|
|
14
|
+
* which exposed that the prose-tuned dense cells erase the line numbers and
|
|
15
|
+
* indentation that code/search output depends on:
|
|
12
16
|
*
|
|
13
|
-
* - **Anthropic** (`
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* - **Google** (`
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
* - **OpenAI** (`
|
|
27
|
-
*
|
|
28
|
-
* Patch billing (32px × 1.2, 10k-patch budget at `detail: "original"`) is
|
|
17
|
+
* - **Anthropic** (`11on16-bw`): 8x13 glyphs on an 11px advance (extra
|
|
18
|
+
* letter-spacing), black ink. On the tool-result bench, tracking the
|
|
19
|
+
* readable cell beat plain `8on16-bw` (opus-4.8 f1 .806 vs .755) and far
|
|
20
|
+
* beat the prior dense `6x12-dim` (.351, which fell below the OCR ~16px/char
|
|
21
|
+
* floor and abstained). Opus 4.7+/Fable/Mythos ingest high-res natively
|
|
22
|
+
* (2576px edge, 4,784 visual-token cap), so those lines get 1932px frames:
|
|
23
|
+
* same bill, fewer frames. Older Claude lines downscale past 1568px.
|
|
24
|
+
* - **Google** (`8on22-bw` @2048): 8x13 glyphs on a 22px pitch (extra line
|
|
25
|
+
* spacing), black ink. Leading lifted gemini-3.5-flash to f1 .934 vs .807
|
|
26
|
+
* for `8on16-bw` and .287 for the prior `doc-8on16-sent-dim`. Gemini 3.x
|
|
27
|
+
* bills a fixed `media_resolution` budget per image (default 1,120 tokens)
|
|
28
|
+
* regardless of pixels, so the 2048px frame carries more chars at the same
|
|
29
|
+
* bill.
|
|
30
|
+
* - **OpenAI** (`8on22-bw`): same leading win (gpt-5.5/gpt-5.4-mini). Patch
|
|
31
|
+
* billing (32px × 1.2, 10k-patch budget at `detail: "original"`) is
|
|
29
32
|
* area-proportional, so resolution cannot improve chars/$ — 1568 stays.
|
|
30
33
|
* `detail: "high"` would downgrade (2,500-patch cap); `original` is sent.
|
|
31
34
|
* - **Unknown providers** default to the Anthropic shape. Gateways can
|
|
@@ -82,9 +85,11 @@ export type ShapeGeometry = Omit<Shape, "frameTokenEstimate" | "imageDetail">;
|
|
|
82
85
|
* (redundancy coding), `6x6u` unscii Lanczos-squeezed to 6x6 (densest
|
|
83
86
|
* readable cell), `5x8` the X.org legacy font on its 2576px frame, `6x12`
|
|
84
87
|
* and `8x13` the X.org misc fonts, `8on16` 8x13 glyphs on an 8x16 cell pitch
|
|
85
|
-
* (no stretch, extra leading), `
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
+
* (no stretch, extra leading), `8on22` the same glyphs on a 22px pitch (more
|
|
89
|
+
* leading), `11on16` the same glyphs on an 11px advance (more tracking),
|
|
90
|
+
* `doc-` prefixed shapes a two-column word-wrapped newspaper layout. Ink:
|
|
91
|
+
* `sent` cycles six hues at sentence boundaries, `bw` is plain black, `-dim`
|
|
92
|
+
* suffix prints stopwords in gray.
|
|
88
93
|
*/
|
|
89
94
|
export declare const SHAPE_VARIANTS: {
|
|
90
95
|
readonly "8x8r-bw": {
|
|
@@ -177,6 +182,24 @@ export declare const SHAPE_VARIANTS: {
|
|
|
177
182
|
readonly lineRepeat: 1;
|
|
178
183
|
readonly frameSize: 1568;
|
|
179
184
|
};
|
|
185
|
+
readonly "8on22-bw": {
|
|
186
|
+
readonly font: "8x13";
|
|
187
|
+
readonly cellWidth: 8;
|
|
188
|
+
readonly cellHeight: 22;
|
|
189
|
+
readonly stretch: false;
|
|
190
|
+
readonly variant: "bw";
|
|
191
|
+
readonly lineRepeat: 1;
|
|
192
|
+
readonly frameSize: 1568;
|
|
193
|
+
};
|
|
194
|
+
readonly "11on16-bw": {
|
|
195
|
+
readonly font: "8x13";
|
|
196
|
+
readonly cellWidth: 11;
|
|
197
|
+
readonly cellHeight: 16;
|
|
198
|
+
readonly stretch: false;
|
|
199
|
+
readonly variant: "bw";
|
|
200
|
+
readonly lineRepeat: 1;
|
|
201
|
+
readonly frameSize: 1568;
|
|
202
|
+
};
|
|
180
203
|
readonly "doc-8on16-bw": {
|
|
181
204
|
readonly font: "8x13";
|
|
182
205
|
readonly cellWidth: 8;
|
|
@@ -217,19 +240,20 @@ export declare const SHAPE_VARIANT_NAMES: readonly ShapeVariantName[];
|
|
|
217
240
|
export declare function isShapeVariantName(value: unknown): value is ShapeVariantName;
|
|
218
241
|
/** Eval-validated shapes, keyed by the provider family they won on. */
|
|
219
242
|
export declare const SHAPES: {
|
|
220
|
-
/** `
|
|
221
|
-
*
|
|
222
|
-
*
|
|
223
|
-
*
|
|
243
|
+
/** `11on16-bw`: 8x13 glyphs on an 11px advance (extra tracking), black ink.
|
|
244
|
+
* Tool-result legibility bench (real search/read/find output, structure QA)
|
|
245
|
+
* on opus-4.8: f1 .806 vs .755 for plain `8on16-bw` and .351 for the prior
|
|
246
|
+
* `6x12-dim` default — letter-spacing the readable cell wins; the dense
|
|
247
|
+
* 6x12 was below the OCR ~16px/char floor and abstained. */
|
|
224
248
|
anthropic: Shape;
|
|
225
|
-
/** `
|
|
226
|
-
*
|
|
227
|
-
*
|
|
249
|
+
/** `8on22-bw`: 8x13 glyphs on a 22px pitch (extra leading), black ink.
|
|
250
|
+
* Tool-result legibility bench on gemini-3.5-flash: f1 .934 vs .807 for
|
|
251
|
+
* plain `8on16-bw` and .287 for the prior `doc-8on16-sent-dim`; the
|
|
252
|
+
* line-spacing reduces row crowding so line numbers stay legible. */
|
|
228
253
|
google: Shape;
|
|
229
|
-
/** `
|
|
230
|
-
*
|
|
231
|
-
*
|
|
232
|
-
* scored it .906. */
|
|
254
|
+
/** `8on22-bw`: 8x13 glyphs on a 22px pitch (extra leading), black ink.
|
|
255
|
+
* Same line-spacing win for OpenAI; bench on gpt-5.5/gpt-5.4-mini showed
|
|
256
|
+
* leading lifts recall on the readable cell over plain `8on16-bw`. */
|
|
233
257
|
openai: Shape;
|
|
234
258
|
/** Original 5x8 X.org shape (pre-shape-table sessions rendered this). */
|
|
235
259
|
legacy: Shape;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@oh-my-pi/snapcompact",
|
|
4
|
-
"version": "15.
|
|
4
|
+
"version": "15.13.0",
|
|
5
5
|
"description": "Bitmap-frame context compression for vision-capable LLMs",
|
|
6
6
|
"homepage": "https://omp.sh",
|
|
7
7
|
"author": "Can Boluk",
|
|
@@ -31,9 +31,9 @@
|
|
|
31
31
|
"fmt": "biome format --write ."
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@oh-my-pi/pi-ai": "15.
|
|
35
|
-
"@oh-my-pi/pi-natives": "15.
|
|
36
|
-
"@oh-my-pi/pi-utils": "15.
|
|
34
|
+
"@oh-my-pi/pi-ai": "15.13.0",
|
|
35
|
+
"@oh-my-pi/pi-natives": "15.13.0",
|
|
36
|
+
"@oh-my-pi/pi-utils": "15.13.0"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
39
|
"@types/bun": "^1.3.14"
|
package/src/snapcompact.ts
CHANGED
|
@@ -7,25 +7,28 @@
|
|
|
7
7
|
* reader. Frames are `frameSize` wide; their height hugs the text rows
|
|
8
8
|
* actually printed, so a partially filled frame never bills blank rows.
|
|
9
9
|
*
|
|
10
|
-
* The frame shape is provider-aware
|
|
11
|
-
* (`packages/snapcompact`, 200k-token monolithic runs)
|
|
10
|
+
* The frame shape is provider-aware. Original choices came from the SQuAD
|
|
11
|
+
* prose evals (`packages/snapcompact`, 200k-token monolithic runs); the
|
|
12
|
+
* spacing choices below come from the tool-result legibility bench
|
|
13
|
+
* (`research/toolbench.py`, real search/read/find output with structure QA),
|
|
14
|
+
* which exposed that the prose-tuned dense cells erase the line numbers and
|
|
15
|
+
* indentation that code/search output depends on:
|
|
12
16
|
*
|
|
13
|
-
* - **Anthropic** (`
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* - **Google** (`
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
* - **OpenAI** (`
|
|
27
|
-
*
|
|
28
|
-
* Patch billing (32px × 1.2, 10k-patch budget at `detail: "original"`) is
|
|
17
|
+
* - **Anthropic** (`11on16-bw`): 8x13 glyphs on an 11px advance (extra
|
|
18
|
+
* letter-spacing), black ink. On the tool-result bench, tracking the
|
|
19
|
+
* readable cell beat plain `8on16-bw` (opus-4.8 f1 .806 vs .755) and far
|
|
20
|
+
* beat the prior dense `6x12-dim` (.351, which fell below the OCR ~16px/char
|
|
21
|
+
* floor and abstained). Opus 4.7+/Fable/Mythos ingest high-res natively
|
|
22
|
+
* (2576px edge, 4,784 visual-token cap), so those lines get 1932px frames:
|
|
23
|
+
* same bill, fewer frames. Older Claude lines downscale past 1568px.
|
|
24
|
+
* - **Google** (`8on22-bw` @2048): 8x13 glyphs on a 22px pitch (extra line
|
|
25
|
+
* spacing), black ink. Leading lifted gemini-3.5-flash to f1 .934 vs .807
|
|
26
|
+
* for `8on16-bw` and .287 for the prior `doc-8on16-sent-dim`. Gemini 3.x
|
|
27
|
+
* bills a fixed `media_resolution` budget per image (default 1,120 tokens)
|
|
28
|
+
* regardless of pixels, so the 2048px frame carries more chars at the same
|
|
29
|
+
* bill.
|
|
30
|
+
* - **OpenAI** (`8on22-bw`): same leading win (gpt-5.5/gpt-5.4-mini). Patch
|
|
31
|
+
* billing (32px × 1.2, 10k-patch budget at `detail: "original"`) is
|
|
29
32
|
* area-proportional, so resolution cannot improve chars/$ — 1568 stays.
|
|
30
33
|
* `detail: "high"` would downgrade (2,500-patch cap); `original` is sent.
|
|
31
34
|
* - **Unknown providers** default to the Anthropic shape. Gateways can
|
|
@@ -94,9 +97,11 @@ export type ShapeGeometry = Omit<Shape, "frameTokenEstimate" | "imageDetail">;
|
|
|
94
97
|
* (redundancy coding), `6x6u` unscii Lanczos-squeezed to 6x6 (densest
|
|
95
98
|
* readable cell), `5x8` the X.org legacy font on its 2576px frame, `6x12`
|
|
96
99
|
* and `8x13` the X.org misc fonts, `8on16` 8x13 glyphs on an 8x16 cell pitch
|
|
97
|
-
* (no stretch, extra leading), `
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
+
* (no stretch, extra leading), `8on22` the same glyphs on a 22px pitch (more
|
|
101
|
+
* leading), `11on16` the same glyphs on an 11px advance (more tracking),
|
|
102
|
+
* `doc-` prefixed shapes a two-column word-wrapped newspaper layout. Ink:
|
|
103
|
+
* `sent` cycles six hues at sentence boundaries, `bw` is plain black, `-dim`
|
|
104
|
+
* suffix prints stopwords in gray.
|
|
100
105
|
*/
|
|
101
106
|
export const SHAPE_VARIANTS = {
|
|
102
107
|
"8x8r-bw": { font: "8x8", cellWidth: 8, cellHeight: 8, variant: "bw", lineRepeat: 2, frameSize: 1568 },
|
|
@@ -126,6 +131,24 @@ export const SHAPE_VARIANTS = {
|
|
|
126
131
|
lineRepeat: 1,
|
|
127
132
|
frameSize: 1568,
|
|
128
133
|
},
|
|
134
|
+
"8on22-bw": {
|
|
135
|
+
font: "8x13",
|
|
136
|
+
cellWidth: 8,
|
|
137
|
+
cellHeight: 22,
|
|
138
|
+
stretch: false,
|
|
139
|
+
variant: "bw",
|
|
140
|
+
lineRepeat: 1,
|
|
141
|
+
frameSize: 1568,
|
|
142
|
+
},
|
|
143
|
+
"11on16-bw": {
|
|
144
|
+
font: "8x13",
|
|
145
|
+
cellWidth: 11,
|
|
146
|
+
cellHeight: 16,
|
|
147
|
+
stretch: false,
|
|
148
|
+
variant: "bw",
|
|
149
|
+
lineRepeat: 1,
|
|
150
|
+
frameSize: 1568,
|
|
151
|
+
},
|
|
129
152
|
"doc-8on16-bw": {
|
|
130
153
|
font: "8x13",
|
|
131
154
|
cellWidth: 8,
|
|
@@ -223,20 +246,21 @@ function priceShape(base: ShapeGeometry, family: BillingFamily): Shape {
|
|
|
223
246
|
|
|
224
247
|
/** Eval-validated shapes, keyed by the provider family they won on. */
|
|
225
248
|
export const SHAPES = {
|
|
226
|
-
/** `
|
|
227
|
-
*
|
|
228
|
-
*
|
|
229
|
-
*
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
*
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
*
|
|
239
|
-
|
|
249
|
+
/** `11on16-bw`: 8x13 glyphs on an 11px advance (extra tracking), black ink.
|
|
250
|
+
* Tool-result legibility bench (real search/read/find output, structure QA)
|
|
251
|
+
* on opus-4.8: f1 .806 vs .755 for plain `8on16-bw` and .351 for the prior
|
|
252
|
+
* `6x12-dim` default — letter-spacing the readable cell wins; the dense
|
|
253
|
+
* 6x12 was below the OCR ~16px/char floor and abstained. */
|
|
254
|
+
anthropic: priceShape(SHAPE_VARIANTS["11on16-bw"], "anthropic"),
|
|
255
|
+
/** `8on22-bw`: 8x13 glyphs on a 22px pitch (extra leading), black ink.
|
|
256
|
+
* Tool-result legibility bench on gemini-3.5-flash: f1 .934 vs .807 for
|
|
257
|
+
* plain `8on16-bw` and .287 for the prior `doc-8on16-sent-dim`; the
|
|
258
|
+
* line-spacing reduces row crowding so line numbers stay legible. */
|
|
259
|
+
google: priceShape(SHAPE_VARIANTS["8on22-bw"], "google"),
|
|
260
|
+
/** `8on22-bw`: 8x13 glyphs on a 22px pitch (extra leading), black ink.
|
|
261
|
+
* Same line-spacing win for OpenAI; bench on gpt-5.5/gpt-5.4-mini showed
|
|
262
|
+
* leading lifts recall on the readable cell over plain `8on16-bw`. */
|
|
263
|
+
openai: priceShape(SHAPE_VARIANTS["8on22-bw"], "openai"),
|
|
240
264
|
/** Original 5x8 X.org shape (pre-shape-table sessions rendered this). */
|
|
241
265
|
legacy: priceShape(SHAPE_VARIANTS["5x8-sent"], "anthropic"),
|
|
242
266
|
} satisfies Record<string, Shape>;
|
|
@@ -271,9 +295,9 @@ export function isShape(value: unknown): value is Shape {
|
|
|
271
295
|
/** Eval-winning variant per provider family (billing fallback when the
|
|
272
296
|
* model id matches no known reader line). */
|
|
273
297
|
const FAMILY_VARIANT: Record<BillingFamily, ShapeVariantName> = {
|
|
274
|
-
anthropic: "
|
|
275
|
-
google: "
|
|
276
|
-
openai: "
|
|
298
|
+
anthropic: "11on16-bw",
|
|
299
|
+
google: "8on22-bw",
|
|
300
|
+
openai: "8on22-bw",
|
|
277
301
|
};
|
|
278
302
|
|
|
279
303
|
const FAMILY_SHAPE: Record<BillingFamily, Shape> = {
|
|
@@ -297,16 +321,16 @@ export interface IdealShape {
|
|
|
297
321
|
const MODEL_VARIANTS: readonly (readonly [RegExp, IdealShape])[] = [
|
|
298
322
|
// Opus 4.7+ and Fable/Mythos read high-res natively (2576px edge under a
|
|
299
323
|
// 4,784 visual-token cap → 1932px square sweet spot): same recall and
|
|
300
|
-
// cost as 1568, a third fewer frames
|
|
301
|
-
[/claude.*(fable|mythos)/i, { variant: "
|
|
302
|
-
[/claude-?opus-?4[.-][7-9]/i, { variant: "
|
|
324
|
+
// cost as 1568, a third fewer frames.
|
|
325
|
+
[/claude.*(fable|mythos)/i, { variant: "11on16-bw", frameSize: 1932 }],
|
|
326
|
+
[/claude-?opus-?4[.-][7-9]/i, { variant: "11on16-bw", frameSize: 1932 }],
|
|
303
327
|
// Older Claude lines downscale past 1568px — keep the safe size.
|
|
304
|
-
[/claude/i, { variant: "
|
|
328
|
+
[/claude/i, { variant: "11on16-bw" }],
|
|
305
329
|
// Gemini 3.x bills a fixed 1,120-token budget per image regardless of
|
|
306
|
-
// pixels: 2048px packs
|
|
307
|
-
[/gemini/i, { variant: "
|
|
330
|
+
// pixels: 2048px packs more chars per frame at the same bill.
|
|
331
|
+
[/gemini/i, { variant: "8on22-bw", frameSize: 2048 }],
|
|
308
332
|
// gpt-5.5 patch billing is area-proportional; 1568 is already optimal.
|
|
309
|
-
[/gpt|codex/i, { variant: "
|
|
333
|
+
[/gpt|codex/i, { variant: "8on22-bw" }],
|
|
310
334
|
// kimi's image processor downscales past 1792px (64×64 28px patches);
|
|
311
335
|
// 1568 wins on chars/$ and reads at f1 .973 (≤8 frames per request).
|
|
312
336
|
[/kimi/i, { variant: "8on16-bw" }],
|