llm-context-fmt
CLI that reveals how format constraints (JSON, XML, Markdown, plain text) systematically bias LLM outputs — based on July 2026 ArXiv research
llm-context-fmt
Run one prompt across multiple output format constraints and measure how format instructions change model behavior.
Why
Many teams assume output formatting instructions only change structure, not meaning. In practice, constraints like “JSON only” or “respond as CSV” often alter lexical choices, response length, and even the content direction.
llm-context-fmt helps you quantify that shift by:
- Running the same prompt across multiple format constraints
- Collecting per-format response metrics (token count, lexical diversity, sentiment proxy)
- Computing cross-format similarity and a format sensitivity score
- Producing terminal-friendly output and machine-readable JSON for analysis pipelines
Installation
Requirements
- Python
>=3.10 - Access to at least one supported LLM provider API key
Install from source
git clone <your-repo-url>
cd llm-context-fmt
pip install -e .
Install developer dependencies
pip install -e ".[dev]"
Usage
Basic command
llm-context-fmt "Explain why caching improves API latency."
Built-in demo prompt
Runs the built-in “Pick a word” prompt to showcase format sensitivity on a simple query.
llm-context-fmt --demo
Select specific formats
llm-context-fmt \
--formats plain,json,xml,markdown \
"Describe the lifecycle of a butterfly."
Use JSON Schema-constrained output
llm-context-fmt \
--formats plain,json-schema \
--schema '{"type":"object","properties":{"answer":{"type":"string"}},"required":["answer"]}' \
"Name one city in Japan."
You can also pass a schema file:
llm-context-fmt \
--formats json-schema \
--schema ./schema.json \
"Summarize this topic in one sentence."
Run multiple trials per format
Useful for reducing one-off sampling noise and averaging metric values.
llm-context-fmt \
--runs 5 \
--temperature 0.7 \
"Give three practical stress-management tips."
Emit JSON for downstream analysis
llm-context-fmt \
--output json \
--formats plain,json,xml \
"Write a one-paragraph product summary."
Print both table and JSON
llm-context-fmt \
--output both \
"Compare monolith and microservice architectures."
Use custom format templates
Named custom template:
llm-context-fmt \
--format-template "yaml=Respond in valid YAML with keys title and bullets." \
--formats plain,yaml \
"Outline a 2-week onboarding plan."
Unnamed templates are auto-named as custom_1, custom_2, etc.
CLI Options Reference
| Option | Type | Default | Description |
|---|---|---|---|
PROMPT | argument | none | Prompt to evaluate. Required unless --demo is used. |
--formats | comma-separated string | plain,json,xml,markdown,csv,list | Formats to run. Supports built-ins plus custom template names. |
--model | string | deepseek/deepseek-v4-flash | Model identifier passed to the selected provider API. |
--provider | enum | openrouter | Provider to use: openrouter, openai, anthropic. |
--api-base | string | provider default | Override base URL (useful for OpenAI-compatible endpoints). |
--api-key | string | env var | Explicit API key (otherwise read from provider-specific env var). |
--schema | string | none | JSON schema as inline JSON or a file path (required for json-schema format). |
--demo | flag | false | Runs built-in prompt instead of requiring PROMPT. |
--output | enum | table | Output mode: table, json, or both. |
--max-tokens | integer | 1024 | Max tokens requested from model response. Must be >= 1. |
--temperature | float | 0.7 | Sampling temperature passed to provider API. |
--runs | integer | 1 | Number of calls per format. Must be >= 1. |
--format-template | repeatable string | none | Custom format template (name=template) or unnamed template text. |
--help | flag | n/a | Shows help message and exits. |
Supported Built-in Formats
plainjsonxmlmarkdowncsvlist-
json-schema(requires--schema)
Output Format
Table output (--output table)
Displays a terminal table with:
-
Format: format name tested -
Output (truncated): response preview (up to 500 chars) -
Token Cnt: estimated token count (character-length heuristic) -
Word Cnt: total detected words -
Lex Div: lexical diversity (unique_words / total_words) -
Status:ok,refused, orerror
Then prints:
- prompt preview
- model name
- aggregate
Format Sensitivity ScoreandLength variance
JSON output (--output json or --output both)
High-level shape:
{
"prompt": "string",
"model": "string",
"provider": "string",
"formats": {
"<format_name>": {
"output": "string",
"token_count": 0,
"word_count": 0,
"lexical_diversity": 0.0,
"sentiment_score": 0.0,
"structural_features": ["..."],
"status": "ok|refused|error",
"errors": []
}
},
"comparison": {
"overlap_scores": {
"plain_json": 0.0
},
"format_sensitivity_score": 0.0,
"length_variance": 0.0,
"structural_features": {
"<format_name>": ["..."]
}
},
"raw_runs": {
"<format_name>": [
{
"format_name": "string",
"run_index": 0,
"output": "string",
"refused": false,
"error": null
}
]
}
}
Environment Variables
-
OPENROUTER_API_KEY: used when--provider openrouter(default provider) -
OPENAI_API_KEY: used when--provider openai -
ANTHROPIC_API_KEY: used when--provider anthropic
--api-key overrides environment variable lookup.
--api-base overrides provider default base URL.
Exit Codes
-
0: success -
1: provider configuration error (for example, missing API key) -
3: no valid responses received
Argument and validation issues are reported by Click as usage errors.
Testing
Run tests with:
pytest
License
MIT License. See LICENSE for details.