tokenfold 0.3.4 → 0.4.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 +19 -0
- package/dist/index.d.ts +36 -0
- package/dist/index.js +58 -12
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -15,5 +15,24 @@ const { payload, report } = await compress(input, {
|
|
|
15
15
|
});
|
|
16
16
|
```
|
|
17
17
|
|
|
18
|
+
Opt-in lossy array pruning drops whole array items to hit a token budget instead
|
|
19
|
+
of only restructuring them, replacing each with a `$tf_ref` marker that resolves
|
|
20
|
+
through the local retrieval store. Generic JSON only:
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
import { compress, retrieve } from "tokenfold";
|
|
24
|
+
|
|
25
|
+
const { payload, report } = await compress(feed, {
|
|
26
|
+
format: "json",
|
|
27
|
+
lossy: "heuristic",
|
|
28
|
+
lossyRatio: 0.35, // selection hint, not an enforced budget
|
|
29
|
+
lossyPreserve: ["meta"], // arrays that are never pruned
|
|
30
|
+
});
|
|
31
|
+
const original = await retrieve(hash); // a dropped item's original bytes
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Pass the same options to `inspect` to preview the projected savings without
|
|
35
|
+
writing anything to the store.
|
|
36
|
+
|
|
18
37
|
Requires Node.js 22 or newer. The matching native CLI is installed through an
|
|
19
38
|
optional platform package; set `TOKENFOLD_BINARY_PATH` to use a custom binary.
|
package/dist/index.d.ts
CHANGED
|
@@ -5,6 +5,8 @@ export { binaryPath, run, TokenFoldProcessError };
|
|
|
5
5
|
export type { Input, ProcessResult, RunOptions };
|
|
6
6
|
export type CompressionMode = "conservative" | "balanced" | "aggressive";
|
|
7
7
|
export type TaskScope = "all" | "general" | "code_review" | "change_summary" | "debugging" | "generation" | "api_overview" | "retrieval_qa" | "agent_history";
|
|
8
|
+
/** Selection backend for opt-in lossy pruning. `heuristic` is the only Phase 1 path. */
|
|
9
|
+
export type LossyPath = "heuristic";
|
|
8
10
|
export interface CompressionOptions {
|
|
9
11
|
format?: "auto" | "openai" | "anthropic" | "json" | "text" | "command" | "diff";
|
|
10
12
|
mode?: CompressionMode;
|
|
@@ -14,6 +16,22 @@ export interface CompressionOptions {
|
|
|
14
16
|
experimental?: boolean;
|
|
15
17
|
storeOriginals?: boolean;
|
|
16
18
|
retrieveNamespace?: string;
|
|
19
|
+
/**
|
|
20
|
+
* Opt-in LOSSY JSON array-item pruning (`--lossy`): drops whole array items to hit a token
|
|
21
|
+
* budget instead of only restructuring them. Generic JSON only — on any other format the run
|
|
22
|
+
* is a no-op and `json_prune` comes back as `status: "skipped"` with
|
|
23
|
+
* `skipped_reason: "not_applicable_to_format"`, persisting nothing. Dropped items are replaced
|
|
24
|
+
* by `$tf_ref` markers and stay recoverable via {@link retrieve}; pruning needs a durable
|
|
25
|
+
* filesystem retrieval store, so pass `configPath` when you want one other than the default.
|
|
26
|
+
*/
|
|
27
|
+
lossy?: LossyPath;
|
|
28
|
+
/**
|
|
29
|
+
* `--lossy-ratio`: a best-effort selection hint (0.0..=1.0), not an enforced budget — the
|
|
30
|
+
* achieved ratio differs by design. Use `targetTokens` for a real ceiling.
|
|
31
|
+
*/
|
|
32
|
+
lossyRatio?: number;
|
|
33
|
+
/** `--lossy-preserve`: dot-separated paths (e.g. `data.results`) whose arrays are never pruned. */
|
|
34
|
+
lossyPreserve?: readonly string[];
|
|
17
35
|
configPath?: string;
|
|
18
36
|
signal?: AbortSignal;
|
|
19
37
|
}
|
|
@@ -128,3 +146,21 @@ export interface CompressionResult {
|
|
|
128
146
|
}
|
|
129
147
|
export declare function compress(input: Input, options?: CompressionOptions): Promise<CompressionResult>;
|
|
130
148
|
export declare function inspect(input: Input, options?: CompressionOptions): Promise<CompressionResult>;
|
|
149
|
+
export interface RetrieveOptions {
|
|
150
|
+
/** `--retrieve-namespace`: the namespace the item was stored under. */
|
|
151
|
+
namespace?: string;
|
|
152
|
+
configPath?: string;
|
|
153
|
+
signal?: AbortSignal;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Restores the original bytes of something a lossy run dropped, or a `storeOriginals` run saved,
|
|
157
|
+
* mirroring `tokenfold retrieve`. `reference` is either a raw hex SHA-256 hash — a compressed
|
|
158
|
+
* payload's `$tf_ref.hash` — or a `[tokenfold:retrieve hash=... namespace=...]` text marker,
|
|
159
|
+
* whose embedded namespace is used when `options.namespace` is omitted. A CompressionReport
|
|
160
|
+
* path is NOT a valid reference: the current report schema carries no per-entry hash, and the
|
|
161
|
+
* CLI rejects it rather than guessing.
|
|
162
|
+
*
|
|
163
|
+
* Throws `TokenFoldProcessError` (`code: "tokenfold_exit"`) when the hash is unknown, its TTL
|
|
164
|
+
* has elapsed, or the reference is malformed — the CLI reports all of those as a non-zero exit.
|
|
165
|
+
*/
|
|
166
|
+
export declare function retrieve(reference: string, options?: RetrieveOptions): Promise<Uint8Array>;
|
package/dist/index.js
CHANGED
|
@@ -3,24 +3,43 @@ import { TokenFoldProcessError } from "./errors.js";
|
|
|
3
3
|
import { run } from "./process.js";
|
|
4
4
|
export { binaryPath, run, TokenFoldProcessError };
|
|
5
5
|
function argumentsFor(command, options) {
|
|
6
|
-
|
|
6
|
+
// The `inspect` subcommand has no --lossy flags of its own; the CLI's lossy preview is
|
|
7
|
+
// `compress --dry-run`, which routes to the same code path and, exactly like `inspect --json`,
|
|
8
|
+
// writes the report to stdout and no payload. So a lossy inspect() becomes that instead.
|
|
9
|
+
const wantsLossy = options.lossy !== undefined ||
|
|
10
|
+
options.lossyRatio !== undefined ||
|
|
11
|
+
(options.lossyPreserve?.length ?? 0) > 0;
|
|
12
|
+
const previewLossy = command === "inspect" && wantsLossy;
|
|
13
|
+
const args = previewLossy ? ["compress", "--json", "--dry-run"] : [command, "--json"];
|
|
14
|
+
const compressing = command === "compress" || previewLossy;
|
|
7
15
|
if (options.format)
|
|
8
16
|
args.push("--format", options.format);
|
|
9
17
|
if (options.mode)
|
|
10
18
|
args.push("--mode", options.mode);
|
|
11
19
|
if (options.targetTokens !== undefined)
|
|
12
20
|
args.push("--target-tokens", String(options.targetTokens));
|
|
13
|
-
if (options.disable?.length &&
|
|
21
|
+
if (options.disable?.length && compressing)
|
|
14
22
|
args.push("--disable", options.disable.join(","));
|
|
15
23
|
if (options.taskScope)
|
|
16
24
|
args.push("--task-scope", options.taskScope);
|
|
17
25
|
if (options.experimental)
|
|
18
26
|
args.push("--experimental");
|
|
19
|
-
if (options.storeOriginals &&
|
|
27
|
+
if (options.storeOriginals && compressing)
|
|
20
28
|
args.push("--store-originals");
|
|
21
|
-
if (options.retrieveNamespace &&
|
|
29
|
+
if (options.retrieveNamespace && compressing) {
|
|
22
30
|
args.push("--retrieve-namespace", options.retrieveNamespace);
|
|
23
31
|
}
|
|
32
|
+
if (wantsLossy && compressing) {
|
|
33
|
+
// `--lossy-ratio`/`--lossy-preserve` are `requires = "lossy"` in the CLI. Forward them even
|
|
34
|
+
// when `lossy` is unset rather than dropping them: silently ignoring a pruning-aggression
|
|
35
|
+
// knob is worse than the CLI's own "the following required arguments were not provided".
|
|
36
|
+
if (options.lossy)
|
|
37
|
+
args.push("--lossy", options.lossy);
|
|
38
|
+
if (options.lossyRatio !== undefined)
|
|
39
|
+
args.push("--lossy-ratio", String(options.lossyRatio));
|
|
40
|
+
for (const path of options.lossyPreserve ?? [])
|
|
41
|
+
args.push("--lossy-preserve", path);
|
|
42
|
+
}
|
|
24
43
|
if (options.configPath)
|
|
25
44
|
args.push("--config", options.configPath);
|
|
26
45
|
return args;
|
|
@@ -39,6 +58,16 @@ function parseReport(bytes, result) {
|
|
|
39
58
|
});
|
|
40
59
|
}
|
|
41
60
|
}
|
|
61
|
+
function throwIfFailed(result) {
|
|
62
|
+
if (result.exitCode === 0)
|
|
63
|
+
return;
|
|
64
|
+
throw new TokenFoldProcessError(`tokenfold exited with status ${result.exitCode ?? result.signal}`, {
|
|
65
|
+
code: "tokenfold_exit",
|
|
66
|
+
exitCode: result.exitCode,
|
|
67
|
+
signal: result.signal,
|
|
68
|
+
stderr: result.stderr,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
42
71
|
async function execute(command, input, options) {
|
|
43
72
|
const runOptions = {
|
|
44
73
|
stdin: input,
|
|
@@ -47,14 +76,7 @@ async function execute(command, input, options) {
|
|
|
47
76
|
if (options.signal)
|
|
48
77
|
runOptions.signal = options.signal;
|
|
49
78
|
const result = await run(argumentsFor(command, options), runOptions);
|
|
50
|
-
|
|
51
|
-
throw new TokenFoldProcessError(`tokenfold exited with status ${result.exitCode ?? result.signal}`, {
|
|
52
|
-
code: "tokenfold_exit",
|
|
53
|
-
exitCode: result.exitCode,
|
|
54
|
-
signal: result.signal,
|
|
55
|
-
stderr: result.stderr,
|
|
56
|
-
});
|
|
57
|
-
}
|
|
79
|
+
throwIfFailed(result);
|
|
58
80
|
const reportBytes = command === "compress" ? result.stderr : result.stdout;
|
|
59
81
|
return {
|
|
60
82
|
payload: command === "compress" ? result.stdout : Uint8Array.from(Buffer.from(input)),
|
|
@@ -67,3 +89,27 @@ export function compress(input, options = {}) {
|
|
|
67
89
|
export function inspect(input, options = {}) {
|
|
68
90
|
return execute("inspect", input, options);
|
|
69
91
|
}
|
|
92
|
+
/**
|
|
93
|
+
* Restores the original bytes of something a lossy run dropped, or a `storeOriginals` run saved,
|
|
94
|
+
* mirroring `tokenfold retrieve`. `reference` is either a raw hex SHA-256 hash — a compressed
|
|
95
|
+
* payload's `$tf_ref.hash` — or a `[tokenfold:retrieve hash=... namespace=...]` text marker,
|
|
96
|
+
* whose embedded namespace is used when `options.namespace` is omitted. A CompressionReport
|
|
97
|
+
* path is NOT a valid reference: the current report schema carries no per-entry hash, and the
|
|
98
|
+
* CLI rejects it rather than guessing.
|
|
99
|
+
*
|
|
100
|
+
* Throws `TokenFoldProcessError` (`code: "tokenfold_exit"`) when the hash is unknown, its TTL
|
|
101
|
+
* has elapsed, or the reference is malformed — the CLI reports all of those as a non-zero exit.
|
|
102
|
+
*/
|
|
103
|
+
export async function retrieve(reference, options = {}) {
|
|
104
|
+
const args = ["retrieve", reference];
|
|
105
|
+
if (options.namespace)
|
|
106
|
+
args.push("--retrieve-namespace", options.namespace);
|
|
107
|
+
if (options.configPath)
|
|
108
|
+
args.push("--config", options.configPath);
|
|
109
|
+
const runOptions = { env: { TOKENFOLD_ANALYTICS_ENABLED: "false" } };
|
|
110
|
+
if (options.signal)
|
|
111
|
+
runOptions.signal = options.signal;
|
|
112
|
+
const result = await run(args, runOptions);
|
|
113
|
+
throwIfFailed(result);
|
|
114
|
+
return result.stdout;
|
|
115
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tokenfold",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "Token-aware compression for LLM payloads, backed by the tokenfold Rust CLI.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -49,11 +49,11 @@
|
|
|
49
49
|
"prepack": "npm run build"
|
|
50
50
|
},
|
|
51
51
|
"optionalDependencies": {
|
|
52
|
-
"@tokenfold/cli-darwin-x64": "0.
|
|
53
|
-
"@tokenfold/cli-darwin-arm64": "0.
|
|
54
|
-
"@tokenfold/cli-linux-x64": "0.
|
|
55
|
-
"@tokenfold/cli-linux-arm64": "0.
|
|
56
|
-
"@tokenfold/cli-win32-x64": "0.
|
|
52
|
+
"@tokenfold/cli-darwin-x64": "0.4.1",
|
|
53
|
+
"@tokenfold/cli-darwin-arm64": "0.4.1",
|
|
54
|
+
"@tokenfold/cli-linux-x64": "0.4.1",
|
|
55
|
+
"@tokenfold/cli-linux-arm64": "0.4.1",
|
|
56
|
+
"@tokenfold/cli-win32-x64": "0.4.1"
|
|
57
57
|
},
|
|
58
58
|
"devDependencies": {
|
|
59
59
|
"@types/node": "^24.0.0",
|