A terminal UI for exploring the full LLM model catalog — 167 providers, ~5,691 models, instant search.
modelx connects to models.dev and gives you a fast, keyboard-driven interface for browsing every LLM provider and model: context limits, pricing, modalities, capabilities, and raw JSON. It also works as a headless CLI and a local JSON API for scripting and automation.
| Comparison · Table (specs + benchmarks) | Comparison · Bar graph (sorted best → worst, one colour per model) |
|---|---|
![]() |
![]() |
- 3-pane master-detail TUI — Providers → Models → Detail, all keyboard-navigable
- Instant startup from a local cache, with a background refresh that hot-swaps when done
- Fuzzy search across provider and model names/IDs
- Sort by name, provider, context limit, cost, release date, and more
- Filter by capability: reasoning, tool-call support, open weights, modality, minimum context
- Selection set — mark individual models (
Space), all in view (a), then export or compare - Comparison tool (
c) — a full-screen view for 2+ selected models with two display modes toggled byTab: a Table view (transposed Specs + Benchmarks table; best value per row green, worst red; copy-as-Markdown, export) and a new Bar view (grouped benchmark bar chart for Arena Elo, Coding Elo, and Math Elo; each model keeps one consistent colour across every metric group — matching the table header and the colour-matched legend below — while best → worst reads off the left-to-right sort and bar height;1/2/3toggle which metrics are shown). Opening compare shows a toast if any selected models are missing benchmark data. Models that have benchmark data are flagged with a★marker in the Models pane. - Benchmark data —
modelx benchqueries models enriched with benchmark scores; filter, sort, and export by any metric key (arena_elo,coding_elo,math_elo, …); runmodelx refreshto populate the benchmark cache; see docs/benchmarks.md - Export wizard — choose fields, format (plain list, CSV, Markdown table, JSON), and destination (clipboard or file)
- Copy to clipboard — focused value (
y) or full model JSON (Y) - Raw JSON view — toggle to the unprocessed source object for any model
- Rich headless CLI — query by any field with comparison operators (
=,!=,<,>=,~, …), sort/limit/count, choose output fields and format (plain,csv,md,json); plus dedicatedproviders,bench,fields,show,sources,refresh,api, andcompletionssubcommands; catalog auto-refreshes after 12 h so you always query fresh data - Local JSON API —
modelx apistarts a zero-dependency HTTP server exposing the full catalog on127.0.0.1:8080; optional--refresh-intervalkeeps it current in the background; ready to run in Docker - Offline mode — works from cache with
--offline - Cross-platform — Linux, macOS, Windows, FreeBSD
A recent stable Rust toolchain (rustup.rs).
git clone https://github.com/alejandro-llanes/models-explorer
cd models-explorer
cargo build --release
# binary is at target/release/modelxcargo install --path crates/modelx-clidocs/guide.md — a thorough, task-oriented guide covering every use case: the TUI (panes, search, sort, filter, comparison Table and Bar views, export), the full CLI (all subcommands, filter expressions, output formats), and the HTTP API (routes, query parameters, curl recipes, Docker).
# Launch the TUI
modelx
# Launch on a specific source (currently only models.dev is available)
modelx --source models.dev
# Never hit the network; use the local cache only
modelx --offlineOn first launch, modelx immediately begins fetching the full catalog in the background. While it loads, you see the cached data (or an empty state if no cache exists yet). A status indicator at the bottom shows when the refresh completes.
| Key | Action |
|---|---|
q / Ctrl-C |
Quit |
Tab / l |
Focus next pane |
BackTab / h |
Focus previous pane |
j / k / ↓ / ↑ |
Move cursor down / up |
g / G |
Jump to top / bottom |
/ |
Open search — searches the focused pane: filters providers when the Providers pane is focused, fuzzy-searches models when the Models pane is focused (Enter to confirm, Esc to clear) |
s |
Open sort menu (d or re-selecting a field toggles ascending/descending) |
f |
Open filter menu |
Space |
Toggle selection on focused model |
a |
Select all models in the current view |
A |
Clear the entire selection |
c |
Compare the selected models (2+) — opens the comparison view |
y |
Copy the focused field value to clipboard |
Y |
Copy the focused model as pretty JSON to clipboard |
e |
Open export wizard |
r |
Refresh the active data source |
S |
Open source picker |
? |
Toggle help overlay |
Esc |
Close the current overlay |
J (Detail pane) |
Toggle raw JSON view |
Select 2+ models with Space, then press c to open the full-screen comparison view.
If any selected models have no benchmark data a toast appears immediately:
⚠ N of M selected models have no benchmark data (or ⚠ benchmark data not loaded — run \modelx refresh`` when the benchmark cache is empty).
The comparison has two display modes — press Tab / BackTab to switch between them.
Table view (default) — a transposed table (metric rows × model columns) split into two labelled sections:
- ▌ Specs — context, output limit, per-million-token costs, and derived value metrics.
- ▌ Benchmarks — a
Matched asprovenance row (which benchmark entry each model matched, or—), then one row per metric (Arena Elo, Coding, Math, Creative, Instruction, Hard Prompts, Vision, Image Gen, Code Pass@1, ASR WER).—when a model has no benchmark data. Best value per row is green, worst red (for ASR WER the best is the lowest). The title shows a coverage note likebenchmarks: 3/4 models matched.
Bar view — a benchmark bar chart grouped by metric. Press 1, 2, or 3 to toggle
which metrics are displayed (at least one stays on; default: all three):
1— Arena Elo2— Coding Elo3— Math Elo
Each active metric forms one group of bars, one bar per compared model that has a value
for that metric (models with no value are omitted from that group). Within each group the
bars are sorted best → worst, left to right and labelled with their Elo score. Each
model keeps one consistent colour across every metric group — the same colour used for
that model in the table header and in the colour-matched legend below — so colour maps to a
model at a glance, while best → worst is conveyed by the sort order and bar height. The
models: legend at the bottom pairs each colour swatch with the full model name.
If none of the selected models has benchmark data, the Bar view shows:
No benchmark data for the selected models — run \modelx refresh`.`
Benchmark data is loaded from the local cache at startup — run modelx refresh once to
populate it. A model with no benchmark data still compares on specs in the Table view.
| Key | Action |
|---|---|
Tab / BackTab |
Switch between Table view and Bar view |
1 / 2 / 3 |
Toggle Bar view metrics (Arena / Coding / Math Elo) |
↑ / ↓ / j / k |
Scroll rows (Table view) |
PageUp / PageDown |
Page up / down (Table view) |
y |
Copy the benchmark table as a Markdown table |
e |
Open the export wizard for the compared models |
Esc / c |
Return to the browser (selection preserved) |
q / Ctrl-C |
Quit |
? |
Help |
Running modelx with no arguments opens the TUI. Passing a subcommand runs a headless action and exits. All subcommands accept these global flags:
--source <id> Use a specific registered data source
--offline Serve from cache only; error if no cache exists
--config <path> Use a custom config file
Before any data subcommand (providers, models, show) modelx checks whether the cache is missing or older than 12 hours. If so, it auto-fetches the active source and prints a short notice to stderr (stdout stays clean for pipes). Pass --offline to suppress this. Use modelx refresh to force an update at any time.
Lists the LLM providers/vendors in the catalog.
modelx providers [--filter <PATTERN>] [--regex] [--fields <keys>] [--sort <col>]
[--desc] [--limit <N>] [--count] [--format <fmt>] [--output <FILE>]
--filter is a case-insensitive substring match on provider id or name; add --regex to treat it as a regular expression. Provider columns available for --fields and --sort: id, name, npm, api, doc, env, models (where models is the provider's model count). Default fields: id,name,models.
modelx providers --filter anthro
modelx providers --fields id,name,models --sort models --desc --limit 3 --format jsonThe main query subcommand. list and export are aliases kept for compatibility.
modelx models [--filter <"FIELD OP VALUE">]… [--provider <P>] [--search <Q>]
[--regex] [--fields <keys>] [--sort <field>] [--desc]
[--limit <N>] [--count] [--format <fmt>] [--output <FILE>]
--filteris repeatable; all expressions are AND-combined. The format is"FIELD OP VALUE"— see Filter expressions below.--providernarrows by provider: case-insensitive substring on provideridorname.--searchis a case-insensitive substring across provider name, model name, and model id.--regexmakes both--providerand~/!~filter operators treat their value as a regex.--countprints a single integer (the number of matching models) instead of rows.- Default fields:
provider_id,id,name.
Examples:
# Cheapest big-context models
modelx models --filter "input_cost<=1" --filter "context_limit>=200000" \
--fields provider_id,id,context_limit,input_cost --sort input_cost --limit 5
# Anthropic models whose name contains "opus", as JSON
modelx models --provider anthropic --filter "name~opus" --fields id,name,input_cost --format json
# Count reasoning-capable models
modelx models --filter "reasoning=true" --count
# Word-form operator: context ≥ 1 M tokens
modelx models --filter "context_limit gte 1000000" --count
# Models released in 2026 or later, newest first, as a Markdown table
modelx models --filter "release_date>=2026-01-01" --sort release_date --desc --limit 3 --format md
# Regex: Anthropic claude-opus-4 or claude-sonnet-4 model IDs
modelx models --regex --provider anthropic --filter 'id~^claude-(opus|sonnet)-4' --fields id
# Alias still works
modelx export --provider anthropic --filter "name~haiku" --fields id,name,input_cost --format csvThe argument to --filter is a quoted string: "FIELD OP VALUE". Both symbol and word forms of each operator are accepted:
| Symbol | Word | Meaning |
|---|---|---|
< |
lt |
less than |
<= |
lte |
less than or equal |
= |
eq |
equals |
!= |
ne |
not equal |
>= |
gte |
greater than or equal |
> |
gt |
greater than |
~ |
contains |
contains (substring, or regex with --regex) |
!~ |
ncontains |
does not contain |
Comparison semantics depend on the field's type (inspect with modelx fields):
- number —
context_limit,output_limit,*_costfields: numeric comparison. - text — all string fields including
release_date,last_updated,knowledge: case-insensitive string comparison. Because ISO dates sort lexically,release_date>=2025-01-01works correctly. - bool —
reasoning,tool_call,open_weights, etc.: acceptstrue/false/yes/no/1/0. - list —
input_modalities,output_modalities: use~to test membership.
Missing values never satisfy an ordering or equality filter.
Alias: benchmarks. Queries models enriched with benchmark scores. Accepts all the same flags as modelx models, plus benchmark metric keys are valid in --filter, --fields, and --sort. A coverage note (<N>/<M> models have benchmark data) is printed to stderr; missing benchmark values render as —.
modelx bench [--filter <"FIELD OP VALUE">]… [--provider <P>] [--search <Q>]
[--regex] [--fields <keys>] [--sort <key>] [--desc]
[--limit <N>] [--count] [--format <fmt>] [--output <FILE>]
[--offline]
Default fields: provider_id,id,name,arena_elo,coding_elo,math_elo.
# Top Anthropic models by coding score
modelx bench --provider anthropic --fields id,name,arena_elo,coding_elo,math_elo \
--sort coding_elo --desc --limit 5
# Models with a coding Elo of 1500 or above
modelx bench --filter "coding_elo>=1500" --fields provider_id,id,coding_elo \
--sort coding_elo --desc --limit 8
# Cheap Anthropic models with their Arena Elo, as JSON
modelx bench --provider anthropic --filter "input_cost<=10" \
--fields id,name,input_cost,arena_elo --format jsonBenchmark metric keys (valid in --filter, --fields, --sort):
| Key | Label | Higher better? |
|---|---|---|
arena_elo |
Arena Elo | yes |
coding_elo |
Coding | yes |
math_elo |
Math | yes |
creative_elo |
Creative | yes |
instruction_elo |
Instruction | yes |
hard_prompts_elo |
Hard Prompts | yes |
vision_elo |
Vision | yes |
imagegen_elo |
Image Gen | yes |
code_pass_at_1 |
Code Pass@1 | yes |
asr_wer |
ASR WER | no (lower is better) |
See docs/benchmarks.md for data sources, matching rules, and caveats.
Lists every model field with its machine key, human label, and type (text, number, bool, or list). Also prints a Benchmarks section with the 10 metric keys, their labels, sources, and higher-is-better flags. Does not touch the network.
modelx fields
modelx fields --format jsonPrints the full detail for one model. Default format is json (the unprocessed source object).
modelx show <provider> <model> [--format <fmt>]
Provider and model are resolved by exact id first, then by substring. An error is printed clearly on no match or an ambiguous match.
modelx show anthropic claude-opus-4-5Force-fetches the active source and updates the on-disk catalog cache. Also refreshes the benchmark cache (all three leaderboard sources). Exits non-zero if any fetch fails.
modelx refresh
modelx refresh --source models.devLists all registered data sources and the age/status of their cached catalogs.
modelx sourcesPrints a shell completion script to stdout. Supported shells: bash, zsh, fish, powershell, elvish.
modelx completions bash > modelx.bash
source modelx.bashAll data subcommands accept --format <fmt>:
| Format | Description |
|---|---|
plain / list |
Default. One row per model; tab-separated when multiple fields are selected. |
csv |
Comma-separated values with a header row. |
md / markdown |
GitHub-flavored Markdown table. |
json |
Array of objects keyed by field machine key. |
--output <FILE> writes to a file instead of stdout.
Field keys accepted by --fields and --sort: provider_id, provider_name, id, name, description, family, status, context_limit, output_limit, input_cost, output_cost, cache_read_cost, cache_write_cost, reasoning_cost, reasoning, tool_call, structured_output, attachment, temperature, open_weights, knowledge, release_date, last_updated, input_modalities, output_modalities, reasoning_efforts. Run modelx fields to see the full list with types.
modelx api starts a local synchronous HTTP server that exposes the catalog as JSON. No async runtime, no authentication — it is a local scripting tool.
modelx api [--listen-addr <ADDR>] [--listen-port <PORT>] [--refresh-interval <DUR>]
Defaults: --listen-addr 127.0.0.1, --listen-port 8080, no auto-refresh. Pass --refresh-interval 1h (or 30s, 10m, 2d, or a bare integer = seconds) to have a background thread re-fetch and hot-swap the catalog and benchmarks on that interval.
Route table (all GET, all return application/json):
| Path | Description |
|---|---|
/health |
Status, source, model count, provider count, fetched_at, benchmark availability |
/sources |
Registered data sources with cache age |
/fields |
All model field keys + benchmark metric keys with types and metadata |
/providers |
Provider list — query params: filter, fields, sort, desc, limit, regex |
/models |
Model list — query params: filter (repeatable, AND), provider, search, regex, fields, sort, desc, limit |
/models/{provider}/{model} |
Raw source object for one model; 404 if not found |
/bench |
Models with benchmark scores — same params as /models; benchmark metric keys valid in filter/sort |
Filter values containing <, >, =, or , must be URL-encoded (e.g. <= → %3C%3D). A bad filter/field/sort returns 400 {"error":"..."}.
Examples:
modelx api --refresh-interval 1h # serve 127.0.0.1:8080, refresh every hour
curl 'http://127.0.0.1:8080/health'
curl 'http://127.0.0.1:8080/models?provider=anthropic&limit=5&fields=id,name,input_cost'
curl 'http://127.0.0.1:8080/models?filter=input_cost%3C%3D1&filter=context_limit%3E%3D200000&sort=input_cost&limit=10'
curl 'http://127.0.0.1:8080/bench?filter=coding_elo%3E%3D1500&fields=provider_id,id,coding_elo&sort=coding_elo&desc=true&limit=10'
curl 'http://127.0.0.1:8080/models/anthropic/claude-opus-4-6'See docs/guide.md for the full route reference, query-parameter semantics, and more curl recipes.
A Dockerfile in the repository root builds a fully static musl binary and runs it on a minimal Alpine image (CA certs, non-root user, /data volume for the cache). The default command runs modelx api on all interfaces.
docker build -t modelx .
# Run the API (ephemeral cache)
docker run --rm -p 8080:8080 modelx
# Persist the cache across restarts
docker run --rm -p 8080:8080 -v modelx-data:/data modelx
# Run a CLI subcommand instead
docker run --rm modelx models --provider anthropic --fields id,name --format jsonThe ENTRYPOINT is modelx, so any subcommand works. The TUI needs a real terminal and is not the Docker use case.
The release binary is optimized for startup speed and minimal size: opt-level=3, fat LTO, codegen-units=1, symbols stripped, panic="abort" (the TUI panic hook still restores the terminal before exit). Prebuilt binaries are available on the Releases page.
modelx looks for an optional TOML config file in the platform config directory. A missing file uses built-in defaults.
Default locations:
- Linux:
~/.config/modelx/config.toml - macOS:
~/Library/Application Support/dev.modelx.modelx/config.toml - Windows:
%APPDATA%\modelx\modelx\config\config.toml
Override with --config <path>.
default_source = "models.dev"
[cache]
ttl_hours = 12
[ui]
theme = "default"All keys are optional.
modelx has a DataSource abstraction that separates the source of the catalog (where the data comes from) from the LLM providers and vendors (Anthropic, OpenAI, Google, etc.) that appear inside that catalog. The current version ships with one data source: models.dev.
The fetched catalog is stored as JSON on disk:
| Platform | Cache path |
|---|---|
| Linux | ~/.cache/modelx/sources/models.dev.json |
| macOS | ~/Library/Caches/dev.modelx.modelx/sources/models.dev.json |
| Windows | %LOCALAPPDATA%\modelx\modelx\cache\sources\models.dev.json |
Cache writes are atomic (write to a temporary file in the same directory, then rename). The default TTL is 12 hours (cache.ttl_hours in config); modelx starts from the cache regardless of age and refreshes in the background. Pass --offline to suppress all network requests.
See docs/data-sources.md for the full caching spec and how to add new data sources.
| Platform | Status |
|---|---|
| Linux (X11 / Wayland) | Supported |
| macOS | Supported |
| Windows | Supported |
| FreeBSD | Supported |
models-explorer/
├── Cargo.toml # virtual workspace manifest
├── crates/
│ ├── modelx-core/ # domain types, field registry, query engine (pure, no I/O)
│ ├── modelx-datasource/ # DataSource trait, registry, models.dev HTTP source
│ ├── modelx-cache/ # atomic on-disk cache (platform directories)
│ ├── modelx-export/ # renders selections to plain list / CSV / Markdown / JSON
│ ├── modelx-tui/ # ratatui UI: state machine, widgets, event loop, theme
│ └── modelx-cli/ # binary `modelx`: clap CLI, config, wiring, background refresh
└── docs/
├── architecture.md # crate contracts and integration details
├── usage.md # full TUI and CLI usage guide
└── data-sources.md # caching, offline use, adding new sources
See docs/architecture.md for the full crate-level contracts and dependency graph.
# Run all tests
cargo test --workspace
# Lint (warnings are errors)
cargo clippy --workspace --all-targets -- -D warnings
# Format check
cargo fmtThe core, datasource, cache, and export crates are unit-tested; datasource and export tests run against committed fixture files.
- Clipboard persistence on Linux/X11: content copied with
yorYmay not survive aftermodelxexits unless a clipboard manager (e.g.xclip,clipman,copyq) is running. This is a known limitation of the underlyingarboardlibrary on X11. Wayland is unaffected. - Single data source: models.dev is the only data source in this release. The
DataSourcetrait and registry are in place for additional sources in the future.
MIT — see LICENSE.
Model data is provided by models.dev — a comprehensive, community-maintained catalog of LLM providers and models. Many thanks to the models.dev contributors.



