Skip to main content
A new user runs two commands, maybe three, and has a working GAIA. Today it takes a Python install, a system-wide Lemonade install that asks for an administrator password, a model download, and knowing which of those failed when something goes wrong. After this work, GAIA carries its own Lemonade, picks the right model for the PC, and sets itself up on first launch if the user skipped that step.

1. Decisions

2. What exists today

  • Clients already prefer the embedded server. resolve_lemonade_base_url() and resolve_lemonade_api_key() read ~/.gaia/lemonade/state.json before falling back to the default port, so every GAIA client talks to a running embedded server without changes.
  • The TUI already sets itself up on first launch (chat/setup.go) and through /setup, by running gaia init --profile gaia --yes. Its setup screen also starts a stopped Lemonade by itself (tryAutoStartLemonade), but only through system launchers.
  • The installers already leave Lemonade to gaia init: install.sh, install.ps1 and the Windows desktop installer (gaia.nsi) install GAIA only.
  • What still assumes a system Lemonade:
    • gaia init finds or installs it (LemonadeInstaller: the Windows MSI, Linux packages, macOS pkg) and starts it through lemonade_launcher (tray app, systemctl, macOS app).
    • LemonadeManager.ensure_ready() only checks, and reports a stopped server as an error.
    • The TUI’s auto-start and start hints (preflight/autostart.go, lemonadeStartRemedy).
    • About 20 CI workflows run on self-hosted machines with a system Lemonade.
  • gaia with no command opens the web Agent UI, not the terminal UI; the terminal UI is a separate gaia-tui binary. gaia chat --ui, and the top-level --ui and --cli flags, are other routes into the same web UI, referenced in 43 files.
  • npx @amd-gaia/gaia fetches the TUI and agent binaries and opens the TUI, but needs the Python gaia command installed separately, and leaves no lasting gaia command.

3. PR 1: gaia init sets up embedded Lemonade

gaia init installs, starts and uses GAIA’s embedded Lemonade, and every GAIA entry point starts it when it’s stopped. A system Lemonade is no longer installed, found or started. PR 1 ships as three PRs so each one can be reviewed and tested on its own: The fallback removal waits for 1c because 18 CI workflows still reach a system Lemonade on the default port without setting LEMONADE_BASE_URL.

Changes

  1. gaia init on embedded Lemonade. Replace the system path in InitCommand (_ensure_lemonade_installed, _install_lemonade, _upgrade_lemonade, _check_version_compatibility, _find_lemonade_server, _auto_start_server, _ensure_server_running) with EmbeddedLemonade: install the pinned artifact (SHA-256 verified), start it, and install the profile’s backend through EmbeddedLemonade.install_backend (llamacpp everywhere, flm:npu for the NPU profile). Model download, the hardware-based model choice (#4176) and verification are unchanged, because they already talk to whatever resolve_lemonade_base_url() returns. No administrator prompt on any platform.
  2. gaia init --check reports embedded status: installed, running, version, and the models. An installed-but-stopped server counts as ready, since any entry point starts it.
  3. The daemon owns the server. gaia daemon start starts embedded Lemonade and gaia daemon stop stops it. Every entry point that needs a model (the TUI, the Agent UI, agents, evals, and gaia web / gaia desktop once they exist) ensures the daemon, so a stopped server after a reboot is started without the user doing anything. LemonadeManager.ensure_ready() stops reporting “not running” and asks the daemon instead. With LEMONADE_BASE_URL set, nothing is started: the user chose that server. The default-port fallback is removed from resolve_lemonade_base_url(), so GAIA can never silently land on a different Lemonade (the app’s, on 13305) when its own isn’t up.
  4. Remove the system-Lemonade code that nothing uses any more: LemonadeInstaller’s MSI, package and pkg installers, the system launch paths in lemonade_launcher, the init-time upgrade prompts, and gaia install --lemonade. describe_start_hint() becomes one message, “run gaia init”, with the remote-server case kept.
  5. Existing users and the Lemonade app. Models live in the shared Hugging Face cache, so the embedded server re-uses everything already downloaded. The two servers never clash on ports (embedded uses its own port and key and stays off Lemonade’s discovery). They can clash on memory (a model the app keeps loaded) and on the NPU (one process at a time). So:
    • GAIA never uses the app’s server, and never uninstalls or reconfigures it.
    • gaia init and its health check detect the app and report it: its version, whether it is running, what it has loaded and how much memory that holds, and how to quit it, stop it starting at login, or uninstall it.
    • A model load that fails because the app holds the memory or the NPU says so, naming the app and the amount, instead of a generic out-of-memory error.
  6. gaia uninstall already removes the embedded server; its tiers are checked against the new layout.
  7. Docs, all together: install and quickstart guides, docs/reference/cli.mdx (gaia init, gaia lemonade, the removed gaia install --lemonade), troubleshooting, the installer scripts’ messages (no more administrator prompt), CLAUDE.md’s Lemonade start rules, and the flagship package’s README.md / SPEC.md / SKILL.md / CHANGELOG.md.

Technical debt fixed in PR 1

  • Leftover mentions of the removed lemonade-server serve command across hints, docs and the flagship sidecar’s readiness messages.
  • Duplicate version-parsing and install-state logic spread across init_command.py, lemonade_installer.py and lemonade_launcher.py, which the embedded path replaces.
  • except Exception: pass-style handlers in the touched Lemonade code, replaced with specific errors per CLAUDE.md’s no-silent-fallback rule.
  • Tests that mock the system installer; replaced with tests of the embedded path, plus one real test that installs and starts embedded Lemonade into a temporary GAIA_HOME.

Verification

  • Unit tests: init’s embedded steps, start-on-demand with and without LEMONADE_BASE_URL, --check states, the TUI remedy text, removed commands give a clear message.
  • Real: a clean GAIA_HOME on this Mac, gaia init --profile gaia --yes from nothing → embedded server installed and started → model ready → gaia llm "hi" answers. Then stop the server and confirm gaia chat and the TUI start it again.
  • CI: the self-hosted workflows keep working through LEMONADE_BASE_URL; the ones that exercise gaia init move to the embedded path.
  • Windows and Linux (Strix Halo) checked on hardware before merge.

4. PR 2: plain gaia opens the terminal UI; remove legacy commands

Running gaia with no arguments starts the terminal UI, launching the verified gaia-tui binary the installer put in place, with the same flags passed through. Today it opens the web Agent UI. If the terminal UI binary is missing, gaia says how to install it rather than opening something else. Removed, each with a one-line message naming what replaces it, plus every doc page, guide section, test and workflow step about it: Init profiles collapse to gaia, plus npu and sd where the hardware needs them. chat, rag, minimal, vlm, email and all are removed. Kept, each with a CI test if it lacks one: gaia, gaia web, gaia desktop, gaia init, gaia config, gaia uninstall, gaia diagnostics, gaia connectors, gaia hub, gaia email, gaia slack, gaia telegram, gaia schedule, gaia talk, gaia memory, and for developers gaia agent, gaia skill, gaia eval, gaia mcp (trimmed to what’s used), gaia daemon, gaia api. “Chat” leaves the docs, help text and profile names. The ChatAgent class, which the GAIA agent is built on, keeps its name until a separate rename.

5. Later PRs

6. Risks and open questions

  • NPU: the FastFlowLM backend needs the AMD NPU driver on the machine; embedded Lemonade installs the backend but not the driver. gaia init --profile npu must check the driver and say so.
  • Two Lemonades at once: a user who still runs a system Lemonade on port 13305 will have two servers. The embedded one uses its own port, so nothing breaks, but both can hold a model in memory. gaia init warns when it sees one running.
  • Desktop app and Agent UI start-up: both call ensure_ready(), so they gain start on demand; the embedded server’s first start takes a few seconds, which their loading screens must allow for.
  • Removing commands breaks scripts that call them. Each removed command prints its replacement, and the release notes list them all.