PROJECT 13 / 18DEVELOPER TOOLSGO

Released · v0.1.1

ModelTUI.

An explorable model catalog, inside the terminal.

4catalog perspectives
45 sminimum request spacing
15 minautomatic refresh cadence
01 / IDEA02 / SYSTEM03 / PLAYGROUND04 / DECISIONS05 / SOURCE
01 / THE IDEA

A closer look.

A Go terminal application for the models.dev catalog. It connects canonical models, provider-specific offerings, labs, capabilities and prices in four browsable views, with conditional refresh, disk caching and an embedded offline snapshot.

The same model can appear under several providers with different prices and limits. A useful catalog needs to preserve that distinction, expose the relationships and remain usable when the network is unavailable.

01

Four views of one catalog

Models, Providers, Offerings and Labs are built from the same typed catalog. A canonical model can be traced to provider offerings, while provider-specific cost and limit fields remain attached to the offering.

02

Polite, conditional refresh

The network path respects a 45-second minimum spacing, ETag conditional requests and 429 Retry-After backoff. Idle auto-refresh uses a 15-minute cadence; repeated refresh keys do not bypass the floor.

03

An offline fallback chain

A failed live fetch falls back to disk cache and then a bundled catalog snapshot. The interface carries source labels, including stale or offline cache states.

04

A terminal interface with motion

Bubble Tea handles the interaction loop, capability forms narrow results and fuzzy filtering drives browsing. Harmonica spring state animates the layout while a detail view exposes the catalog fields.

02 / UNDER THE SURFACE

One catalog, several connected views

Inspect the network/cache branches and the index that connects models to offerings.

DRAG TO PAN · SELECT A NODE · + / − TO ZOOM

Read the architecture as text
  1. Refresh intent — Space, Ctrl+R or an idle refresh message initiates catalog refresh. UI state receives a RefreshResult and keeps the origin label.
  2. Spacing + backoff — CanRefresh checks persisted BackoffUntil and LastRequest. Both ordinary and force refresh respect the 45-second minimum.
  3. Conditional GET — fetchCatalogConditional requests catalog.json with the saved ETag. RefreshCatalog serializes requests using a mutex and records the request time before the network call.
  4. 429 response — Retry-After becomes a persisted backoff deadline, capped at 30 minutes. If the header supplies no usable delay, the fallback is twice the minimum spacing.
  5. Disk catalog — Catalog JSON is written to a temporary file and renamed into place. A 304 response reuses this cache rather than parsing a fresh payload.
  6. Embedded fallback — The executable includes catalog.snapshot.json. LoadCatalog uses it only when neither live data nor readable disk cache is available.
  7. Typed parsing — ParseCatalog unmarshals JSON, fills nil model/provider maps and supplies missing IDs from map keys. It produces a Catalog before indexing.
  8. Sorted index — BuildIndex sorts models and providers, flattens provider model maps into offerings, and groups canonical models by lab prefix.
  9. Canonical models — Canonical models carry model identity and capabilities. They are a different entity from provider offerings and do not own offering prices.
  10. Provider offerings — Offerings pair ProviderID and ProviderName with an OfferingModel. OfferingsForCanonical matches full IDs or trailing ID segments.
  11. Providers + labs — Providers retain their hosted models. Labs group canonical models by the prefix before a slash, with display names normalized for known organizations.
  12. Capability filter — Filters include reasoning, tools, attachments, open weights, structured output, multimodal and free offerings. Free-price filtering is not applied as if canonical models owned prices.
  13. Detail viewport — Detail rendering exposes the selected catalog record and related information. It presents upstream data; it does not execute a model benchmark.
  14. Spring animation — SpringPair holds position, velocity and target. A Harmonica spring updates at the animation cadence and settles near the target.
03 / INTERACTIVE STUDY

When should a catalog refresh?

Explore the spacing and Retry-After gates behind a responsive catalog that avoids unnecessary network traffic.

CHANGE THE INPUTS

Illustrative refresh policy, not live models.dev traffic. The source checks active backoff first, then the 45-second spacing floor. ETag reuse and offline cache are separate response paths; all catalog examples are synthetic.

ILLUSTRATIVE MODELLIVE

04 / ENGINEERING CHOICES

Why it works this way.

01

Preserve canonical versus offered models

A model identity and a provider’s offering have different fields. BuildIndex keeps them separate and exposes the relationship, preventing model-level capabilities from being confused with provider pricing.

02

Persist refresh state

ETag, last request, last success and backoff deadline survive the immediate fetch. A mutex serializes refresh work and the force flag still respects rate policy.

03

Ship a usable offline baseline

The fallback chain prioritizes live data, then cached catalog, then an embedded snapshot. Older data stays inspectable, with source labels signaling its freshness limits.

05 / OPEN THE SOURCE

Trace it back.

Implementation details, examples, and project documentation.

Scope & limitations

  • Prices, capabilities and benchmark values come from models.dev; ModelTUI does not independently measure or guarantee them. Disk and embedded data can be older than the live catalog.
  • Canonical-to-offering lookup uses ID and suffix matching. Similar naming is a lookup heuristic, not independent proof that two offerings have identical behavior.

Architecture and descriptions reflect the linked repository snapshot. The playground explains a mechanism; it does not execute the repository or report measured performance.