@llmtrim/js 0.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/README.md ADDED
@@ -0,0 +1,67 @@
1
+ # llmtrim-wasm
2
+
3
+ WebAssembly/JS bindings for the [`llmtrim-core`](../llmtrim-core) compression engine, for
4
+ running the static prompt/payload compressor in a browser, Node, Bun, Deno, or a Cloudflare
5
+ Worker. No network or filesystem access.
6
+
7
+ Published to npm as **`@llmtrim/js`** (with **`@llmtrim/wasm`** as an alias that re-exports
8
+ the same package).
9
+
10
+ ## API
11
+
12
+ ```ts
13
+ import { compress } from "@llmtrim/js";
14
+
15
+ const out = compress(requestBodyJson, "openai", "agent");
16
+ // out: CompressOutput { request_json, provider, model, tokenizer_label,
17
+ // tokenizer_exact, input_tokens_before, input_tokens_after,
18
+ // frozen_input_tokens, output_shaped, stages: StageReport[] }
19
+ ```
20
+
21
+ - `provider`: `"openai" | "anthropic" | "google"`, or `undefined`/`null` to auto-detect from
22
+ the body.
23
+ - `preset`: a named workload preset (`aggressive`, `agent`, `code`, `rag`, `safe`, …), or
24
+ `undefined`/`null` for the built-in defaults. This binding never reads the environment or a
25
+ config file.
26
+
27
+ TypeScript types for `CompressOutput` and `StageReport` are generated via `tsify`, so the
28
+ `.d.ts` is fully typed (no `any`).
29
+
30
+ This build links `llmtrim-core` with `default-features = false`: the estimate tokenizer is
31
+ used (counts are approximate; savings percentages are unchanged), and code skeletonization
32
+ and image downscaling are no-ops. That keeps the bundle small (~1 MB gzipped, under the
33
+ Cloudflare Workers 3 MB free-tier cap).
34
+
35
+ ## Building
36
+
37
+ The npm package is built in CI with `wasm-pack build --target bundler` (it manages a
38
+ matching `wasm-bindgen` internally). The manual `cargo` + `wasm-bindgen` recipe below is the
39
+ equivalent for local development:
40
+
41
+ ```sh
42
+ rustup target add wasm32-unknown-unknown
43
+ cargo install wasm-bindgen-cli
44
+
45
+ # The JS-backed getrandom backend needs a rustc cfg in the environment (it cannot live in a
46
+ # repo .cargo/config.toml, which would break `cargo publish`).
47
+ RUSTFLAGS='--cfg getrandom_backend="wasm_js"' \
48
+ cargo build -p llmtrim-wasm --release --target wasm32-unknown-unknown
49
+
50
+ wasm-bindgen target/wasm32-unknown-unknown/release/llmtrim_wasm.wasm \
51
+ --out-dir pkg --target bundler # or: nodejs | web
52
+
53
+ # optional size pass
54
+ wasm-opt -O3 --enable-reference-types --enable-bulk-memory --enable-mutable-globals \
55
+ --enable-nontrapping-float-to-int --enable-sign-ext --enable-multivalue \
56
+ pkg/llmtrim_wasm_bg.wasm -o pkg/llmtrim_wasm_bg.wasm
57
+ ```
58
+
59
+ ## Smoke test
60
+
61
+ After building with `--target nodejs` into `pkg/`, run `node smoke.mjs` (it imports
62
+ `./pkg/llmtrim_wasm.js`). It exercises OpenAI and Anthropic requests, a named preset, an
63
+ error path, and non-ASCII (CJK) input.
64
+
65
+ ## License
66
+
67
+ MPL-2.0.
@@ -0,0 +1,47 @@
1
+ /* tslint:disable */
2
+ /* eslint-disable */
3
+ /**
4
+ * The result of compressing one request body.
5
+ */
6
+ export interface CompressOutput {
7
+ request_json: string;
8
+ provider: string;
9
+ model: string | undefined;
10
+ tokenizer_label: string;
11
+ tokenizer_exact: boolean;
12
+ input_tokens_before: number;
13
+ input_tokens_after: number;
14
+ frozen_input_tokens: number;
15
+ output_shaped: boolean;
16
+ stages: StageReport[];
17
+ }
18
+
19
+ /**
20
+ * What one pipeline stage did to the request, projected from
21
+ * [`llmtrim_core::pipeline::StageReport`]. `tokens_before - tokens_after` is that stage\'s
22
+ * own contribution to the input reduction.
23
+ */
24
+ export interface StageReport {
25
+ name: string;
26
+ applied: boolean;
27
+ tokens_before: number;
28
+ tokens_after: number;
29
+ note: string | undefined;
30
+ }
31
+
32
+
33
+ /**
34
+ * Compress an LLM API request body (a JSON string).
35
+ *
36
+ * - `provider`: `"openai"`, `"anthropic"`, or `"google"`; omit (`null`/`undefined`) to
37
+ * auto-detect from the body shape.
38
+ * - `preset`: a named workload preset (`aggressive`, `agent`, `code`, `rag`, `safe`, …);
39
+ * omit to use the built-in defaults. Unlike the native crate, this binding never reads
40
+ * the environment or a config file (there is none in a Worker), so the configuration
41
+ * comes only from the preset or the defaults.
42
+ *
43
+ * Returns the [`CompressOutput`] as a typed JS object (TypeScript types are generated for
44
+ * it), or throws on invalid JSON, an undetectable provider, or an unknown preset/provider
45
+ * name.
46
+ */
47
+ export function compress(input: string, provider?: string | null, preset?: string | null): CompressOutput;
@@ -0,0 +1,9 @@
1
+ /* @ts-self-types="./llmtrim_wasm.d.ts" */
2
+ import * as wasm from "./llmtrim_wasm_bg.wasm";
3
+ import { __wbg_set_wasm } from "./llmtrim_wasm_bg.js";
4
+
5
+ __wbg_set_wasm(wasm);
6
+
7
+ export {
8
+ compress
9
+ } from "./llmtrim_wasm_bg.js";
@@ -0,0 +1,228 @@
1
+ /**
2
+ * Compress an LLM API request body (a JSON string).
3
+ *
4
+ * - `provider`: `"openai"`, `"anthropic"`, or `"google"`; omit (`null`/`undefined`) to
5
+ * auto-detect from the body shape.
6
+ * - `preset`: a named workload preset (`aggressive`, `agent`, `code`, `rag`, `safe`, …);
7
+ * omit to use the built-in defaults. Unlike the native crate, this binding never reads
8
+ * the environment or a config file (there is none in a Worker), so the configuration
9
+ * comes only from the preset or the defaults.
10
+ *
11
+ * Returns the [`CompressOutput`] as a typed JS object (TypeScript types are generated for
12
+ * it), or throws on invalid JSON, an undetectable provider, or an unknown preset/provider
13
+ * name.
14
+ * @param {string} input
15
+ * @param {string | null} [provider]
16
+ * @param {string | null} [preset]
17
+ * @returns {CompressOutput}
18
+ */
19
+ export function compress(input, provider, preset) {
20
+ try {
21
+ const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
22
+ const ptr0 = passStringToWasm0(input, wasm.__wbindgen_export, wasm.__wbindgen_export2);
23
+ const len0 = WASM_VECTOR_LEN;
24
+ var ptr1 = isLikeNone(provider) ? 0 : passStringToWasm0(provider, wasm.__wbindgen_export, wasm.__wbindgen_export2);
25
+ var len1 = WASM_VECTOR_LEN;
26
+ var ptr2 = isLikeNone(preset) ? 0 : passStringToWasm0(preset, wasm.__wbindgen_export, wasm.__wbindgen_export2);
27
+ var len2 = WASM_VECTOR_LEN;
28
+ wasm.compress(retptr, ptr0, len0, ptr1, len1, ptr2, len2);
29
+ var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
30
+ var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
31
+ var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
32
+ if (r2) {
33
+ throw takeObject(r1);
34
+ }
35
+ return takeObject(r0);
36
+ } finally {
37
+ wasm.__wbindgen_add_to_stack_pointer(16);
38
+ }
39
+ }
40
+ export function __wbg_Error_ef53bc310eb298a0(arg0, arg1) {
41
+ const ret = Error(getStringFromWasm0(arg0, arg1));
42
+ return addHeapObject(ret);
43
+ }
44
+ export function __wbg_String_8564e559799eccda(arg0, arg1) {
45
+ const ret = String(getObject(arg1));
46
+ const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_export, wasm.__wbindgen_export2);
47
+ const len1 = WASM_VECTOR_LEN;
48
+ getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true);
49
+ getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true);
50
+ }
51
+ export function __wbg___wbindgen_throw_1506f2235d1bdba0(arg0, arg1) {
52
+ throw new Error(getStringFromWasm0(arg0, arg1));
53
+ }
54
+ export function __wbg_getRandomValues_3f44b700395062e5() { return handleError(function (arg0, arg1) {
55
+ globalThis.crypto.getRandomValues(getArrayU8FromWasm0(arg0, arg1));
56
+ }, arguments); }
57
+ export function __wbg_new_ce1ab61c1c2b300d() {
58
+ const ret = new Object();
59
+ return addHeapObject(ret);
60
+ }
61
+ export function __wbg_new_d90091b82fdf5b91() {
62
+ const ret = new Array();
63
+ return addHeapObject(ret);
64
+ }
65
+ export function __wbg_set_6be42768c690e380(arg0, arg1, arg2) {
66
+ getObject(arg0)[takeObject(arg1)] = takeObject(arg2);
67
+ }
68
+ export function __wbg_set_dca99999bba88a9a(arg0, arg1, arg2) {
69
+ getObject(arg0)[arg1 >>> 0] = takeObject(arg2);
70
+ }
71
+ export function __wbindgen_cast_0000000000000001(arg0) {
72
+ // Cast intrinsic for `F64 -> Externref`.
73
+ const ret = arg0;
74
+ return addHeapObject(ret);
75
+ }
76
+ export function __wbindgen_cast_0000000000000002(arg0, arg1) {
77
+ // Cast intrinsic for `Ref(String) -> Externref`.
78
+ const ret = getStringFromWasm0(arg0, arg1);
79
+ return addHeapObject(ret);
80
+ }
81
+ export function __wbindgen_cast_0000000000000003(arg0) {
82
+ // Cast intrinsic for `U64 -> Externref`.
83
+ const ret = BigInt.asUintN(64, arg0);
84
+ return addHeapObject(ret);
85
+ }
86
+ export function __wbindgen_object_clone_ref(arg0) {
87
+ const ret = getObject(arg0);
88
+ return addHeapObject(ret);
89
+ }
90
+ export function __wbindgen_object_drop_ref(arg0) {
91
+ takeObject(arg0);
92
+ }
93
+ function addHeapObject(obj) {
94
+ if (heap_next === heap.length) heap.push(heap.length + 1);
95
+ const idx = heap_next;
96
+ heap_next = heap[idx];
97
+
98
+ heap[idx] = obj;
99
+ return idx;
100
+ }
101
+
102
+ function dropObject(idx) {
103
+ if (idx < 1028) return;
104
+ heap[idx] = heap_next;
105
+ heap_next = idx;
106
+ }
107
+
108
+ function getArrayU8FromWasm0(ptr, len) {
109
+ ptr = ptr >>> 0;
110
+ return getUint8ArrayMemory0().subarray(ptr / 1, ptr / 1 + len);
111
+ }
112
+
113
+ let cachedDataViewMemory0 = null;
114
+ function getDataViewMemory0() {
115
+ if (cachedDataViewMemory0 === null || cachedDataViewMemory0.buffer.detached === true || (cachedDataViewMemory0.buffer.detached === undefined && cachedDataViewMemory0.buffer !== wasm.memory.buffer)) {
116
+ cachedDataViewMemory0 = new DataView(wasm.memory.buffer);
117
+ }
118
+ return cachedDataViewMemory0;
119
+ }
120
+
121
+ function getStringFromWasm0(ptr, len) {
122
+ return decodeText(ptr >>> 0, len);
123
+ }
124
+
125
+ let cachedUint8ArrayMemory0 = null;
126
+ function getUint8ArrayMemory0() {
127
+ if (cachedUint8ArrayMemory0 === null || cachedUint8ArrayMemory0.byteLength === 0) {
128
+ cachedUint8ArrayMemory0 = new Uint8Array(wasm.memory.buffer);
129
+ }
130
+ return cachedUint8ArrayMemory0;
131
+ }
132
+
133
+ function getObject(idx) { return heap[idx]; }
134
+
135
+ function handleError(f, args) {
136
+ try {
137
+ return f.apply(this, args);
138
+ } catch (e) {
139
+ wasm.__wbindgen_export3(addHeapObject(e));
140
+ }
141
+ }
142
+
143
+ let heap = new Array(1024).fill(undefined);
144
+ heap.push(undefined, null, true, false);
145
+
146
+ let heap_next = heap.length;
147
+
148
+ function isLikeNone(x) {
149
+ return x === undefined || x === null;
150
+ }
151
+
152
+ function passStringToWasm0(arg, malloc, realloc) {
153
+ if (realloc === undefined) {
154
+ const buf = cachedTextEncoder.encode(arg);
155
+ const ptr = malloc(buf.length, 1) >>> 0;
156
+ getUint8ArrayMemory0().subarray(ptr, ptr + buf.length).set(buf);
157
+ WASM_VECTOR_LEN = buf.length;
158
+ return ptr;
159
+ }
160
+
161
+ let len = arg.length;
162
+ let ptr = malloc(len, 1) >>> 0;
163
+
164
+ const mem = getUint8ArrayMemory0();
165
+
166
+ let offset = 0;
167
+
168
+ for (; offset < len; offset++) {
169
+ const code = arg.charCodeAt(offset);
170
+ if (code > 0x7F) break;
171
+ mem[ptr + offset] = code;
172
+ }
173
+ if (offset !== len) {
174
+ if (offset !== 0) {
175
+ arg = arg.slice(offset);
176
+ }
177
+ ptr = realloc(ptr, len, len = offset + arg.length * 3, 1) >>> 0;
178
+ const view = getUint8ArrayMemory0().subarray(ptr + offset, ptr + len);
179
+ const ret = cachedTextEncoder.encodeInto(arg, view);
180
+
181
+ offset += ret.written;
182
+ ptr = realloc(ptr, len, offset, 1) >>> 0;
183
+ }
184
+
185
+ WASM_VECTOR_LEN = offset;
186
+ return ptr;
187
+ }
188
+
189
+ function takeObject(idx) {
190
+ const ret = getObject(idx);
191
+ dropObject(idx);
192
+ return ret;
193
+ }
194
+
195
+ let cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true });
196
+ cachedTextDecoder.decode();
197
+ const MAX_SAFARI_DECODE_BYTES = 2146435072;
198
+ let numBytesDecoded = 0;
199
+ function decodeText(ptr, len) {
200
+ numBytesDecoded += len;
201
+ if (numBytesDecoded >= MAX_SAFARI_DECODE_BYTES) {
202
+ cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true });
203
+ cachedTextDecoder.decode();
204
+ numBytesDecoded = len;
205
+ }
206
+ return cachedTextDecoder.decode(getUint8ArrayMemory0().subarray(ptr, ptr + len));
207
+ }
208
+
209
+ const cachedTextEncoder = new TextEncoder();
210
+
211
+ if (!('encodeInto' in cachedTextEncoder)) {
212
+ cachedTextEncoder.encodeInto = function (arg, view) {
213
+ const buf = cachedTextEncoder.encode(arg);
214
+ view.set(buf);
215
+ return {
216
+ read: arg.length,
217
+ written: buf.length
218
+ };
219
+ };
220
+ }
221
+
222
+ let WASM_VECTOR_LEN = 0;
223
+
224
+
225
+ let wasm;
226
+ export function __wbg_set_wasm(val) {
227
+ wasm = val;
228
+ }
Binary file
package/package.json ADDED
@@ -0,0 +1,24 @@
1
+ {
2
+ "name": "@llmtrim/js",
3
+ "type": "module",
4
+ "description": "WebAssembly/JS bindings for the llmtrim-core compression engine (wasm-bindgen).",
5
+ "version": "0.2.1",
6
+ "license": "MPL-2.0",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/fkiene/llmtrim"
10
+ },
11
+ "files": [
12
+ "llmtrim_wasm_bg.wasm",
13
+ "llmtrim_wasm.js",
14
+ "llmtrim_wasm_bg.js",
15
+ "llmtrim_wasm.d.ts"
16
+ ],
17
+ "main": "llmtrim_wasm.js",
18
+ "homepage": "https://github.com/fkiene/llmtrim",
19
+ "types": "llmtrim_wasm.d.ts",
20
+ "sideEffects": [
21
+ "./llmtrim_wasm.js",
22
+ "./snippets/*"
23
+ ]
24
+ }