Architecture - arimxyer/models GitHub Wiki
This page describes the internal architecture of models for contributors and developers. It covers the module structure, data flow, async patterns, and key design decisions.
- Language: Rust (stable)
- TUI framework: ratatui with crossterm backend
- Async runtime: tokio
-
HTTP client: reqwest with
rustls-tls-native-roots(loads certificates from the OS trust store) - Serialization: serde + serde_json + toml
- CLI framework: clap (derive)
- Markdown parsing: comrak (CLI only), custom regex-based converter (TUI)
โโโโโโโโโโโโโโโโ
โ models.dev โ
โ API โ
โโโโโโโโฌโโโโโโโโ
โ
โโโโโโโโโโโโโโโโ โ โโโโโโโโโโโโโโโโ
โ Artificial โ โ โ GitHub โ
โ Analysis โ โ โ REST API โ
โ (CDN) โ โ โ โ
โโโโโโโโฌโโโโโโโโ โ โโโโโโโโฌโโโโโโโโโ
โ โ โ
โผ โผ โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ tokio::spawn tasks โ
โ (background fetch workers) โ
โโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ mpsc channels
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Message enum โ
โ (ModelsLoaded, BenchmarksLoaded, โ
โ AgentsLoaded, StatusLoaded, ...) โ
โโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ App::update() โ
โ (state mutation) โ
โโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ draw() โ
โ (render current state) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
The TUI follows an Elm-architecture pattern:
-
Events arrive as crossterm
KeyEvents or asyncMessages via mpsc channels -
event.rsmaps key events toMessagevariants using theNavActiondedup pattern -
App::update()processes messages and mutates state -
draw()renders the current state to the terminal
There are no callbacks. All state changes flow through the Message enum.
src/tui/
โโโ mod.rs # Startup, event loop, async channel handling
โโโ app.rs # App struct, Tab enum, Message enum, update() logic
โโโ event.rs # Keybinding -> Message mapping, NavAction dedup
โโโ ui.rs # draw(), shared helpers (focus_border, caret, selection_style)
โโโ markdown.rs # Custom regex-based markdown converter
โโโ models/
โ โโโ app.rs # ModelsApp state, Focus, Filters, SortOrder
โ โโโ render.rs # Models tab rendering
โโโ agents/
โ โโโ app.rs # AgentsApp state, AgentFocus, AgentSortOrder
โ โโโ render.rs # Agents tab rendering, picker modal
โโโ benchmarks/
โ โโโ app.rs # BenchmarksApp state, BenchmarkFocus, BottomView
โ โโโ render.rs # Benchmarks tab rendering
โ โโโ compare.rs # H2H table, scatter plot
โ โโโ radar.rs # Radar chart rendering
โโโ status/
โ โโโ app.rs # StatusApp state, StatusFocus, panel focus enums
โ โโโ render.rs # Status tab dispatch + shared helpers
โ โโโ overall.rs # Overall dashboard rendering
โ โโโ detail.rs # Provider detail rendering
โโโ widgets/
โโโ scrollable_panel.rs # ScrollablePanel (bordered scroll with scrollbar)
โโโ scroll_offset.rs # ScrollOffset (Cell<u16> for render-time writeback)
โโโ soft_card.rs # SoftCard (health-colored accent stripe cards)
โโโ comparison_legend.rs # ComparisonLegend (benchmarks compare views)
Each tab follows the same pattern: app.rs for state and types, render.rs for drawing. The tab's mod.rs re-exports via pub use app::*.
src/
โโโ data.rs # Provider/Model data structures (models.dev API)
โโโ api.rs # Model data fetching
โโโ config.rs # User config file (TOML)
โโโ formatting.rs # Shared utilities (truncate, parse_date, format_tokens, etc.)
โโโ provider_category.rs # Provider categorization logic
โโโ agents/
โ โโโ data.rs # Agent, GitHubData, AgentEntry types
โ โโโ loader.rs # Load embedded agents.json
โ โโโ detect.rs # Version detection (runs CLI commands)
โ โโโ github.rs # GitHub API fetching (releases, repo metadata)
โ โโโ cache.rs # Disk cache with ETag conditional fetching
โ โโโ changelog_parser.rs # Comrak AST -> normalized IR (ChangelogBlock)
โ โโโ health.rs # Agent-to-status-provider mapping
โโโ benchmarks/
โ โโโ store.rs # BenchmarkStore/BenchmarkEntry types
โ โโโ fetch.rs # CDN fetcher
โ โโโ traits.rs # AA <-> models.dev matching
โโโ status/
โโโ types.rs # Status IR types (ProviderStatus, Incident, etc.)
โโโ registry.rs # Provider registry and strategy mapping
โโโ assessment.rs # Health assessment logic
โโโ fetch.rs # Multi-source status fetching
โโโ adapters/ # Per-platform parsers (Statuspage, BetterStack, etc.)
src/cli/
โโโ mod.rs # Module index
โโโ picker.rs # PickerTerminal, nav helpers, shared styles
โโโ models.rs # Models picker (filter, sort, preview)
โโโ benchmarks.rs # Benchmarks picker
โโโ agents.rs # Clap schema, command dispatch
โโโ agents_ui.rs # Release browser, changelog search, source picker
โโโ list.rs # `models list` subcommand
โโโ search.rs # `models search` subcommand
โโโ show.rs # `models show` subcommand
โโโ styles.rs # Shared CLI colors
All three inline pickers (models, benchmarks, agents) use ratatui Viewport::Inline with the shared PickerTerminal wrapper for raw mode lifecycle.
Background fetches use tokio::spawn + mpsc channels:
- At startup, background tasks are spawned for model, benchmark, agent, and status data fetching
- Each task sends results through an mpsc channel as
Messagevariants - The main event loop polls both the terminal (key events) and the mpsc receiver
- When a message arrives,
App::update()processes it and the nextdraw()call reflects the new state
The app never blocks on network calls. Loading states are shown until data arrives.
event.rs defines a NavAction enum to avoid duplicating keybinding logic:
NavAction: Down | Up | First | Last | PageDown | PageUp | FocusLeft | FocusRight | Search | ClearEsc
parse_nav_key() maps crossterm KeyCode to NavAction (handling vim keys, arrow keys, Page Up/Down, etc. in one place). Each tab-specific handler then converts NavAction to tab-specific Message variants.
- No disk cache for benchmarks -- data is fetched fresh from CDN on every launch. The store is empty until the CDN responds.
-
Embedded agent catalog --
data/agents.jsonis compiled into the binary viainclude_str!. No runtime file dependency. - ETag conditional fetching for GitHub -- minimizes API calls. Cache stores ETags alongside data; 304 responses reuse cached data.
-
ScrollOffset(Cell<u16>)-- interior mutability enables render-time scroll clamping and writeback without requiring&mut selfin render functions. -
rustls-tls-native-roots-- uses OS certificate store instead of bundled certs, supporting corporate TLS-inspecting proxies. - Separate markdown renderers -- CLI uses comrak (CommonMark AST) for plain-text changelog output; TUI uses a custom regex-based converter that preserves inline formatting for styled ratatui rendering.
| Workflow | Trigger | Purpose |
|---|---|---|
ci.yml |
PR/push | Format check, clippy, tests |
release.yml |
v* tags |
Build 5 targets, package .deb/.rpm, publish to crates.io, update AUR |
update-benchmarks.yml |
Every 30 min | Fetch AA API, commit if data changed |
Pre-release tags (containing -) skip crates.io publish and AUR update, and mark the GitHub release as prerelease.
- Create
src/tui/{tab}/withmod.rs,app.rs,render.rs - In
mod.rs:pub mod app; pub(in crate::tui) mod render; pub use app::*; - Add
pub mod {tab};totui/mod.rs - Add
{Tab}variant toTabenum intui/app.rs - Add tab-specific
Messagevariants - Implement
update()handlers intui/app.rs - Add render call in
ui.rs - Add keybinding handlers to
event.rsusingNavActionpattern - Add footer hints and help text to
ui.rs