@oh-my-pi/snapcompact 18.1.22 → 18.2.1
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 +6 -0
- package/THIRD-PARTY-NOTICES.txt +54 -25
- package/dist/types/snapcompact.d.ts +10 -0
- package/package.json +6 -6
- package/src/snapcompact.ts +100 -34
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [18.2.1] - 2026-09-15
|
|
6
|
+
|
|
7
|
+
### Changed
|
|
8
|
+
|
|
9
|
+
- `historyBlocks()` now resolves persisted frame payloads lazily, keeps the newest frames within a byte budget, and drops unresolved blob references instead of sending them to providers ([#10227](https://github.com/can1357/oh-my-pi/pull/10227) by [@lemonleks](https://github.com/lemonleks)).
|
|
10
|
+
|
|
5
11
|
## [18.1.18] - 2026-09-11
|
|
6
12
|
|
|
7
13
|
### Fixed
|
package/THIRD-PARTY-NOTICES.txt
CHANGED
|
@@ -10843,7 +10843,7 @@ RUST RUNTIME DEPENDENCY LICENSES
|
|
|
10843
10843
|
|
|
10844
10844
|
Generated from Cargo.lock by cargo-about 0.8.2 in locked, offline, workspace, all-feature, all-target mode, then restricted using cargo metadata to normal and build edges reachable from the workspace (development-only edges are excluded). cargo-deny independently evaluates the complete all-target graph.
|
|
10845
10845
|
|
|
10846
|
-
Native Opus (
|
|
10846
|
+
Native Opus (opus/opusic-sys) and PCRE2 (pcre2/pcre2-sys) are covered by the exact crate license payloads below. inferno 0.12.8 is package-scoped as CDDL-1.0: its CDDL-covered source remains available in the locked crates.io source archive at https://crates.io/crates/inferno/0.12.8 and https://github.com/jonhoo/inferno. The full locked license text follows below.
|
|
10847
10847
|
|
|
10848
10848
|
-------------------------------------------------------------------------------
|
|
10849
10849
|
License: Apache License 2.0 (Apache-2.0)
|
|
@@ -12509,6 +12509,58 @@ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
|
12509
12509
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
12510
12510
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
12511
12511
|
|
|
12512
|
+
-------------------------------------------------------------------------------
|
|
12513
|
+
License: BSD 3-Clause "New" or "Revised" License (BSD-3-Clause)
|
|
12514
|
+
Used by:
|
|
12515
|
+
- opusic-sys 0.7.5 (https://github.com/DoumanAsh/opusic-sys)
|
|
12516
|
+
|
|
12517
|
+
Full license text:
|
|
12518
|
+
|
|
12519
|
+
Copyright 2001-2023 Xiph.Org, Skype Limited, Octasic,
|
|
12520
|
+
Jean-Marc Valin, Timothy B. Terriberry,
|
|
12521
|
+
CSIRO, Gregory Maxwell, Mark Borgerding,
|
|
12522
|
+
Erik de Castro Lopo, Mozilla, Amazon
|
|
12523
|
+
|
|
12524
|
+
Redistribution and use in source and binary forms, with or without
|
|
12525
|
+
modification, are permitted provided that the following conditions
|
|
12526
|
+
are met:
|
|
12527
|
+
|
|
12528
|
+
- Redistributions of source code must retain the above copyright
|
|
12529
|
+
notice, this list of conditions and the following disclaimer.
|
|
12530
|
+
|
|
12531
|
+
- Redistributions in binary form must reproduce the above copyright
|
|
12532
|
+
notice, this list of conditions and the following disclaimer in the
|
|
12533
|
+
documentation and/or other materials provided with the distribution.
|
|
12534
|
+
|
|
12535
|
+
- Neither the name of Internet Society, IETF or IETF Trust, nor the
|
|
12536
|
+
names of specific contributors, may be used to endorse or promote
|
|
12537
|
+
products derived from this software without specific prior written
|
|
12538
|
+
permission.
|
|
12539
|
+
|
|
12540
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
12541
|
+
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
12542
|
+
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
12543
|
+
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
|
12544
|
+
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
12545
|
+
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
12546
|
+
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
12547
|
+
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
12548
|
+
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
12549
|
+
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
12550
|
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
12551
|
+
|
|
12552
|
+
Opus is subject to the royalty-free patent licenses which are
|
|
12553
|
+
specified at:
|
|
12554
|
+
|
|
12555
|
+
Xiph.Org Foundation:
|
|
12556
|
+
https://datatracker.ietf.org/ipr/1524/
|
|
12557
|
+
|
|
12558
|
+
Microsoft Corporation:
|
|
12559
|
+
https://datatracker.ietf.org/ipr/1914/
|
|
12560
|
+
|
|
12561
|
+
Broadcom Corporation:
|
|
12562
|
+
https://datatracker.ietf.org/ipr/1526/
|
|
12563
|
+
|
|
12512
12564
|
-------------------------------------------------------------------------------
|
|
12513
12565
|
License: BSD 3-Clause "New" or "Revised" License (BSD-3-Clause)
|
|
12514
12566
|
Used by:
|
|
@@ -13290,29 +13342,6 @@ jurisdiction of the Federal Courts of the Northern District of
|
|
|
13290
13342
|
California and the state courts of the State of California, with
|
|
13291
13343
|
venue lying in Santa Clara County, California.
|
|
13292
13344
|
|
|
13293
|
-
-------------------------------------------------------------------------------
|
|
13294
|
-
License: ISC License (ISC)
|
|
13295
|
-
Used by:
|
|
13296
|
-
- audiopus_sys 0.2.2 (https://github.com/lakelezz/audiopus_sys.git)
|
|
13297
|
-
|
|
13298
|
-
Full license text:
|
|
13299
|
-
|
|
13300
|
-
ISC License
|
|
13301
|
-
|
|
13302
|
-
Copyright (c) 2019, Lakelezz
|
|
13303
|
-
|
|
13304
|
-
Permission to use, copy, modify, and/or distribute this software for any
|
|
13305
|
-
purpose with or without fee is hereby granted, provided that the above
|
|
13306
|
-
copyright notice and this permission notice appear in all copies.
|
|
13307
|
-
|
|
13308
|
-
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
13309
|
-
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
13310
|
-
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
13311
|
-
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
13312
|
-
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
13313
|
-
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
13314
|
-
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
13315
|
-
|
|
13316
13345
|
-------------------------------------------------------------------------------
|
|
13317
13346
|
License: ISC License (ISC)
|
|
13318
13347
|
Used by:
|
|
@@ -18734,7 +18763,7 @@ DEALINGS IN THE SOFTWARE.
|
|
|
18734
18763
|
-------------------------------------------------------------------------------
|
|
18735
18764
|
License: MIT License (MIT)
|
|
18736
18765
|
Used by:
|
|
18737
|
-
- opus 0.
|
|
18766
|
+
- opus 0.4.0 (https://github.com/SpaceManiac/opus-rs)
|
|
18738
18767
|
|
|
18739
18768
|
Full license text:
|
|
18740
18769
|
|
|
@@ -566,10 +566,20 @@ export declare function stripPreservedArchive(preserveData: Record<string, unkno
|
|
|
566
566
|
export declare function archiveSourceText(archive: Archive): string | undefined;
|
|
567
567
|
/** Build the text used to choose and preflight a font-aware snapcompact shape. */
|
|
568
568
|
export declare function renderabilityProbeText(serialized: string, previousPreserveData?: Record<string, unknown>, previousSummary?: string): string;
|
|
569
|
+
/** A frame payload that can be priced before it is materialized. */
|
|
570
|
+
export interface LazyFrameData {
|
|
571
|
+
readonly bytes: number;
|
|
572
|
+
read(): string;
|
|
573
|
+
}
|
|
569
574
|
/** Options for reconstructing a persisted snapcompact archive into prompt blocks. */
|
|
570
575
|
export interface HistoryBlockOptions {
|
|
571
576
|
/** Hard cap on image base64 bytes attached to one rebuilt provider request. */
|
|
572
577
|
maxFrameDataBytes?: number;
|
|
578
|
+
/**
|
|
579
|
+
* Price and resolve a frame payload. Called in newest-first budget order.
|
|
580
|
+
* Returning `undefined` drops a missing payload.
|
|
581
|
+
*/
|
|
582
|
+
resolveFrameData?: (data: string) => LazyFrameData | undefined;
|
|
573
583
|
}
|
|
574
584
|
/** Convert archive frames into LLM image blocks (oldest first). */
|
|
575
585
|
export declare function images(archive: Archive): ImageContent[];
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@oh-my-pi/snapcompact",
|
|
4
|
-
"version": "18.1
|
|
4
|
+
"version": "18.2.1",
|
|
5
5
|
"description": "Bitmap-frame context compression for vision-capable LLMs",
|
|
6
6
|
"homepage": "https://omp.sh",
|
|
7
7
|
"author": "Stencil Labs, Inc.",
|
|
@@ -31,11 +31,11 @@
|
|
|
31
31
|
"fmt": "oxfmt --no-error-on-unmatched-pattern 'src/**/*.{ts,tsx}' '{test,bench,examples,scripts}/**/*.ts' '*.ts'"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@oh-my-pi/pi-ai": "18.1
|
|
35
|
-
"@oh-my-pi/pi-catalog": "18.1
|
|
36
|
-
"@oh-my-pi/pi-natives": "18.1
|
|
37
|
-
"@oh-my-pi/pi-utils": "18.1
|
|
38
|
-
"@oh-my-pi/pi-wire": "18.1
|
|
34
|
+
"@oh-my-pi/pi-ai": "18.2.1",
|
|
35
|
+
"@oh-my-pi/pi-catalog": "18.2.1",
|
|
36
|
+
"@oh-my-pi/pi-natives": "18.2.1",
|
|
37
|
+
"@oh-my-pi/pi-utils": "18.2.1",
|
|
38
|
+
"@oh-my-pi/pi-wire": "18.2.1"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@types/bun": "^1.3.14"
|
package/src/snapcompact.ts
CHANGED
|
@@ -1769,10 +1769,21 @@ export function renderabilityProbeText(
|
|
|
1769
1769
|
return serialized;
|
|
1770
1770
|
}
|
|
1771
1771
|
|
|
1772
|
+
/** A frame payload that can be priced before it is materialized. */
|
|
1773
|
+
export interface LazyFrameData {
|
|
1774
|
+
readonly bytes: number;
|
|
1775
|
+
read(): string;
|
|
1776
|
+
}
|
|
1777
|
+
|
|
1772
1778
|
/** Options for reconstructing a persisted snapcompact archive into prompt blocks. */
|
|
1773
1779
|
export interface HistoryBlockOptions {
|
|
1774
1780
|
/** Hard cap on image base64 bytes attached to one rebuilt provider request. */
|
|
1775
1781
|
maxFrameDataBytes?: number;
|
|
1782
|
+
/**
|
|
1783
|
+
* Price and resolve a frame payload. Called in newest-first budget order.
|
|
1784
|
+
* Returning `undefined` drops a missing payload.
|
|
1785
|
+
*/
|
|
1786
|
+
resolveFrameData?: (data: string) => LazyFrameData | undefined;
|
|
1776
1787
|
}
|
|
1777
1788
|
|
|
1778
1789
|
function formatFrameDataBytes(bytes: number): string {
|
|
@@ -1781,38 +1792,105 @@ function formatFrameDataBytes(bytes: number): string {
|
|
|
1781
1792
|
return `${bytes} B`;
|
|
1782
1793
|
}
|
|
1783
1794
|
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1795
|
+
/**
|
|
1796
|
+
* Prefix of an externalized frame payload (see the session blob store).
|
|
1797
|
+
*
|
|
1798
|
+
* A frame persisted by a recent session holds this reference rather than
|
|
1799
|
+
* base64, so a caller that never supplies `resolveFrameData` would otherwise
|
|
1800
|
+
* hand the reference string to the provider as image data. Dropping the frame
|
|
1801
|
+
* is the safe failure: a missing picture beats a rejected request.
|
|
1802
|
+
*/
|
|
1803
|
+
const BLOB_REFERENCE_PREFIX = "blob:sha256:";
|
|
1804
|
+
|
|
1805
|
+
function isUnresolvedBlobReference(data: string): boolean {
|
|
1806
|
+
return data.startsWith(BLOB_REFERENCE_PREFIX);
|
|
1807
|
+
}
|
|
1808
|
+
|
|
1809
|
+
/** One reconstructed slot: a usable frame, an unavailable gap, or a byte-budget gap. */
|
|
1810
|
+
type FrameSlot = { frame: Frame } | { unavailable: true } | { omittedBytes: number };
|
|
1811
|
+
|
|
1812
|
+
/**
|
|
1813
|
+
* Price every frame newest-first and retain only payloads that fit the byte
|
|
1814
|
+
* budget. Gap slots preserve the original chronology without materializing
|
|
1815
|
+
* rejected payloads.
|
|
1816
|
+
*/
|
|
1817
|
+
function imagesWithinBudget(archive: Archive, options: HistoryBlockOptions): FrameSlot[] {
|
|
1818
|
+
const { maxFrameDataBytes, resolveFrameData } = options;
|
|
1819
|
+
const hasUnresolvedReference = archive.frames.some(frame => isUnresolvedBlobReference(frame.data));
|
|
1820
|
+
if (maxFrameDataBytes === undefined && !resolveFrameData && !hasUnresolvedReference) {
|
|
1821
|
+
return archive.frames.map(frame => ({ frame }));
|
|
1790
1822
|
}
|
|
1791
1823
|
|
|
1792
1824
|
let usedBytes = 0;
|
|
1793
|
-
|
|
1794
|
-
let omittedBytes = 0;
|
|
1795
|
-
const keptNewestFirst: Frame[] = [];
|
|
1825
|
+
const newestFirst: FrameSlot[] = [];
|
|
1796
1826
|
for (let index = archive.frames.length - 1; index >= 0; index--) {
|
|
1797
1827
|
const frame = archive.frames[index];
|
|
1798
1828
|
if (!frame) continue;
|
|
1799
|
-
const
|
|
1800
|
-
if (
|
|
1801
|
-
|
|
1802
|
-
|
|
1829
|
+
const lazy = resolveFrameData?.(frame.data);
|
|
1830
|
+
if (!lazy && (resolveFrameData || isUnresolvedBlobReference(frame.data))) {
|
|
1831
|
+
newestFirst.push({ unavailable: true });
|
|
1832
|
+
continue;
|
|
1833
|
+
}
|
|
1834
|
+
const bytes = lazy ? lazy.bytes : frame.data.length;
|
|
1835
|
+
if (maxFrameDataBytes !== undefined && usedBytes + bytes > maxFrameDataBytes) {
|
|
1836
|
+
newestFirst.push({ omittedBytes: bytes });
|
|
1803
1837
|
continue;
|
|
1804
1838
|
}
|
|
1805
1839
|
usedBytes += bytes;
|
|
1806
|
-
|
|
1840
|
+
newestFirst.push({ frame: lazy ? { ...frame, data: lazy.read() } : frame });
|
|
1807
1841
|
}
|
|
1808
|
-
|
|
1809
|
-
return
|
|
1842
|
+
newestFirst.reverse();
|
|
1843
|
+
return newestFirst;
|
|
1844
|
+
}
|
|
1845
|
+
|
|
1846
|
+
/** Collapse a run of unavailable frames into one in-place gap marker. */
|
|
1847
|
+
function unavailableFrameNotice(count: number): string {
|
|
1848
|
+
return `-------------- ${count.toLocaleString()} archived image frame${count === 1 ? "" : "s"} unavailable here --------------`;
|
|
1849
|
+
}
|
|
1850
|
+
|
|
1851
|
+
/** Blocks for the imaged middle, with both gap causes kept in chronological position. */
|
|
1852
|
+
function frameBlocks(slots: FrameSlot[]): (TextContent | ImageContent)[] {
|
|
1853
|
+
const blocks: (TextContent | ImageContent)[] = [];
|
|
1854
|
+
let pendingGap: "unavailable" | "budget" | undefined;
|
|
1855
|
+
let pendingFrames = 0;
|
|
1856
|
+
let pendingBytes = 0;
|
|
1857
|
+
const flushGap = (): void => {
|
|
1858
|
+
if (!pendingGap) return;
|
|
1859
|
+
blocks.push({
|
|
1860
|
+
type: "text",
|
|
1861
|
+
text:
|
|
1862
|
+
pendingGap === "unavailable"
|
|
1863
|
+
? unavailableFrameNotice(pendingFrames)
|
|
1864
|
+
: omittedFrameNotice(pendingFrames, pendingBytes),
|
|
1865
|
+
});
|
|
1866
|
+
pendingGap = undefined;
|
|
1867
|
+
pendingFrames = 0;
|
|
1868
|
+
pendingBytes = 0;
|
|
1869
|
+
};
|
|
1870
|
+
for (const slot of slots) {
|
|
1871
|
+
if ("frame" in slot) {
|
|
1872
|
+
flushGap();
|
|
1873
|
+
blocks.push(...images({ frames: [slot.frame] } as Archive));
|
|
1874
|
+
continue;
|
|
1875
|
+
}
|
|
1876
|
+
const gap = "unavailable" in slot ? "unavailable" : "budget";
|
|
1877
|
+
if (pendingGap && pendingGap !== gap) flushGap();
|
|
1878
|
+
pendingGap = gap;
|
|
1879
|
+
pendingFrames++;
|
|
1880
|
+
if ("omittedBytes" in slot) pendingBytes += slot.omittedBytes;
|
|
1881
|
+
}
|
|
1882
|
+
flushGap();
|
|
1883
|
+
return blocks;
|
|
1810
1884
|
}
|
|
1811
1885
|
|
|
1812
1886
|
function omittedFrameNotice(omittedFrames: number, omittedBytes: number): string {
|
|
1887
|
+
const budgetNote =
|
|
1888
|
+
omittedBytes > 0
|
|
1889
|
+
? ` ${formatFrameDataBytes(omittedBytes)} of base64 exceeded the per-request snapcompact payload budget.`
|
|
1890
|
+
: "";
|
|
1813
1891
|
return [
|
|
1814
1892
|
"-------------- snapcompact image middle omitted",
|
|
1815
|
-
`${omittedFrames.toLocaleString()} archived image frame${omittedFrames === 1 ? "" : "s"}
|
|
1893
|
+
`${omittedFrames.toLocaleString()} archived image frame${omittedFrames === 1 ? "" : "s"} could not be included.${budgetNote} The compacted summary and visible text edges remain available.`,
|
|
1816
1894
|
"--------------",
|
|
1817
1895
|
].join("\n");
|
|
1818
1896
|
}
|
|
@@ -1832,26 +1910,14 @@ export function images(archive: Archive): ImageContent[] {
|
|
|
1832
1910
|
* instead of persisted on the session entry. */
|
|
1833
1911
|
export function historyBlocks(archive: Archive, options: HistoryBlockOptions = {}): (TextContent | ImageContent)[] {
|
|
1834
1912
|
const blocks: (TextContent | ImageContent)[] = [];
|
|
1835
|
-
const
|
|
1836
|
-
const hasImages =
|
|
1837
|
-
const hasOmittedImages =
|
|
1913
|
+
const middle = frameBlocks(imagesWithinBudget(archive, options));
|
|
1914
|
+
const hasImages = middle.some(block => block.type === "image");
|
|
1915
|
+
const hasOmittedImages = middle.some(block => block.type === "text");
|
|
1838
1916
|
if (archive.textHead) {
|
|
1839
|
-
const suffix = hasImages
|
|
1840
|
-
? "\n-------------- imaged middle below\n"
|
|
1841
|
-
: hasOmittedImages
|
|
1842
|
-
? `\n${omittedFrameNotice(budgeted.omittedFrames, budgeted.omittedBytes)}\n`
|
|
1843
|
-
: "";
|
|
1917
|
+
const suffix = hasImages ? "\n-------------- imaged middle below\n" : "";
|
|
1844
1918
|
blocks.push({ type: "text", text: elideDataUrls(toPlainText(archive.textHead), "archive") + suffix });
|
|
1845
|
-
} else if (hasOmittedImages && !hasImages) {
|
|
1846
|
-
blocks.push({ type: "text", text: omittedFrameNotice(budgeted.omittedFrames, budgeted.omittedBytes) });
|
|
1847
|
-
}
|
|
1848
|
-
// Omitted frames are the OLDEST archived images: the byte budget keeps the
|
|
1849
|
-
// newest tail frames, so the gap notice precedes the kept images to keep the
|
|
1850
|
-
// reconstructed blocks oldest-to-newest.
|
|
1851
|
-
if (hasImages && hasOmittedImages) {
|
|
1852
|
-
blocks.push({ type: "text", text: omittedFrameNotice(budgeted.omittedFrames, budgeted.omittedBytes) });
|
|
1853
1919
|
}
|
|
1854
|
-
blocks.push(...
|
|
1920
|
+
blocks.push(...middle);
|
|
1855
1921
|
if (archive.textTail) {
|
|
1856
1922
|
const prefix = hasImages
|
|
1857
1923
|
? "-------------- imaged middle above\n"
|