feat(tui): show pricing & capabilities in /model picker#2511
Merged
dgageot merged 1 commit intodocker:mainfrom Apr 26, 2026
Merged
Conversation
The `/model` dialog now reads as a proper table with a details panel, making it much easier to compare models and pick one. Catalog metadata (pricing, context window, modalities, family) is pulled best-effort from the models.dev store and surfaced both inline and in a stable panel below the list. Runtime - Extend `runtime.ModelChoice` with optional `Family`, `InputCost`, `OutputCost`, `CacheReadCost`, `CacheWriteCost`, `ContextLimit`, `OutputLimit`, `InputModalities`, `OutputModalities` fields. - Populate them from models.dev for both configured models (`AvailableModels`) and catalog entries (`buildCatalogChoices`) via small `populateCatalogMetadata` / `applyCatalogMetadata` helpers (slice fields cloned with `slices.Clone` so cached models stay immutable through `ModelChoice`). TUI - Render the model list as a 3-column table: name + badges on the left, then right-aligned `Input/1M`, `Output/1M`, and `Context` columns, with a static column header above. Missing values render as muted em-dashes. - Drop the inline `provider/model` description from each row and move it into a 4-line details panel under the list, alongside the full pricing breakdown (input, output, cache read/write, per-1M tokens), context/output limits, and modality flow (`text, image → text`). The panel keeps a stable height; "unavailable" replaces missing data so the dialog never resizes. - Pack row styling into a `pickerRowPalette` (built by `pickerRowStyles(selected)`) and details styling into `detailsStyles`, so renderers don't pass a swarm of styles around. - Compose `renderModel` from `renderRowName` + `renderRowStats` and share `pickerNameColWidth` between row and header for one source of truth on table layout. - `formatCostPerMillion` keeps four decimals for sub-cent prices so values in (0, 0.005) don't collapse to a misleading "$0.00". Tests - Cover formatting helpers (`formatCostPerMillion`, `formatContextCell`, `rightAlign`, `modelReference`) and dialog rendering: column headers and per-row stats are present, the provider/model reference is gone from rows, the details panel exposes the full breakdown, and missing-info models fall back to "unavailable". Assisted-By: docker-agent
trungutt
approved these changes
Apr 26, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The
/modeldialog now reads as a proper table with a details panel,making it much easier to compare models and pick one. Catalog metadata
(pricing, context window, modalities, family) is pulled best-effort
from the models.dev store and surfaced both inline and in a stable
panel below the list.
What changes
Runtime
runtime.ModelChoicegains optional fields populated from models.dev(
Family,InputCost,OutputCost,CacheReadCost,CacheWriteCost,ContextLimit,OutputLimit,InputModalities,OutputModalities).LocalRuntimepopulates them for both configured models and catalogentries via small
populateCatalogMetadata/applyCatalogMetadatahelpers. Slice fields are cloned with
slices.Cloneso themodels.dev cache stays immutable through
ModelChoice.TUI
then right-aligned Input/1M, Output/1M, and Context
columns, with a static column header above. Missing values render
as muted em-dashes.
provider/modeldescription has moved out of the rowinto a 4-line details panel below the list:
provider/model(and family if not redundant)text, image → text"unavailable" so the dialog never resizes.
pickerRowPalette/detailsStylesinstead ofbeing threaded through renderer arguments.
renderModelis now athree-line composition over
renderRowName+renderRowStats, andthe row + header share
pickerNameColWidthfor the table layout.formatCostPerMillionkeeps four decimals for sub-cent prices sovalues in
(0, 0.005)don't collapse to a misleading"$0.00".Tests
(
formatCostPerMillion,formatContextCell,rightAlign,modelReference) and dialog rendering: column headers and per-rowstats are present, the provider/model reference is gone from rows,
the details panel exposes the full breakdown, and missing-info
models fall back to "unavailable".
Preview
Validation
mise lint, fullmise test, andmise buildall pass.Assisted-By: docker-agent