agentfmt

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 18, 2026 License: MIT Imports: 12 Imported by: 0

README

AgentFmt

CI Go Reference

Adaptive JSON-to-terminal output for language-model agents and humans.

AgentFmt is a final display gate, not another storage format. Give it the JSON value your tool was about to print. It measures several representations with a real tokenizer and emits the best one for the selected profile:

  • readable indentation for nested or text-heavy data;
  • schema-once rows for uniform arrays;
  • compact JSON when every friendlier representation is materially larger.
$ online search "structured output" --json | agentfmt
query: structured output
status: ok
results[3]{title,url,published,snippet}:
  Structured outputs,https://example.org/one,2026-05-28,Evaluation summary
  JSON RFC 8259,https://www.rfc-editor.org/rfc/rfc8259,2017-12-01,JSON grammar
  TOON specification,https://github.com/toon-format/spec,null,Token-oriented format

Why adaptive?

No single text notation is smallest and clearest for every JSON shape. Indentation alone does not guarantee token savings. Repeated keys dominate uniform arrays, while table syntax can become worse for irregular or deeply nested values.

AgentFmt therefore generates eligible candidates, counts their tokens using o200k_base or cl100k_base, and applies a profile:

Profile Behavior
balanced Prefer outline; use table at ≥8% savings or JSON at ≥20% additional savings
tokens Select the eligible candidate with the fewest measured tokens
readable Always use the indented outline
json Emit deterministic compact JSON for programmatic consumers

The output is deterministic, preserves JSON field order and numeric lexemes, quotes ambiguous strings, rejects duplicate object keys, and isolates multiline strings with collision-safe literal delimiters.

Install

Universal CLI
go install github.com/khanglvm/agentfmt/cmd/agentfmt@latest

Prebuilt macOS, Linux and Windows archives are also attached to each GitHub release.

Then place it at the final stdout boundary:

my-agent-tool --json | agentfmt
agentfmt response.json
agentfmt -profile tokens -encoding cl100k_base response.json
agentfmt -stats response.json  # decision evidence goes to stderr
Go
import "github.com/khanglvm/agentfmt"

result, err := agentfmt.RenderJSON(payload)
if err != nil {
    return err
}
fmt.Println(result.Text)

Configure or reuse a renderer:

renderer, _ := agentfmt.NewRenderer(
    agentfmt.WithProfile(agentfmt.ProfileBalanced),
    agentfmt.WithEncoding(agentfmt.EncodingO200K),
)
result, err := renderer.Render(toolResult)
Python

The thin adapter delegates to the same executable, so it cannot drift from the Go engine:

pip install "agentfmt-adapter @ git+https://github.com/khanglvm/agentfmt.git"
go install github.com/khanglvm/agentfmt/cmd/agentfmt@latest
from agentfmt import render

text = render(tool_result)

Use AGENTFMT_BIN=/path/to/agentfmt or binary=... when the executable is not on PATH.

JavaScript / TypeScript
npm install github:khanglvm/agentfmt
go install github.com/khanglvm/agentfmt/cmd/agentfmt@latest
import { render, renderSync } from "agentfmt-adapter";

const text = await render(toolResult);
const immediate = renderSync(toolResult, { profile: "tokens" });

The Python and JS adapters send JSON through subprocess stdin with shell disabled. Binary resolution is: explicit option, AGENTFMT_BIN, then PATH.

Agent and MCP integration

Use AgentFmt for human/agent-facing text only:

native result → JSON-compatible value → AgentFmt → terminal/tool text

For MCP, put AgentFmt text in a text content block. Keep structuredContent or other application-consumed fields as JSON. This avoids breaking clients while reducing the representation placed in model context.

Benchmark

The committed benchmark uses five fixtures covering uniform rows, search results, nested configuration, heterogeneous tool output, and long untrusted content.

go run ./cmd/agentfmt-bench -encoding o200k_base
go run ./cmd/agentfmt-bench -encoding cl100k_base

o200k_base results:

Fixture Compact JSON Pretty JSON Outline Table Balanced Strategy
heterogeneous tool result 84 149 117 84 JSON
long content 87 108 95 95 outline
nested config 95 169 116 116 outline
search results 248 332 283 214 214 table
uniform records 183 327 268 118 118 table
Total 697 1,085 879 627 adaptive

On these fixtures, balanced AgentFmt is 10.0% smaller than compact JSON and 42.2% smaller than pretty JSON. These are reproducible fixture results, not a claim that AgentFmt wins on every dataset or tokenizer. See benchmarks/results.md for both encodings and exact methodology.

Format and design

Important boundary

AgentFmt output is intentionally not guaranteed to round-trip into the original JSON. It may omit redundant per-row field names and use human-oriented literal blocks. Use -profile json or your original JSON API when a program must parse, persist, sign, hash, or validate the result.

Literal blocks protect the display grammar, not the model from prompt injection. Treat web pages, logs, and user-controlled strings as untrusted even when fenced.

License

MIT

Documentation

Overview

Package agentfmt converts JSON-shaped data into compact, human-scannable terminal text intended for language-model agents and people.

AgentFmt output is a display projection, not a serialization format. Use the JSON profile when another program must parse the output.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Candidate

type Candidate struct {
	Strategy Strategy
	Tokens   int
	Bytes    int
}

Candidate describes one eligible rendering considered by the selector.

type Encoding

type Encoding string

Encoding identifies the tokenizer used to compare candidates.

const (
	EncodingO200K  Encoding = "o200k_base"
	EncodingCL100K Encoding = "cl100k_base"
)

type Estimator

type Estimator interface {
	Name() string
	Count(text string) (int, error)
}

Estimator counts model tokens. Custom estimators make selection portable to models that do not use OpenAI-compatible encodings.

func NewTokenEstimator

func NewTokenEstimator(encoding Encoding) (Estimator, error)

NewTokenEstimator returns the exact built-in counter for an encoding.

type Option

type Option func(*config) error

Option configures a Renderer.

func WithEncoding

func WithEncoding(encoding Encoding) Option

WithEncoding chooses the built-in exact tokenizer.

func WithEstimator

func WithEstimator(estimator Estimator) Option

WithEstimator replaces the built-in tokenizer.

func WithProfile

func WithProfile(profile Profile) Option

WithProfile chooses balanced, tokens, readable, or json behavior.

func WithSavingsThresholds

func WithSavingsThresholds(table, compactJSON float64) Option

WithSavingsThresholds sets the fractional savings required by balanced mode before it selects table or compact JSON over the outline.

func WithTableMinRows

func WithTableMinRows(rows int) Option

WithTableMinRows sets the smallest uniform primitive-object array eligible for schema-once table rendering.

type Profile

type Profile string

Profile controls the readability/token trade-off.

const (
	ProfileBalanced Profile = "balanced"
	ProfileTokens   Profile = "tokens"
	ProfileReadable Profile = "readable"
	ProfileJSON     Profile = "json"
)

type Renderer

type Renderer struct {
	// contains filtered or unexported fields
}

Renderer is safe for concurrent use after construction.

func NewRenderer

func NewRenderer(options ...Option) (*Renderer, error)

NewRenderer constructs a reusable adaptive renderer.

func (*Renderer) Render

func (r *Renderer) Render(value any) (Result, error)

Render normalizes a Go value through encoding/json and renders it.

func (*Renderer) RenderJSON

func (r *Renderer) RenderJSON(data []byte) (Result, error)

RenderJSON parses one JSON value and renders it.

type Result

type Result struct {
	Text       string
	Strategy   Strategy
	Tokens     int
	Encoding   string
	Candidates []Candidate
}

Result is the selected display and the evidence behind the choice.

func RenderJSON

func RenderJSON(data []byte, options ...Option) (Result, error)

RenderJSON is the convenience form of NewRenderer(...).RenderJSON(data).

type Strategy

type Strategy string

Strategy identifies the representation selected for a result.

const (
	StrategyOutline Strategy = "outline"
	StrategyTable   Strategy = "table"
	StrategyJSON    Strategy = "json"
)

Directories

Path Synopsis
cmd
agentfmt command
agentfmt-bench command
internal
cli

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL