Documentation
¶
Overview ¶
Package arms implements the encoding arms of the discovery-effectiveness profiler (spec 083). An arm is one deterministic way of rendering tool definitions into agent-context text (full JSON baseline, compact signature, TSCG, TOON, ...). Arms are compared on frozen corpora for token cost and — when they alter what the retrieval index ingests — for retrieval quality.
The behavioral contract every arm must satisfy lives in specs/083-discovery-profiler/contracts/arm-interface.md. In short: byte-deterministic output (FR-010), explicit errors instead of silent truncation (FR-009), self-reported lower-bound labeling when descriptions are dropped, an explicit index-ingestion mapping (FR-008), and ErrArmUnavailable at registry-resolution time when an external runtime is missing (FR-006).
Index ¶
- Constants
- Variables
- func CanonicalJSON(raw json.RawMessage) (string, error)
- func CanonicalToolText(t bench.Tool) (string, error)
- func MustRegister(a Arm)
- func Names() []string
- func Register(a Arm) error
- type Arm
- type AvailabilityChecker
- type BaselineArm
- func (*BaselineArm) EncodeIndexMetadata(t bench.Tool) (config.ToolMetadata, error)
- func (a *BaselineArm) EncodeListing(ts []bench.Tool) (string, error)
- func (*BaselineArm) EncodeTool(t bench.Tool) (string, error)
- func (*BaselineArm) IndexAltering() bool
- func (*BaselineArm) LowerBound() bool
- func (*BaselineArm) Name() string
- type CompactArm
- func (*CompactArm) EncodeIndexMetadata(t bench.Tool) (config.ToolMetadata, error)
- func (a *CompactArm) EncodeListing(ts []bench.Tool) (string, error)
- func (a *CompactArm) EncodeTool(t bench.Tool) (string, error)
- func (*CompactArm) IndexAltering() bool
- func (*CompactArm) LowerBound() bool
- func (*CompactArm) Name() string
- type Registry
- type ResultFixture
- type ResultFixtureSet
- type TSCGArm
- func (a *TSCGArm) Available() error
- func (a *TSCGArm) EncodeIndexMetadata(t bench.Tool) (config.ToolMetadata, error)
- func (a *TSCGArm) EncodeListing(ts []bench.Tool) (string, error)
- func (a *TSCGArm) EncodeTool(t bench.Tool) (string, error)
- func (*TSCGArm) IndexAltering() bool
- func (*TSCGArm) LowerBound() bool
- func (*TSCGArm) Name() string
- type ToonListingArm
- func (*ToonListingArm) EncodeIndexMetadata(t bench.Tool) (config.ToolMetadata, error)
- func (*ToonListingArm) EncodeListing(ts []bench.Tool) (string, error)
- func (*ToonListingArm) EncodeTool(t bench.Tool) (string, error)
- func (*ToonListingArm) IndexAltering() bool
- func (*ToonListingArm) LowerBound() bool
- func (*ToonListingArm) Name() string
- type ToonResultsRun
- type TronArm
Constants ¶
const ( PayloadClassTabular = "tabular" PayloadClassNonTabular = "non_tabular" )
PayloadClassTabular / PayloadClassNonTabular are the fixture classification hints (datasets/README.md rule: tabular = a uniform JSON array of flat objects; everything else is non_tabular).
const BaselineName = "baseline_json"
BaselineName is the registry key of the full-JSON baseline arm.
const CompactName = "compact_sig"
CompactName is the registry key of the compact-signature arm.
const TSCGName = "tscg"
TSCGName is the registry key of the TSCG-compiled arm.
const ToonListingName = "toon_listing"
ToonListingName is the registry key of the TOON tool-listing arm.
const ToonResultsName = "toon_results"
ToonResultsName is the report key of the fixture-driven TOON results arm.
const TronName = "tron_dedup"
TronName is the registry key of the TRON named-class schema-dedup arm.
Variables ¶
ErrArmUnavailable is returned (wrapped, with a human-readable reason) when an arm's external runtime is missing — e.g. the TSCG arm without a node binary or bench/tscg/node_modules. It is surfaced at registry-resolution time, before any tool is processed, so the harness reports an arm-level skip-with-reason instead of per-tool failures (contract rule 5).
Functions ¶
func CanonicalJSON ¶
func CanonicalJSON(raw json.RawMessage) (string, error)
CanonicalJSON re-encodes raw JSON bytes into the canonical form used everywhere in the bench (research D7b): object keys sorted lexicographically at every depth, array order preserved, number literals preserved verbatim (via json.Number — no float round-trip), compact (no insignificant whitespace), and no HTML escaping. Identical JSON content in any key order canonicalizes to identical bytes (FR-010).
The implementation lives in bench.CanonicalJSON so every schema ingestion boundary (corpus loaders, live fetch) canonicalizes with the SAME function the arms render with — contract parity between the baseline arm and Tokenizer.CountToolWithSchema. This alias keeps the arms-package API.
func CanonicalToolText ¶
CanonicalToolText is THE canonical full-definition renderer (research D7b): name + "\n" + description, plus "\n" + canonical-JSON schema when the tool has one — byte-identical to the text shape the existing Tokenizer.CountToolWithSchema counts for a canonical-schema tool. This single renderer feeds the baseline_json arm, the naive full-menu count, the proxy-menu count, savings-% denominators, and break-even inputs, so every savings percentage shares one denominator.
func MustRegister ¶
func MustRegister(a Arm)
MustRegister adds an arm to the package default registry, panicking on a registration bug (duplicate/invalid name) — a programmer error, caught by any test importing the package.
Types ¶
type Arm ¶
type Arm interface {
// Name is the unique registry key (lowercase snake_case, stable across
// releases): baseline_json, compact_sig, tscg, toon_listing, toon_results,
// tron_dedup.
Name() string
// IndexAltering reports whether the arm changes any text the retrieval
// index ingests. True obligates retrieval-quality scoring (FR-008); the
// two-sided contract test (T022) verifies the declaration against
// EncodeIndexMetadata diffs on corpus_v2.
IndexAltering() bool
// LowerBound reports whether the arm drops or truncates descriptions, so
// its savings are rendered as a lower-bound estimate (contract rule 3).
LowerBound() bool
// EncodeTool renders one tool definition. Byte-deterministic; an
// unencodable tool returns an error (counted as a skip), never a silently
// truncated encoding.
EncodeTool(t bench.Tool) (string, error)
// EncodeListing renders a whole-response tool listing. Formats with a
// shared preamble or dictionary (TRON classes, TOON header) amortize it
// here, not per-tool (contract rule 6).
EncodeListing(ts []bench.Tool) (string, error)
// EncodeIndexMetadata returns the exact Name/ServerName/Description/
// ParamsJSON the production index (internal/index.Manager.BatchIndexTools →
// BleveIndex.IndexTool) ingests for this arm. It is the single mapping the
// armindex builder and the IndexAltering contract test consume. Rendering-
// only arms return the tool's fields unchanged.
EncodeIndexMetadata(t bench.Tool) (config.ToolMetadata, error)
}
Arm is one deterministic tool-definition encoding under measurement.
type AvailabilityChecker ¶
type AvailabilityChecker interface {
Available() error
}
AvailabilityChecker is an optional interface for arms with external runtimes. Registry.Resolve calls Available() and propagates its error, which must wrap ErrArmUnavailable when the runtime is missing.
type BaselineArm ¶
type BaselineArm struct{}
BaselineArm is the mandatory baseline_json arm: the canonical full-definition rendering every other arm's savings are measured against.
func (*BaselineArm) EncodeIndexMetadata ¶
func (*BaselineArm) EncodeIndexMetadata(t bench.Tool) (config.ToolMetadata, error)
EncodeIndexMetadata implements Arm: a rendering-only arm returns the tool's production index fields unchanged — the same Name/ServerName/Description/ ParamsJSON mapping internal/upstream/core builds from a live tools/list (ParamsJSON is the tool's input-schema JSON, empty when the tool has none).
func (*BaselineArm) EncodeListing ¶
func (a *BaselineArm) EncodeListing(ts []bench.Tool) (string, error)
EncodeListing implements Arm: per-tool renderings joined by a fixed separator (the baseline has no shared preamble to amortize).
func (*BaselineArm) EncodeTool ¶
func (*BaselineArm) EncodeTool(t bench.Tool) (string, error)
EncodeTool implements Arm via the canonical full-definition renderer.
func (*BaselineArm) IndexAltering ¶
func (*BaselineArm) IndexAltering() bool
IndexAltering implements Arm: the baseline IS the production rendering, so it never alters what the index ingests.
func (*BaselineArm) LowerBound ¶
func (*BaselineArm) LowerBound() bool
LowerBound implements Arm: descriptions are preserved verbatim.
type CompactArm ¶
type CompactArm struct{}
CompactArm is the compact-signature arm: each tool renders as `name(param:type, opt?:type)|description` — required parameters bare, optional "?"-suffixed, description preserved verbatim. Pre-measured at −92% on live retrieve_tools responses with recall unchanged (research D2).
func (*CompactArm) EncodeIndexMetadata ¶
func (*CompactArm) EncodeIndexMetadata(t bench.Tool) (config.ToolMetadata, error)
EncodeIndexMetadata implements Arm: Name, ServerName, and Description are ingested unchanged; ParamsJSON is replaced by the compact params text (the parenthesized portion of the signature) — the exact parameter text the index sees under this arm.
func (*CompactArm) EncodeListing ¶
func (a *CompactArm) EncodeListing(ts []bench.Tool) (string, error)
EncodeListing implements Arm: per-tool signatures joined by the shared listing separator (no preamble to amortize), so listing totals decompose exactly into per-tool costs.
func (*CompactArm) EncodeTool ¶
func (a *CompactArm) EncodeTool(t bench.Tool) (string, error)
EncodeTool implements Arm.
func (*CompactArm) IndexAltering ¶
func (*CompactArm) IndexAltering() bool
IndexAltering implements Arm: the arm replaces the ParamsJSON the retrieval index ingests with the compact params text, so retrieval-quality scoring is obligatory (FR-008).
func (*CompactArm) LowerBound ¶
func (*CompactArm) LowerBound() bool
LowerBound implements Arm: descriptions are preserved verbatim, so the measured savings are exact, not a lower bound.
type Registry ¶
type Registry struct {
// contains filtered or unexported fields
}
Registry holds the registered encoding arms.
type ResultFixture ¶
type ResultFixture struct {
ToolID string `json:"tool_id"`
PayloadClassHint string `json:"payload_class_hint"`
Payload json.RawMessage `json:"payload"`
}
ResultFixture is one captured tool-call output payload.
type ResultFixtureSet ¶
type ResultFixtureSet struct {
Captured string `json:"captured"`
FixtureID string `json:"fixture_id"`
Results []ResultFixture `json:"results"`
}
ResultFixtureSet mirrors datasets/result_fixtures_v1.json: a frozen, versioned snapshot of deterministic tool-call outputs (T037).
func LoadResultFixtures ¶
func LoadResultFixtures(path string) (*ResultFixtureSet, error)
LoadResultFixtures reads and validates a result-fixture snapshot. Validation is strict and per-record (the fixture set is frozen and self-describing): missing identity fields, empty/duplicate tool IDs, unknown classification hints, invalid payload JSON, and a tabular hint on a non-array payload are all explicit errors naming the offending record.
type TSCGArm ¶
type TSCGArm struct {
// contains filtered or unexported fields
}
TSCGArm measures the reference TSCG implementation (@tscg/core) by spawning the committed Node shim bench/tscg/shim.mjs once per Encode call and exchanging JSONL records keyed by tool_id (research D3). TSCG is a pure deterministic compiler with pinned options, so identical input bytes always produce identical output bytes (FR-010).
func NewTSCG ¶
func NewTSCG() *TSCGArm
NewTSCG constructs the tscg arm, locating bench/tscg by walking up from the working directory (tests run in bench/arms; the bench CLI runs from the repo root) and resolving the node binary from PATH. Both checks happen here, at construction; Available() reports the cached verdict.
func NewTSCGAt ¶
NewTSCGAt constructs the tscg arm against an explicit shim directory (tests use this to exercise the unavailable path).
func (*TSCGArm) Available ¶
Available implements AvailabilityChecker with the construction-time verdict.
func (*TSCGArm) EncodeIndexMetadata ¶
EncodeIndexMetadata implements Arm (FR-008). The compiled text has a fixed three-part shape (verified against @tscg/core@1.4.3 output on all of corpus_v2):
<name>: <rewritten description> <param>[*] (<type>): <desc> | <param2> ... ← absent for parameterless tools [CLOSURE:<name>(<required params>)]
Mapping decision: Name stays the tool name (TSCG never re-encodes it, and the "<name>: " header prefix would duplicate the Name field the index already ingests); Description gets the TSCG-rewritten prose (the compiled header may span multiple lines — e.g. sequential-thinking — so the split is structural, not line-count-based); ParamsJSON gets the compiled parameter representation — the trailing run of two-space-indented parameter lines plus the CLOSURE signature line — replacing the JSON schema, mirroring how the compiled text itself represents params. The three fields reconstruct the compiled encoding exactly (Name + ": " + Description + "\n" + ParamsJSON), so the index ingests precisely what this arm renders: nothing more, nothing less.
func (*TSCGArm) EncodeListing ¶
EncodeListing implements Arm: one node spawn for the whole listing, per-tool compilations joined by the shared separator (TSCG has no listing-level preamble or dictionary to amortize — contract rule 6).
func (*TSCGArm) EncodeTool ¶
EncodeTool implements Arm: a batch of one through the shim.
func (*TSCGArm) IndexAltering ¶
IndexAltering implements Arm: TSCG re-encodes both the description prose and the parameter text, so retrieval-quality scoring is obligatory (FR-008).
func (*TSCGArm) LowerBound ¶
LowerBound implements Arm: the balanced profile rewrites descriptions and elides filler phrases (measured on corpus_v2: e.g. filesystem:read_text_file loses "Use this tool when you need to"), so savings are a lower-bound estimate per contract rule 3.
type ToonListingArm ¶
type ToonListingArm struct{}
ToonListingArm renders tool listings as TOON (Token-Oriented Object Notation, https://github.com/toon-format) via the official toon-go encoder (research D1: measured honestly even though the TOON spec itself concedes compact JSON often wins on deeply-nested structures like JSON Schema).
Each tool becomes an ordered TOON object {name, description, inputSchema} where inputSchema is the tool's JSON input schema re-expressed as TOON text. Determinism (FR-010): field order is fixed by explicit toon.Object fields, schema object keys are sorted lexicographically by toon-go's normalizer, and schema numbers are decoded as json.Number (no float round-trip surprises in the literal source).
func NewToonListing ¶
func NewToonListing() *ToonListingArm
NewToonListing returns the toon_listing arm.
func (*ToonListingArm) EncodeIndexMetadata ¶
func (*ToonListingArm) EncodeIndexMetadata(t bench.Tool) (config.ToolMetadata, error)
EncodeIndexMetadata implements Arm: Name/ServerName/Description are the production values unchanged; ParamsJSON is replaced by the TOON-encoded schema text — the exact parameter text the retrieval index would ingest under this arm (empty when the tool has no schema, matching baseline).
func (*ToonListingArm) EncodeListing ¶
func (*ToonListingArm) EncodeListing(ts []bench.Tool) (string, error)
EncodeListing implements Arm: the whole listing is a single TOON array document, so the shared array header ("[N]:") is paid once for the listing rather than per tool.
func (*ToonListingArm) EncodeTool ¶
func (*ToonListingArm) EncodeTool(t bench.Tool) (string, error)
EncodeTool implements Arm: one tool as a bare TOON object document. The listing array header is amortized in EncodeListing, not here (contract rule 6).
func (*ToonListingArm) IndexAltering ¶
func (*ToonListingArm) IndexAltering() bool
IndexAltering implements Arm: the TOON schema text replaces ParamsJSON in the index mapping (see EncodeIndexMetadata), so the arm changes text the retrieval index ingests and obligates retrieval-quality scoring (FR-008).
func (*ToonListingArm) LowerBound ¶
func (*ToonListingArm) LowerBound() bool
LowerBound implements Arm: descriptions are preserved verbatim (TOON quotes and escapes multi-line strings; nothing is dropped or truncated).
type ToonResultsRun ¶
type ToonResultsRun struct {
// Rows: [0] = compact-JSON baseline row (arm baseline_json, savings 0),
// [1] = the toon_results row (savings vs [0]).
Rows []bench.ArmResult `json:"rows"`
// Per-class token totals: the tabular/non-tabular split of each side's
// TotalTokens (FR-007 classification split).
TabularToonTokens int `json:"tabular_toon_tokens"`
TabularBaselineTokens int `json:"tabular_baseline_tokens"`
NonTabularToonTokens int `json:"non_tabular_toon_tokens"`
NonTabularBaselineTokens int `json:"non_tabular_baseline_tokens"`
}
ToonResultsRun is one toon_results measurement: the two report rows (compact-JSON baseline of the payloads, then TOON of the same payloads) and the tabular/non-tabular token split behind them. The baseline row makes the savings recomputable from report rows alone (FR-004 spirit).
func RunToonResults ¶
func RunToonResults(tk *bench.Tokenizer, fx *ResultFixtureSet) (*ToonResultsRun, error)
RunToonResults measures the fixture payloads under both encodings and assembles the two results-class report rows. Determinism (FR-010): payloads are processed in fixture-file order (the file is sorted by tool_id at capture), the baseline is CanonicalJSON (sorted keys, compact), and TOON object keys are sorted by toon-go's normalizer.
func (*ToonResultsRun) NonTabularSavingsPct ¶
func (r *ToonResultsRun) NonTabularSavingsPct() float64
NonTabularSavingsPct is the TOON savings vs compact JSON on non-tabular payloads only (0 when none was measured).
func (*ToonResultsRun) TabularSavingsPct ¶
func (r *ToonResultsRun) TabularSavingsPct() float64
TabularSavingsPct is the TOON savings vs compact JSON on tabular payloads only (0 when no tabular payload was measured).
type TronArm ¶
type TronArm struct{}
TronArm is a minimal, honest in-tree implementation of TRON's named-class schema deduplication (research D2, arXiv:2605.29676 — no Go implementation exists upstream). Mechanism: each tool's input schema is canonicalized (CanonicalJSON — sorted keys, compact); byte-identical canonical schemas share ONE class definition, emitted once in the EncodeListing preamble as
class C<id> = {…canonical schema…}
and each tool entry references its class by name:
name|description|C<id>
Deliberate deviations from the paper, documented for honesty:
- Dedup key is the EXACT canonical schema bytes, not a structural "shape" with per-property descriptions abstracted away. This is lossless (no description is dropped or truncated — LowerBound stays false) but conservative: schemas that differ only in embedded descriptions do not merge, so measured savings are what exact dedup achieves, not the paper's upper bound.
- Class names are content-addressed (C + first 8 hex of SHA-256 of the canonical schema) rather than sequential C1/C2, so the per-tool index mapping (EncodeIndexMetadata) and the listing agree without shared state, independent of tool order. Content addressing costs a few more tokens per reference than sequential names would — a conservative bias.
- Every distinct schema gets a class, including schemas used by a single tool. Singletons pay the class overhead ("class … = " plus the reference) instead of inlining; this keeps the format uniform and the honest cost visible rather than optimized away.
- Field delimiters (| and newlines) are not escaped: the arm measures token cost of the format shape, and descriptions are preserved verbatim because escaping would perturb the very token counts under measurement.
Amortization lives ONLY in EncodeListing (contract rule 6): EncodeTool returns the non-deduped single form name|description|<canonical schema>, so per-tool means remain comparable across arms.
func (*TronArm) EncodeIndexMetadata ¶
EncodeIndexMetadata implements Arm: under TRON the schema body lives in a shared class definition that is not attached to any single tool, so the per-tool params text the production index ingests is the content-addressed class reference — Name/ServerName/Description are unchanged. Content addressing makes this mapping deterministic per tool, with no listing context, and guarantees it names the same class EncodeListing defines.
func (*TronArm) EncodeListing ¶
EncodeListing implements Arm: a class-definition preamble (one line per distinct canonical schema, in first-appearance order) followed by tool entries referencing their class by name. Determinism: class identity is content-addressed, preamble order is input order — no map iteration.
func (*TronArm) EncodeTool ¶
EncodeTool implements Arm: the non-deduped single form name|description|<canonical schema> (name|description when the tool has no schema). Class machinery appears only in EncodeListing.
func (*TronArm) IndexAltering ¶
IndexAltering implements Arm: the schema body moves into a shared class definition, so the per-tool params text the index ingests becomes a class reference instead of the schema JSON (see EncodeIndexMetadata).
func (*TronArm) LowerBound ¶
LowerBound implements Arm: descriptions (tool-level and inside schemas) are preserved verbatim.