Skip to content

feat(models): show the model picker as aligned columns - #242

Merged
duyet merged 2 commits into
mainfrom
feat/model-picker-columns
Oct 29, 2026
Merged

duyet merged 2 commits into
mainfrom
feat/model-picker-columns

Conversation

@duyet

@duyet duyet commented Oct 29, 2026

Copy link
Copy Markdown
Contributor

What changes for users

anyr claude model, anyr models --pick, and the other model pickers now show each model as a row of aligned columns. You can compare price, intelligence score, and context without opening each model.

  • Columns. The columns are model id, input price, output price, smart, and context, plus tags.
    • Prices are pricing.input_per_1m and pricing.output_per_1m.
    • Smart is the Artificial Analysis intelligence_index from the public /api/v1/analytics/model-benchmarks feed. It has a 2 s timeout and falls back to a dash.
    • Context is context_length. A value of 0 counts as missing.
    • Tags are free (billing is free), new (created in the last 14 days, the same window as the web catalog), and sale (list_pricing is above pricing).
  • No invented values. A missing value draws a dim —. An empty tags cell is blank, not a dash.
  • Width. At 80 columns every column fits, and long ids are cut with …. When a terminal is narrower, the tags column drops first, then smart, then context, then price.
  • Detail block. It shows the highlighted model's full id and name, plus its input, cached input, output and context.
    • It also shows a cost gauge, ranked by a 3:1 blended price among the priced rows.
    • Cached input appears only when an upstream charges exactly the headline input and output price. Otherwise it is —. Today that is 2 of 290 models, because BYOK hops list $0.
  • Legend and footer. A one-line legend explains the tags. The footer lists the keys: type to search · ↑↓ select · ↵ confirm · esc cancel.
  • Unchanged behavior.
    • anyrouter/auto is still pinned first, and typeahead still matches ids only, so typing 1.2 does not match $1.25. @preset/* rows stay.
    • The layout is plain and top-left, with thin rules and no boxes.
    • With NO_COLOR, the picker draws no color codes and keeps bold only.
    • Non-TTY and --dump-tui output is plain text.
  • › prefix. The › prefix on every anthropic/claude-* row is gone. It came from item_icon matching "claude" in the id.

Shape

  • http.rs parses pricing, created, and per_upstream cache prices into CatalogModel (ModelPricing). It also adds fetch_intelligence.
  • cmd/model_picker.rs (native) builds one ModelRow per id with model_rows. Then model_table turns the rows into a PickTable.
  • tui/table.rs is the generic layout. layout(table, width) picks the column widths and the columns to drop. row and header lay out the text.
  • The live renderer and the --dump-tui dump both use the same layout. Other pickers do not change.
  • model_pick_label and picker_choice_list are deleted. Their tests now use the row API, and they check the same behavior: preset pinned, typeahead, and dump order.

Before / after

Same data for both binaries: a saved authenticated /api/v1/models response (290 models) and the benchmarks feed, served locally. The isolated ANYROUTER_HOME holds no live key. Live captures are from tmux capture-pane after models --pick --agent claude and three ↓ presses.

Before, 80 columns

claude model
type to search · anyrouter/auto is pinned · enter to select
⌕ █
────────────────────────────────────────────────────────────
  anyrouter/auto
  @preset/aidr  ·  1M
  @preset/chmonitor
❯ @preset/duyet-agent
  @preset/hermes-agent  ·  1M
  agents-a1/agents-a1-free
  agnes-ai/agnes-2.5-flash
  agnes-ai/agnes-2.5-pro  ·  1M
  agnes-ai/agnes-2.5-pro-alpha  ·  1M
  agnes-ai/agnes-3.0-flash

After, 80 columns

claude model
⌕ █ type to search
────────────────────────────────────────────────────────────
  model                                  input  output  smart  context
  anyrouter/auto                            $0      $0      —       1M  free
  @preset/aidr                          $0.375   $1.88      —       1M
  @preset/chmonitor                      $0.06    $0.4      —     200K
❯ @preset/duyet-agent                    $0.06    $0.4      —     200K
  @preset/hermes-agent                      $0      $0      —       1M  free
  agents-a1/agents-a1-free                  $0      $0      —     262K
  agnes-ai/agnes-2.5-flash               $0.03   $0.15      —     512K
  agnes-ai/agnes-2.5-pro                 $0.45    $0.9      —       1M
  agnes-ai/agnes-2.5-pro-alpha           $0.45    $0.9      —       1M
  agnes-ai/agnes-3.0-flash               $0.03   $0.15      —     512K
  anthropic/claude-fable-5                 $10     $50   59.9       1M
  anthropic/claude-fable-5-1               $10     $50      —       1M
  anthropic/claude-haiku-5-5              $0.1    $0.5      —       1M
  anthropic/claude-haiku-latest           $0.1    $0.5      —       1M
  anthropic/claude-opus-4-1                $15     $75      —       1M
  anthropic/claude-opus-4-5                 $5     $25   34.7       1M
  anthropic/claude-opus-4-6                 $5     $25   37.8       1M
  anthropic/claude-opus-4-7                 $5     $25   53.5       1M
  anthropic/claude-opus-4-8                 $5     $25   55.7       1M
  anthropic/claude-opus-5                   $5     $25      —       1M
  anthropic/claude-opus-5-5                 $4     $20      —       1M
  anthropic/claude-opus-5-fast             $10     $50      —       1M
  ↓ more below

@preset/duyet-agent  duyet-agent
input $0.06 / 1M   cached input —   output $0.4 / 1M   context 200,000 tokens
cost  low ━━━━━━━━━●────────────── high

$ per 1M tokens  ·  smart AA index  ·  free $0  new 14 days  sale below list
type to search  ·  ↑↓ select  ·  ↵ confirm  ·  esc cancel

After, 120 columns, typing opus

claude model
⌕ opus█
────────────────────────────────────────────────────────────
  model                                           input  output  smart  context
❯ anthropic/claude-opus-4-1                         $15     $75      —       1M
  anthropic/claude-opus-4-5                          $5     $25   34.7       1M
  anthropic/claude-opus-4-6                          $5     $25   37.8       1M
  anthropic/claude-opus-4-7                          $5     $25   53.5       1M
  anthropic/claude-opus-4-8                          $5     $25   55.7       1M
  anthropic/claude-opus-5                            $5     $25      —       1M
  anthropic/claude-opus-5-5                          $4     $20      —       1M
  anthropic/claude-opus-5-fast                      $10     $50      —       1M
  anthropic/claude-opus-latest                       $4     $20      —       1M
  openrouter/fusion                                  $0      $0      —       1M
  xiaomi/mimo-v2.6-pro-ultraspeed                 $4.35    $8.7      —       1M
  anthropic/claude-sonnet-4-5                        $3     $15      —       1M
  anthropic/claude-sonnet-4-6                        $3     $15   34.3       1M
  anthropic/claude-sonnet-5                          $2     $10      —       1M
  anthropic/claude-sonnet-latest                     $2     $10      —       1M
  cohere/command-a-plus                            $0.3    $1.5      —     192K
  anthropic/claude-haiku-latest                    $0.1    $0.5      —       1M

anthropic/claude-opus-4-1  Claude Opus 4.1
input $15 / 1M   cached input —   output $75 / 1M   context 1,000,000 tokens
cost  low ━━━━━━━━━━━━━━━━━━━━━━━● high

$ per 1M tokens  ·  smart AA index  ·  free $0  new 14 days  sale below list
type to search  ·  ↑↓ select  ·  ↵ confirm  ·  esc cancel

Checks

  • cargo fmt --check: clean.
  • cargo clippy --locked --all-targets: 0 warnings.
  • cargo test --locked --all-targets: 1044 passed, 0 failed.
  • cargo check --lib --no-default-features --target wasm32-unknown-unknown: builds, with the same 25 warnings as main (none new).
  • New tests cover:
    • column offsets across rows at 80 and 120 columns;
    • width-based dropping at 120, 60 and 50 columns;
    • the missing-data dash, and blank (not dash) for no tags;
    • typeahead that does not match price text;
    • cached input taken only from a matching upstream;
    • scores missing from the benchmarks feed;
    • the sale tag;
    • the detail block.

🤖 Generated with Claude Code

https://claude.ai/code/session_01TMDU43edyk59D2Wis5si6x

Each row shows input and output price per 1M, the Artificial Analysis
intelligence index, context, and free/new/sale tags, all from the
catalog. A missing value draws a dim dash. Columns drop by width, and a
detail block shows the highlighted model's prices and a cost gauge.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TMDU43edyk59D2Wis5si6x

Co-authored-by: Duyet Le <me@duyet.net>
Co-authored-by: duyetbot <bot@duyet.net>
@github-actions

github-actions Bot commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

anyr size and startup

Startup is wall time for a cold anyr --version / anyr --help (median of 21 runs). Size is the stripped release binary, or the .wasm for the browser demo.

Size budget: anyr-linux-x86_64 must stay ≤ 4.0 MiB (4194304 bytes) stripped.

Asset Kind Size --version median --help median
anyr-darwin-arm64 native 2.5 MiB 5.7 ms 4.69 ms
anyr-darwin-x86_64 native 2.9 MiB 47.81 ms 44.8 ms
anyr-linux-arm64 native 2.9 MiB 0.87 ms 0.88 ms
anyr-linux-x86_64 native 3.4 MiB 1.05 ms 1.03 ms
anyr-windows-x86_64.exe native 2.9 MiB 8.43 ms 7.79 ms
anyr.wasm wasm 252.3 KiB — —

Budget check: ok

raw timings
[
  {
    "asset": "anyr-darwin-arm64",
    "kind": "native",
    "path": "/Users/runner/work/cli/cli/anyr-darwin-arm64",
    "bytes": 2570504,
    "size": "2.5 MiB",
    "version": "0.1.37 (built 2026-10-29 04:22:53)",
    "target": "aarch64-apple-darwin",
    "os": "macos-latest",
    "startup_version": {
      "n": 21,
      "min_ms": 5.05,
      "median_ms": 5.7,
      "p95_ms": 9.14,
      "mean_ms": 6.54
    },
    "startup_help": {
      "n": 21,
      "min_ms": 3.86,
      "median_ms": 4.69,
      "p95_ms": 7.46,
      "mean_ms": 5.1
    }
  },
  {
    "asset": "anyr-darwin-x86_64",
    "kind": "native",
    "path": "/Users/runner/work/cli/cli/anyr-darwin-x86_64",
    "bytes": 3066424,
    "size": "2.9 MiB",
    "version": "0.1.37 (built 2026-10-29 04:23:24)",
    "target": "x86_64-apple-darwin",
    "os": "macos-latest",
    "startup_version": {
      "n": 21,
      "min_ms": 39.23,
      "median_ms": 47.81,
      "p95_ms": 55.49,
      "mean_ms": 47.93
    },
    "startup_help": {
      "n": 21,
      "min_ms": 37.48,
      "median_ms": 44.8,
      "p95_ms": 54.85,
      "mean_ms": 44.84
    }
  },
  {
    "asset": "anyr-linux-arm64",
    "kind": "native",
    "path": "/home/runner/work/cli/cli/anyr-linux-arm64",
    "bytes": 3020048,
    "size": "2.9 MiB",
    "version": "0.1.37 (built 2026-10-29 04:22:27)",
    "target": "aarch64-unknown-linux-gnu",
    "os": "ubuntu-24.04-arm",
    "startup_version": {
      "n": 21,
      "min_ms": 0.8,
      "median_ms": 0.87,
      "p95_ms": 0.97,
      "mean_ms": 0.87
    },
    "startup_help": {
      "n": 21,
      "min_ms": 0.82,
      "median_ms": 0.88,
      "p95_ms": 0.91,
      "mean_ms": 0.88
    }
  },
  {
    "asset": "anyr-linux-x86_64",
    "kind": "native",
    "path": "/home/runner/work/cli/cli/anyr-linux-x86_64",
    "bytes": 3554960,
    "size": "3.4 MiB",
    "version": "0.1.37 (built 2026-10-29 04:22:29)",
    "target": "x86_64-unknown-linux-gnu",
    "os": "ubuntu-latest",
    "startup_version": {
      "n": 21,
      "min_ms": 1.01,
      "median_ms": 1.05,
      "p95_ms": 1.17,
      "mean_ms": 1.07
    },
    "startup_help": {
      "n": 21,
      "min_ms": 1.01,
      "median_ms": 1.03,
      "p95_ms": 1.08,
      "mean_ms": 1.04
    }
  },
  {
    "asset": "anyr-windows-x86_64.exe",
    "kind": "native",
    "path": "D:\\a\\cli\\cli\\anyr-windows-x86_64.exe",
    "bytes": 3027968,
    "size": "2.9 MiB",
    "version": "0.1.37 (built 2026-10-29T04:23:15Z)",
    "target": "x86_64-pc-windows-msvc",
    "os": "windows-latest",
    "startup_version": {
      "n": 21,
      "min_ms": 7.86,
      "median_ms": 8.43,
      "p95_ms": 8.84,
      "mean_ms": 8.38
    },
    "startup_help": {
      "n": 21,
      "min_ms": 7.37,
      "median_ms": 7.79,
      "p95_ms": 8.04,
      "mean_ms": 7.8
    }
  },
  {
    "asset": "anyr.wasm",
    "kind": "wasm",
    "path": "/home/runner/work/cli/cli/target/wasm-pkg/anyr_cli_bg.wasm",
    "bytes": 258338,
    "size": "252.3 KiB",
    "version": "wasm",
    "target": "wasm32-unknown-unknown",
    "os": ""
  }
]

The column keeps a short price, and a paid price under $0.0005 shows as <$0.001 instead of $0. A context just under 1M shows 1M, not 1000K. A legend cut on an exact fit now ends in an ellipsis.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TMDU43edyk59D2Wis5si6x

Co-authored-by: Duyet Le <me@duyet.net>
Co-authored-by: duyetbot <bot@duyet.net>
@duyet
duyet merged commit adaae15 into main Oct 29, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant