Skip to content

feat(tui): ask where chat runs before the first-run model download - #5109

Merged
kovtcharov-amd merged 4 commits into
mainfrom
feat/tui-first-run-provider-choice
Oct 15, 2026
Merged

kovtcharov-amd merged 4 commits into
mainfrom
feat/tui-first-run-provider-choice

Conversation

@kovtcharov-amd

@kovtcharov-amd kovtcharov-amd commented Oct 14, 2026 •

Copy link
Copy Markdown
Collaborator

A new user pressing Enter through first-run setup downloaded ~6 GB of local models without ever seeing that Fireworks AI or the AMD LLM Gateway were options; the choice was only reachable with the p footer key. Setup now opens by asking where chat should run, before anything downloads. Each option gets one plain line on privacy, cost and speed, and Local shows its download size and, once Lemonade can measure the PC, whether the model fits. A cloud choice still installs Lemonade (it routes the requests and runs the embeddings), then opens that provider's key form and downloads only the embedding model.

  Setting up GAIA                                                    step 1 of 3
  ──────────────────────────────────────────────────────────────────────────────
  Step 1 of 3 — where should chat run?
  Change it any time: p on this screen, /provider in chat.

  > 1. Local
       Private and free: nothing leaves this PC. Speed depends on this PC.
       Downloads ~6 GB of models. Setup picks the model that fits this PC's
       memory.

    2. Fireworks AI
       Fast on any PC. Conversations are sent to Fireworks AI, and usage is
       billed to your key.
       Needs a Fireworks AI API key. Downloads under 1 GB.

    3. AMD LLM Gateway
       Fast on any PC. Conversations are sent to your organisation's AMD LLM
       Gateway.
       Needs your gateway key (and its URL). Downloads under 1 GB.

  ──────────────────────────────────────────────────────────────────────────────
  ↑/↓ move · p all providers · enter choose · esc back to the steps

A saved model, --model, --use-claude and headless --query runs skip the question, and p still opens the full provider panel. UI-only: no agent prompt, tool, or model-selection code changed, so no eval run is needed.

Fixes #5082
Refs #5087

Test plan

  • cd tui && go test ./internal/ui/preflight/ ./internal/ui/providers/ ./internal/ui/root/ ./internal/lemonade/. New tests cover step order for a new machine, Local, Fireworks and AMD, the skip cases, the fit line, a golden of the screen above, small terminals, keys, and the host hand-offs.
  • tui/test integration package (not run locally; it builds the mock agent binary).
  • On a clean machine (no Lemonade, no ~/.gaia): gaia tui opens on the question. Choose Fireworks AI, press Enter to install Lemonade, then Enter opens the key form. After you pick a model, the last step reads "download the embedding model (under 1 GB)".
  • Same with Local: steps 2 and 3 install Lemonade and download the models, as before.

Adversarial tests

internal/ui/preflight/chat_adversarial_test.go (10 tests) attacks the first-run choice with:

  • terminals down to 20x6, 12x4, 1x1 and 0x0, and resizing while the question is up
  • a PC whose memory can't be measured, and an unknown provider
  • a re-check that lands mid-choice: Lemonade failing, the chat row gone, or fewer options than the cursor position
  • 6,000 random keypresses
  • enter pressed repeatedly after a choice, which must record it exactly once

None of them found a bug. The cursor clamp on a shorter option list is what stops an index-out-of-range panic; removing it makes the shorter-list test fail.

…download

A first-time user pressing Enter through setup downloaded ~6 GB of local
models without ever seeing that Fireworks AI or the AMD LLM Gateway were an
option; the choice was only behind the `p` footer key.

The first-run gate now opens on "where should chat run?" (step 1 of 3):
Local, with its download size and whether it fits this PC once Lemonade can
say, then Fireworks AI and AMD LLM Gateway, each with one line on privacy,
cost and speed. Lemonade is installed next either way. Local then downloads
the models as before; a cloud choice opens that provider's key form and
downloads only the embedding model. A saved choice, --model, --use-claude,
and headless runs skip the question, and `p` keeps working.
@github-actions github-actions Bot added documentation Documentation changes tui Go terminal UI (gaia-tui) labels Oct 14, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Verdict: Approve with suggestions

New users now get asked where chat should run (Local, Fireworks AI or AMD LLM Gateway) before setup downloads anything. Before this, pressing Enter through setup quietly downloaded ~6 GB of local models. I found no correctness problems. The new step is skipped whenever the answer is already known: a saved model, --model, --use-claude, or headless runs. Step numbering stays right across the trip to the provider panel and back. The docs match the new flow.

One thing before merge: the only run of the new screen is the golden test. Nobody has run it on a real machine yet. Both clean-machine items in the test plan are unchecked, and so is the tui/test integration package. The cloud path is the one to run end to end: choose Fireworks, install Lemonade, connect the key, then download only the embeddings. Unit tests can't show it works, because it hands off between three screens.

Real-world evidence

N/A: no evidence bundle was produced for this run. That stage didn't drive the terminal UI, so the verdict rests on static review. I also built the module and ran the PR's Go test packages on this runner, and they pass:

ok  	github.com/amd/gaia/tui/internal/ui/preflight	8.925s
ok  	github.com/amd/gaia/tui/internal/ui/providers	0.037s
ok  	github.com/amd/gaia/tui/internal/ui/root	0.150s
ok  	github.com/amd/gaia/tui/internal/lemonade	0.030s

The screen in the PR description comes from the golden test, not a live session. A gaia tui capture from a clean machine (or a control-API /status showing overlay: choose-chat, then the provider panel opening on the key form) would close this gap.

🔍 Technical details

Issues

🟡 No live run of the first-run flow (PR test plan). The handoff from the connect step to the provider panel and back to a rebuilt gate is covered by stubs only:

  • ConnectProviderMsg → providers.WithConnect → autoConnect on the first successful read → SelectedMsg → beginPreflightCarrying(..., gateSteps).
  • Worth running once from a cold state (no ~/.gaia, no Lemonade) per CLAUDE.md's "test from the user's real initial state". The driving-the-tui skill covers this.
  • Also check the last step's wording there. downloadStep prefers the reported size (download the models (X GB)) over embeddingStep. When Verify reports the embedder's size, the label will read "download the models (0.x GB)", not the "download the embedding model (under 1 GB)" the test plan expects.

🟢 There's no way back to the question after choosing a cloud provider without connecting it. The session-scoped chatChoice survives esc out of the provider panel. The question's row is gone, so only p → Local reverses the choice. Acceptable, since the remedy names p. A note in the terminal-hub guide would help.

🟢 The footer hardcodes the flagship id (tui/internal/ui/preflight/view.go, m.cfg.AgentID == "gaia"). The root gates the same thing on catalog.FlagshipID. If the preflight package can't import catalog, a shared constant would stop the two from drifting.

Verified

  • stepsBefore/stepsDone arithmetic: choosing an option takes 3 steps down to 2 and reads "step 2 of 3". gateSteps is carried into the rebuilt gate once and then reset (beginPreflightCarrying).
  • IsCloudProvider("local") is false. unconnectedCloud() is empty once a model is picked, so a later /provider switch to a local model isn't blocked by a stale chatChoice.
  • Headless oneshot.go builds LocalOptions without AskWhereChatRuns, so --query never stops on the question.
  • ctrl+c and the root's p interception still work while the question is up. r closes it and re-checks.

Strengths

  • The provider key form is reused through WithConnect/openSetup rather than forked. Moving the register-before-setup guard into openSetup keeps the "don't overwrite another gateway's settings" check on both paths.
  • lemonade.ChatProviders replaces the magic % 3 and the duplicated name list in the providers panel.
  • Good test coverage: step order per choice, the cases that skip the question, the fit line, small terminals, and the root handoffs.

Covers the question at 20x6, 12x4, 1x1 and 0x0, resizing while it is up, a PC
whose memory cannot be measured, an unknown provider, a re-check that lands
mid-choice (Lemonade failing, the chat row gone, fewer options than the
cursor), 6000 random keypresses, and enter mashed after a choice.
…the scripted runner's Fix

Kept both sets of fix kinds and provider fields. Key mashing in the
adversarial test started a fix while an earlier fix goroutine was still
recording, which the race detector flagged in the test double. The local
option's memory need now reads ~5 GB, from main's model sizes.
# Conflicts:
#	tui/internal/ui/providers/model.go
@kovtcharov-amd
kovtcharov-amd merged commit eaf23e7 into main Oct 15, 2026
36 of 37 checks passed
@kovtcharov-amd
kovtcharov-amd deleted the feat/tui-first-run-provider-choice branch October 15, 2026 11:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Documentation changes tui Go terminal UI (gaia-tui)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(tui): ask where chat runs as a first-run step, before the ~6 GB model download

1 participant