toolgz 0.2.11 → 0.2.12
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 +55 -0
- package/dist/index.js +4 -1
- package/dist/types.d.ts +18 -0
- package/llms.txt +9 -2
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -438,6 +438,35 @@ style in 0.2.0.
|
|
|
438
438
|
|
|
439
439
|
---
|
|
440
440
|
|
|
441
|
+
## See it run
|
|
442
|
+
|
|
443
|
+
A demo you can put on a screen. It prints every step — the tool definitions going in,
|
|
444
|
+
what `compress()` turns them into, the request, the model's actual response, the
|
|
445
|
+
`resolve()` translation, and your dispatcher running unchanged.
|
|
446
|
+
|
|
447
|
+
```bash
|
|
448
|
+
npm run demo -- --level=3 # one level, every step
|
|
449
|
+
npm run demo -- --compare # levels 0, 1 and 3 back to back
|
|
450
|
+
npm run demo -- --level=3 --offline # no API key: scripted model, real library
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
`--compare` ends with the side-by-side:
|
|
454
|
+
|
|
455
|
+
```
|
|
456
|
+
level wire tools definition chars reclaimed turns argument checking
|
|
457
|
+
0 6 → 6 3,283 → 3,289 -0.2% 2 provider
|
|
458
|
+
1 6 → 6 3,283 → 1,999 39.1% 2 provider
|
|
459
|
+
3 6 → 2 3,283 → 807 75.4% 2 toolgz
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
It really runs the loop against a real model unless you pass `--offline`, and it says
|
|
463
|
+
which it did. Level 3 also demonstrates the two outcomes a happy path never shows —
|
|
464
|
+
a bad code coming back `kind: "error"` and recoverable, and a `q()` lookup coming back
|
|
465
|
+
`kind: "meta"` with nothing dispatched.
|
|
466
|
+
|
|
467
|
+
Not shipped in the npm package: it imports a provider SDK, which is a devDependency,
|
|
468
|
+
and the library's zero-runtime-dependency guarantee is not negotiable.
|
|
469
|
+
|
|
441
470
|
## Runnable examples
|
|
442
471
|
|
|
443
472
|
Five files in [`examples/`](examples), all offline — no API key, no cost. Every one is
|
|
@@ -951,11 +980,37 @@ number can be recomputed rather than trusted.
|
|
|
951
980
|
| `mapStyle` | `"name+required" \| "explicit" \| "signature"` | `"name+required"` | level 3 only |
|
|
952
981
|
| `namespaceOf` | `(name) => {ns, op}` | split on first `_`/`.` | levels 2–3 grouping |
|
|
953
982
|
| `aliasOf` | `(ns) => string` | identity | level 2 tool naming |
|
|
983
|
+
| `signaturePrefix` | `boolean` | `true` | level 1 only; see below |
|
|
954
984
|
| `searchLimit` | `number` | `8` | max results from a `q` search |
|
|
955
985
|
| `validate` | `boolean` | `true` | **leave this on** |
|
|
956
986
|
| `model` | `string` | — | exact model id; picks the measured style. Omit and nothing changes |
|
|
957
987
|
| `objective` | `"occupancy" \| "cost"` | `"occupancy"` | what the pick optimises. Only `cost` has entries |
|
|
958
988
|
|
|
989
|
+
#### `signaturePrefix` — measured, and not the default for a reason
|
|
990
|
+
|
|
991
|
+
Level 1 prepends `name(a,b?) — ` to each description while keeping the full
|
|
992
|
+
`input_schema`. That prefix restates the tool name, property names, required list, enums
|
|
993
|
+
and item types that the schema already carries, and it is **18.5% of the level-1
|
|
994
|
+
payload** on the real corpus. Setting it `false` makes level 1 strictly smaller — 45.2%
|
|
995
|
+
→ **55.3%** on 149 real tools — and removes the case where level 1 *inflates* a terse
|
|
996
|
+
catalogue at all (−14.4% → −0.6%, which is level 0's own floor).
|
|
997
|
+
|
|
998
|
+
It stays `true` by default because size was not the question. Over 64 live runs, removing
|
|
999
|
+
it was a clear win on three providers and **not** on the fourth:
|
|
1000
|
+
|
|
1001
|
+
| Provider | Block | Median cost | Turns |
|
|
1002
|
+
|---|---:|---:|---|
|
|
1003
|
+
| `gemini-3.1-pro-preview` | −18.5% | −15.3% | 2.00 → 2.00 |
|
|
1004
|
+
| `gpt-5.6-sol` | −26.0% | −13.8% | 2.38 → 2.13 |
|
|
1005
|
+
| `grok-4.5` | −18.2% | −17.2% | 3.00 → 3.00 |
|
|
1006
|
+
| `claude-opus-5` | −18.3% | **+3.8%** | **3.88 → 4.63** |
|
|
1007
|
+
|
|
1008
|
+
On `claude-opus-5` the smaller block is spent on extra turns, and one turn is worth
|
|
1009
|
+
~3,300 prompt tokens here. **Zero malformed arguments and zero hallucinated names on both
|
|
1010
|
+
arms, 64/64 tasks.** So it is safe either way; it is just not universally cheaper. Try it
|
|
1011
|
+
if you are not on Anthropic, and measure. Full write-up in
|
|
1012
|
+
[docs/RESULTS.md](docs/RESULTS.md) Round 7.
|
|
1013
|
+
|
|
959
1014
|
Returns:
|
|
960
1015
|
|
|
961
1016
|
| Field | Type | Notes |
|
package/dist/index.js
CHANGED
|
@@ -228,11 +228,14 @@ export function compress(input, options = {}) {
|
|
|
228
228
|
// Level 1 — signature flattening, native tools, real names
|
|
229
229
|
// -------------------------------------------------------------------------
|
|
230
230
|
if (level === 1) {
|
|
231
|
+
const withSig = options.signaturePrefix ?? true;
|
|
231
232
|
const wire = tools.map((t) => {
|
|
232
233
|
const d = firstSentence(t.description);
|
|
234
|
+
// With no description the signature is the only content there is, so it stays
|
|
235
|
+
// regardless — an empty description would leave the model nothing to read.
|
|
233
236
|
return {
|
|
234
237
|
name: t.name,
|
|
235
|
-
description: d ? `${signatureLine(t)} — ${d}` : signatureLine(t),
|
|
238
|
+
description: withSig || !d ? (d ? `${signatureLine(t)} — ${d}` : signatureLine(t)) : d,
|
|
236
239
|
input_schema: flattenSchema(t.schema),
|
|
237
240
|
};
|
|
238
241
|
});
|
package/dist/types.d.ts
CHANGED
|
@@ -80,6 +80,24 @@ export type CompressOptions = {
|
|
|
80
80
|
};
|
|
81
81
|
/** Override the short alias used for a namespace at level 2. */
|
|
82
82
|
aliasOf?: (ns: string) => string;
|
|
83
|
+
/**
|
|
84
|
+
* Level 1 only. Prepend `name(a,b?)` to each description. Default true, which is
|
|
85
|
+
* the historical behaviour and what every published level-1 figure was measured with.
|
|
86
|
+
*
|
|
87
|
+
* **Experimental, and measured before it may become a default.** The prefix is fully
|
|
88
|
+
* redundant with the `input_schema` level 1 retains — the model already has the tool
|
|
89
|
+
* name, the property names, the required list, the enums and the item types — and it
|
|
90
|
+
* costs 18.5% of the level-1 payload on the real 149-tool corpus. Setting this false
|
|
91
|
+
* makes level 1 strictly smaller and removes the case where level 1 *inflates* a
|
|
92
|
+
* terse catalogue.
|
|
93
|
+
*
|
|
94
|
+
* It is not the default because "smaller" is not the question. Every arm that
|
|
95
|
+
* measured clean — zero malformed arguments, no extra turns — had the prefix, and a
|
|
96
|
+
* one-line signature may be easier for a model to read than the equivalent JSON.
|
|
97
|
+
* Whether that matters at level 1, where the schema is present anyway, is a
|
|
98
|
+
* benchmark result and not yet in hand.
|
|
99
|
+
*/
|
|
100
|
+
signaturePrefix?: boolean;
|
|
83
101
|
/** Cap how many results a search/query meta-call returns. Default 8. */
|
|
84
102
|
searchLimit?: number;
|
|
85
103
|
/** Validate arguments against the original schema before dispatch. Default true. */
|
package/llms.txt
CHANGED
|
@@ -170,8 +170,15 @@ recommendLevel(tools, namespaceOf?) → { level, reason, toolCount, namespaceCou
|
|
|
170
170
|
selectMapStyle(options) → { mapStyle, requestedMapStyle?, fallbackReason? } // pure
|
|
171
171
|
```
|
|
172
172
|
|
|
173
|
-
`CompressOptions`: `level`, `mapStyle`, `namespaceOf`, `aliasOf`, `
|
|
174
|
-
`validate`, `model`, `objective`.
|
|
173
|
+
`CompressOptions`: `level`, `mapStyle`, `namespaceOf`, `aliasOf`, `signaturePrefix`,
|
|
174
|
+
`searchLimit`, `validate`, `model`, `objective`.
|
|
175
|
+
|
|
176
|
+
`signaturePrefix` (level 1, default true) controls the `name(a,b?) — ` prefix on each
|
|
177
|
+
description. Setting it false makes level 1 smaller — 45.2% → 55.3% on real MCP tools —
|
|
178
|
+
and measured cheaper on Gemini, OpenAI and xAI over 64 runs. On `claude-opus-5` it was
|
|
179
|
+
**+3.8% dearer** because turns rose 3.88 → 4.63. Safe either way (zero malformed
|
|
180
|
+
arguments on both arms); not universally cheaper. Do not set it without measuring your
|
|
181
|
+
own workload.
|
|
175
182
|
|
|
176
183
|
`CompressResult`: `tools`, `systemPreamble`, `cachePreamble`, `resolve()`, `codeFor()`,
|
|
177
184
|
`encodeCallForTest()`, `stats`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "toolgz",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.12",
|
|
4
4
|
"description": "Compress LLM tool definitions to reclaim context window. Measured across Anthropic, OpenAI, Google and xAI.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -13,7 +13,9 @@
|
|
|
13
13
|
"bench:tier1": "tsx -r dotenv/config bench/harness/run-multi.ts --provider=all --suite=real --variants --reps=1 --scenario=real-daily-vs-weekly,real-overwrite-vs-append,real-status-vs-result,real-rollup-vs-detail",
|
|
14
14
|
"bench:tier2": "tsx -r dotenv/config bench/harness/run-multi.ts --provider=all --suite=real --variants --reps=2 --scenario=real-daily-vs-weekly,real-overwrite-vs-append,real-status-vs-result,real-rollup-vs-detail,real-two-step-scheduling,real-summary-vs-details",
|
|
15
15
|
"bench:tier3": "tsx -r dotenv/config bench/harness/run-multi.ts --provider=all --suite=real --variants --reps=3",
|
|
16
|
-
"img:metaphor": "tsx bench/metaphor.ts"
|
|
16
|
+
"img:metaphor": "tsx bench/metaphor.ts",
|
|
17
|
+
"demo": "tsx demo/cli.ts",
|
|
18
|
+
"bench:noprefix": "tsx -r dotenv/config bench/harness/run-multi.ts --provider=all --suite=real --variants --arms=signatures,signatures-noprefix --reps=2 --scenario=real-daily-vs-weekly,real-overwrite-vs-append,real-status-vs-result,real-rollup-vs-detail"
|
|
17
19
|
},
|
|
18
20
|
"keywords": [
|
|
19
21
|
"llm",
|