Skip to content

fix(installer): install the extras the daemon needs to start - #3056

Merged
kovtcharov-amd merged 2 commits into
amd:mainfrom
kovtcharov:fix/installer-daemon-extras
Aug 26, 2026
Merged

fix(installer): install the extras the daemon needs to start#3056
kovtcharov-amd merged 2 commits into
amd:mainfrom
kovtcharov:fix/installer-daemon-extras

Conversation

@kovtcharov

@kovtcharov kovtcharov commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Following the website exactly — run the one-line installer, gaia init, then gaia-tui — leaves you unable to start the terminal hub. The installer sets up the gaia CLI the hub depends on, but installs bare amd-gaia, which has no fastapi or uvicorn, and the hub is useless without the background service:

gaia daemon needs packages not in the base install: fastapi, uvicorn.
   Install the daemon extras:  pip install "amd-gaia[ui]"  (or [api]/[dev]).

Nothing downstream covers it — no gaia init profile asks for api or ui (pip_extras is [] or ["rag"] across all nine), so the packages never arrive by any route the onboarding walks.

[api] is fastapi, uvicorn, python-multipart and httpx — what the daemon actually needs. [ui] would also satisfy it but drags in torch and faiss for a process that needs neither, and the flagship agent ships as a frozen sidecar carrying its own dependencies, so the core doesn't need RAG extras on its behalf.

Two follow-ons landed here because installing [api] alone still wasn't sufficient. That extra never declared psutil, which the daemon's own pre-flight check requires — it was arriving only as a transitive of accelerate, one resolver change from re-breaking this. And the error message above pointed at [dev], which declares none of the three, so a user who followed it stayed broken.

This is the last blocker in the TUI onboarding path; #3054 (releasing the core that can run the flagship agent) and #3055 (its version floor) are the other two.

Test plan

  • Clean venv + bare amd-gaiagaia daemon start fails naming fastapi/uvicorn/psutil, and points at [api]
  • Clean venv + amd-gaia[api] → daemon starts, and gaia daemon status lists gaia as a supervised sidecar
  • In that venv, pip show psutil succeeds — declared by the extra, not inherited from accelerate
  • End to end from that venv: gaia install gaiagaia daemon start-agent gaia → sidecar /health returns {"status":"ok"}
  • pytest tests/unit/test_api_extras.py tests/unit/installer/ — the new guards pass (the dash failure is a pre-existing Windows CRLF checkout artifact; install.sh parses clean under both dash -n and bash -n with LF endings)

@github-actions github-actions Bot added the installer Installer changes label Aug 24, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Verdict: Approve with suggestions — one thing to confirm before merge.

This makes the installer pull the api extra so a fresh install actually has what the daemon needs to boot. Right fix, right place, and both the fresh-install and the upgrade path are covered on Linux/macOS and Windows.

The catch: the daemon needs three things at startup — the web server, the ASGI runner, and a process-inspection library — and the extra chosen here only declares the first two. GAIA's own pre-flight check refuses to start the daemon when the third is missing. It may still land on disk today as a side effect of another dependency, but the installer isn't asking for it, so nothing guarantees it. Worth confirming with a clean-venv install followed by starting the daemon; if it's missing there, this fix doesn't finish the job and the extra should declare it.

Second, nothing locks this in. A one-line assertion over the installer scripts — in the existing installer test file — would stop a future edit from quietly dropping the extras again, which is exactly how this regressed.

Real-world evidence

N/A in this run — no evidence bundle was produced, and I had no access to the PR description or GitHub from the review environment, so I can't tell whether the author already attached installer output. The verdict therefore rests on static review alone.

The evidence that would settle the open question is a single clean-machine run: a fresh venv installed the way the script does it, then starting the daemon and showing it come up (rather than printing the missing-dependency error). If that's already in the description, disregard the nudge.

🔍 Technical details

🟡 [api] does not declare psutil, which the daemon imports at startup (installer/scripts/install.sh:210,244, installer/scripts/install.ps1:102,139)

setup.py's api extra is fastapi / uvicorn / python-multipart / httpx (setup.py:176-186). psutil appears only in ui and talk. But gaia.daemon.server.run()_build_registry()gaia.daemon.sidecars.registry, which does a module-level import psutil (src/gaia/daemon/sidecars/registry.py:18), and _check_daemon_deps() hard-exits when psutil is absent (src/gaia/cli.py:7782-7798).

It probably works today because accelerate (a core install_requires entry) pulls psutil transitively — but that's undeclared and one resolver change away from breaking the exact bug this PR fixes. Cleanest fix is to declare it where the daemon's own check expects it:

        "api": [
            "fastapi>=0.115.0",
            "uvicorn>=0.32.0",
            "python-multipart>=0.0.9",
            "httpx>=0.27.0",
            # Daemon sidecar registry imports psutil at module scope
            # (gaia/daemon/sidecars/registry.py) and _check_daemon_deps
            # refuses to start without it.
            "psutil>=5.9.0",
        ],

The alternative — switching the installer to amd-gaia[ui] — drags in torch/faiss/sentence-transformers and is much heavier; [api] + psutil is the better trade.

🟡 No regression test pins the extras onto the install scripts

tests/unit/installer/test_install_scripts_terminal_hub.py already does static assertions over both scripts (it exists because "a static check alone" was what let a previous installer bug ship). A two-line addition there guards this:

def test_scripts_install_the_daemon_extras(sh_text, ps1_text):
    assert sh_text.count('"amd-gaia[api]"') == 2
    assert ps1_text.count('"amd-gaia[api]"') == 2

🟢 Nit — the daemon-deps error message names extras that don't satisfy it (src/gaia/cli.py:7795-7796)

The message reads pip install "amd-gaia[ui]" (or [api]/[dev]), but neither [api] nor [dev] declares psutil — the very module the check just found missing. Pre-existing, but it's the same mismatch this PR is fixing, so it's cheap to correct here (and it becomes accurate for free if psutil moves into [api] above).

Strengths

  • Both call sites in each script are updated — the fresh-install path and the already-installed --upgrade path. Missing the upgrade path would have left every existing user broken, which is the easy half to forget.
  • The extras spec is quoted in both shells, so [api] survives POSIX-shell globbing and PowerShell parsing.
  • [api] over [ui] keeps the install lean; the heavy RAG/torch stack stays opt-in.

@github-actions github-actions Bot added documentation Documentation changes dependencies Dependency updates cli CLI changes tests Test changes labels Aug 25, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Verdict: Approve 🟢

Tight bug-fix: the one-line installer was shipping bare amd-gaia instead of amd-gaia[api], so psutil — which the daemon loads at module scope — was only present transitively via accelerate. A resolver change could silently re-break it. Declaring it explicitly and updating both install paths is the right fix, and the test suite that guards the contract is unusually strong for packaging work.

Checked: [api] and [ui] both declare psutil>=5.9.0, fastapi, and uvicorn — so test_daemon_deps_error_names_an_extra_that_satisfies_it passes for both extras named in the error message. Confirmed [dev] was correctly dropped from the suggested fix (it has no daemon deps). The old message pointing users at [dev] was actively wrong.

One observation on the AST-based helper in test_api_extras.py:

_daemon_required_packages() grabs the first ListComp inside _check_daemon_deps and calls ast.literal_eval on the generator's iterable. This works because the current code uses a tuple literal — if the structure ever changes (e.g. extracting the pairs into a module-level constant), the test raises ValueError rather than silently passing, which is the right failure mode.

No 🔴 or 🟡 issues found. Ship it.

Ovtcharov added 2 commits August 24, 2026 17:21
The one-line installer sets up the `gaia` CLI that the terminal hub depends on,
but installed bare `amd-gaia`, which has no fastapi or uvicorn. The terminal hub
cannot do anything without the background service, so a fresh install reached
`gaia daemon` and stopped there:

    gaia daemon needs packages not in the base install: fastapi, uvicorn.

Nothing downstream filled the gap: no `gaia init` profile requests `api` or
`ui` (pip_extras is [] or ["rag"] for all nine), so following the website
exactly -- install, `gaia init`, `gaia-tui` -- never installed them.

`[api]` is fastapi, uvicorn, python-multipart and httpx, and is what the daemon
actually needs; `[ui]` would also work but drags in torch and faiss for a
process that needs neither. The flagship agent ships as a frozen sidecar with
its own dependencies, so the core does not need RAG extras on its behalf.

Verified on a clean 3.12 venv: bare `amd-gaia` fails as above, and the same
venv with [api] starts the daemon, which then installs and runs the flagship
agent from the terminal hub.
…n start

The installer asks for `amd-gaia[api]`, but that extra declared only fastapi
and uvicorn — the daemon's own pre-flight check also requires psutil, which was
arriving only as a transitive of accelerate. One resolver change away from
re-breaking the exact bug this PR fixes.

The daemon's error message pointed at `[ui]` "(or [api]/[dev])"; [dev] declares
none of the three, so a user following it stayed broken. It now names [api] —
the lean extra the installer uses — with [ui] as the heavier alternative, and
docs/reference/cli.mdx says the same in both places it describes the extras.

Three regression guards, all derived from cli.py's own check list so they can't
drift from it: [api] covers every daemon dep, every extra the error message
suggests actually satisfies it, and both installer call sites request [api].
@kovtcharov-amd
kovtcharov-amd force-pushed the fix/installer-daemon-extras branch from 943f70b to 9b892c2 Compare August 25, 2026 00:29
@github-actions

Copy link
Copy Markdown
Contributor

Approvepsutil was the missing link keeping the daemon from starting on a fresh [api] install, and this fix is correct end-to-end: setup.py[api] now declares the dep, both installer scripts request [api], the _check_daemon_deps error message names an extra that actually satisfies it, and the docs stay in sync. The AST-based test that cross-checks cli.py::_check_daemon_deps against setup.py[api] is exactly the kind of drift-guard this invariant needed.

Two small test-brittleness nits:

🟢 test_every_core_install_requests_the_daemon_extras asserts len(call_sites) == 2, which breaks if a future installer revision adds a third uv pip install … amd-gaia[api] call — even one that fully satisfies the invariant. The bare check on the next line already enforces what matters; the length assertion is redundant.

🟢 test_daemon_deps_error_names_an_extra_that_satisfies_it slices the source with src.index("def handle_daemon_command") as the upper bound. Renaming or relocating that function raises ValueError and silently kills the test.

🔍 Technical details

tests/unit/installer/test_install_scripts_terminal_hub.py:134 — drop the redundant length assertion; the invariant is fully covered by the bare check:

# before
assert len(call_sites) == 2, (...)
bare = [line for line in call_sites if '"amd-gaia[api]"' not in line]
assert not bare, (...)

# after — one check, same invariant
bare = [line for line in call_sites if '"amd-gaia[api]"' not in line]
assert not bare, (...)

tests/unit/test_api_extras.py:239 — tighten the upper bound so it survives a rename:

# before
body = src[
    src.index("def _check_daemon_deps") : src.index("def handle_daemon_command")
]

# after — find the next function definition instead of naming the neighbour
start = src.index("def _check_daemon_deps")
end = src.index("\ndef ", start + 1)
body = src[start:end]

@itomek itomek self-assigned this Aug 25, 2026
@kovtcharov-amd
kovtcharov-amd added this pull request to the merge queue Aug 26, 2026
Merged via the queue into amd:main with commit ba64ed1 Aug 26, 2026
66 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cli CLI changes dependencies Dependency updates documentation Documentation changes installer Installer changes tests Test changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants