Reference
Everything radarmodel-ai 0.1 does, in one page. The README on GitHub says the same in the repository.
Commands
radarmodel-ai init [-config FILE] write a starter config
radarmodel-ai check [-config FILE] [flags] scan once, compare, report
radarmodel-ai watch [-config FILE] [flags] scan every interval until stopped
radarmodel-ai show [-config FILE] SOURCE list the stored models of a source
radarmodel-ai version
The config is radarmodel.json in the current folder, or the file in $RADARMODEL_CONFIG, or -config FILE.
Flags for check and watch:
| Flag | What it does |
|---|---|
-min info|warning|breaking |
Lowest severity shown at the terminal. Warning by default; the rest is counted |
-json |
Print the report as JSON |
-no-alerts |
Skip webhook, Slack and email |
-dry-run |
check only: compare, but keep the stored snapshots |
Exit codes
| Code | Meaning |
|---|---|
| 0 | Scanned, nothing breaking |
| 1 | At least one breaking change |
| 2 | A provider couldn’t be scanned, another run holds the state folder, or the config is wrong |
Breaking wins: a run with a breaking change and a failed provider exits 1, and the report shows both.
Configuration
{
"interval": "1h",
"timeout": "30s",
"min_fraction": 0.5,
"confirm_removals": true,
"state_dir": ".radarmodel",
"providers": [
{"type": "openai"},
{"type": "anthropic"},
{"type": "google"},
{"type": "openrouter"},
{"type": "openai", "name": "proxy", "base_url": "https://llm-proxy.example.com", "api_key_env": "PROXY_KEY"}
],
"watch": ["openai:gpt-4o", "anthropic:claude-sonnet-4-5", "google:gemini-2.5-flash", "openrouter:anthropic/*", "proxy:gpt-4o"],
"alerts": {
"min_severity": "warning",
"webhook": {"url_env": "RADAR_WEBHOOK_URL"},
"slack": {"url_env": "SLACK_WEBHOOK_URL"},
"email": {"host": "smtp.example.com", "port": 587, "from": "radar@example.com",
"to": ["ops@example.com"], "username_env": "SMTP_USER", "password_env": "SMTP_PASS"}
}
}
| Setting | Default | What it does |
|---|---|---|
providers |
none | The lists to read. type is openai, anthropic, google or openrouter |
providers[].name |
the type | Name used in reports, watch entries and file names. Two entries of one type need different names |
providers[].base_url |
the provider’s API | A proxy or compatible endpoint. file:// reads saved answers from disk (Linux and macOS) |
providers[].api_key_env |
OPENAI_API_KEY, ANTHROPIC_API_KEY, GEMINI_API_KEY |
Variable holding the key. OpenRouter needs none |
watch |
none | Models your app calls, as source:model. * matches anything, slashes included; a source of * means every provider |
interval |
1h |
Time between scans in watch mode. At least 1m |
timeout |
30s |
Limit for one HTTP request |
min_fraction |
0.5 |
A list with fewer than this share of the last one’s models is a failed scan. 0 turns it off |
confirm_removals |
true |
A watched removal is breaking only when a second scan agrees. false reports it on the first |
state_dir |
.radarmodel |
Where snapshots are kept, relative to the config file |
alerts.min_severity |
warning |
Lowest severity that sends an alert. A failed scan always sends one |
alerts.webhook |
none | url_env, or url for a URL with no token. Gets the JSON report |
alerts.slack |
none | url_env naming the incoming-webhook URL. Gets a text summary |
alerts.email |
none | host, port (587), from, to, and username_env and password_env if the server needs them. STARTTLS when offered |
Unknown fields are errors, and so are watch entries naming a source that isn’t configured, source names that differ only in case or punctuation, and secret variables that are named but not set.
What counts as what
Only watched models can be breaking or warnings. Every other change is info.
| Change to a watched model | Severity |
|---|---|
| Gone from the list, on two scans in a row | breaking |
| Gone from the list on one scan | warning |
| Context or output limit went down | breaking |
Lifecycle became retired (Anthropic) |
breaking |
Lifecycle left active, such as deprecated (Anthropic) |
warning |
| Got or changed a retirement date (Anthropic, OpenRouter) | warning |
| A limit is no longer published | warning |
| Price went up, or a price appeared or vanished | warning |
| Lost a supported method (Google) | warning |
Named in watch but not in the list |
warning, every run |
| New models, price cuts, bigger limits, name changes | info |
When a watched model disappears and a model with the same display name appears in the same scan, the report adds “possibly renamed to …”.
What each provider shows
| Provider | What’s compared |
|---|---|
| OpenAI | Model IDs and owner |
| Anthropic | Display name, input and output limits, lifecycle, retirement date |
| Display name, version, input and output limits, supported methods | |
| OpenRouter | Name, context length, max output, input and output price per token, retirement date |
What a failed scan is
A failed scan reports an error for that provider, changes nothing in its stored snapshot, and lets the other providers scan as usual. These are all failed scans:
- an HTTP error. 408, 429 and 5xx get three attempts in all, honouring Retry-After up to 30 seconds
- a body that isn’t valid JSON, is cut off, or has no model list
- an empty list
- a page of a paged list failing, or a paged answer where one page was expected
- fewer models than
min_fractionof the last list - a redirect to another host, refused so keys can’t follow it
- a timeout
Files
state_dir holds latest/SOURCE.json, the snapshot the next scan compares with, and the last 50 snapshots in history/SOURCE/. Each file is written whole or not at all. A lock file keeps two runs from using the folder at once; one older than 30 minutes is taken to be left from a crash.