1. Decisions
2. What exists today
- Clients already prefer the embedded server.
resolve_lemonade_base_url()andresolve_lemonade_api_key()read~/.gaia/lemonade/state.jsonbefore 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 runninggaia 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.ps1and the Windows desktop installer (gaia.nsi) install GAIA only. - What still assumes a system Lemonade:
gaia initfinds or installs it (LemonadeInstaller: the Windows MSI, Linux packages, macOS pkg) and starts it throughlemonade_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.
gaiawith no command opens the web Agent UI, not the terminal UI; the terminal UI is a separategaia-tuibinary.gaia chat --ui, and the top-level--uiand--cliflags, are other routes into the same web UI, referenced in 43 files.npx @amd-gaia/gaiafetches the TUI and agent binaries and opens the TUI, but needs the Pythongaiacommand installed separately, and leaves no lastinggaiacommand.
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
gaia initon embedded Lemonade. Replace the system path inInitCommand(_ensure_lemonade_installed,_install_lemonade,_upgrade_lemonade,_check_version_compatibility,_find_lemonade_server,_auto_start_server,_ensure_server_running) withEmbeddedLemonade: install the pinned artifact (SHA-256 verified), start it, and install the profile’s backend throughEmbeddedLemonade.install_backend(llamacppeverywhere,flm:npufor the NPU profile). Model download, the hardware-based model choice (#4176) and verification are unchanged, because they already talk to whateverresolve_lemonade_base_url()returns. No administrator prompt on any platform.gaia init --checkreports embedded status: installed, running, version, and the models. An installed-but-stopped server counts as ready, since any entry point starts it.- The daemon owns the server.
gaia daemon startstarts embedded Lemonade andgaia daemon stopstops it. Every entry point that needs a model (the TUI, the Agent UI, agents, evals, andgaia web/gaia desktoponce 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. WithLEMONADE_BASE_URLset, nothing is started: the user chose that server. The default-port fallback is removed fromresolve_lemonade_base_url(), so GAIA can never silently land on a different Lemonade (the app’s, on 13305) when its own isn’t up. - Remove the system-Lemonade code that nothing uses any more:
LemonadeInstaller’s MSI, package and pkg installers, the system launch paths inlemonade_launcher, the init-time upgrade prompts, andgaia install --lemonade.describe_start_hint()becomes one message, “rungaia init”, with the remote-server case kept. - 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 initand 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.
gaia uninstallalready removes the embedded server; its tiers are checked against the new layout.- Docs, all together: install and quickstart guides,
docs/reference/cli.mdx(gaia init,gaia lemonade, the removedgaia install --lemonade), troubleshooting, the installer scripts’ messages (no more administrator prompt),CLAUDE.md’s Lemonade start rules, and the flagship package’sREADME.md/SPEC.md/SKILL.md/CHANGELOG.md.
Technical debt fixed in PR 1
- Leftover mentions of the removed
lemonade-server servecommand across hints, docs and the flagship sidecar’s readiness messages. - Duplicate version-parsing and install-state logic spread across
init_command.py,lemonade_installer.pyandlemonade_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,--checkstates, the TUI remedy text, removed commands give a clear message. - Real: a clean
GAIA_HOMEon this Mac,gaia init --profile gaia --yesfrom nothing → embedded server installed and started → model ready →gaia llm "hi"answers. Then stop the server and confirmgaia chatand the TUI start it again. - CI: the self-hosted workflows keep working through
LEMONADE_BASE_URL; the ones that exercisegaia initmove 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 npumust 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 initwarns 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.