[nu-demo] uv (Python script runner) not in hero_skills installer — python_exec tool fails silently in hero_agent #170

Closed
opened 2026-04-24 15:16:39 +00:00 by mik-tf · 3 comments
Owner

Symptom

On a fresh herodemo VM installed via hero_skills service_install_all, the AI Assistant tool python_exec doesn't actually execute. It hallucinates execution — the LLM narrates what it "would do" but no subprocess runs. When the user asks the AI to run a Python script, the answer includes phrases like:

"While uv isn't available in this environment, the script uses the standard uv format..."

uv --version returns command not found.

Root cause

hero_skills/tools/modules/installers/installers.nu apt-dep list + tool-install logic doesn't install uv (the Astral Python script runner). The hero_agent_tools crate's python_exec tool wraps uv run --script <file>, so without uv present, the tool errors on invocation — but the LLM often hallucinates successful execution rather than surfacing the error as a tool_result to the user.

Fix

Add to hero_skills installer:

# After apt install step, install uv from upstream tarball (it's not in apt).
let uv_bin = $"($env.HOME)/bin/uv"
if not ($uv_bin | path exists) {
    let url = "https://github.com/astral-sh/uv/releases/latest/download/uv-x86_64-unknown-linux-gnu.tar.gz"
    let tmp = "/data/tmp"
    mkdir $tmp
    http get $url | save -f $"($tmp)/uv.tgz"
    ^tar -xzf $"($tmp)/uv.tgz" -C $tmp
    mv $"($tmp)/uv-x86_64-unknown-linux-gnu/uv" $uv_bin
    mv $"($tmp)/uv-x86_64-unknown-linux-gnu/uvx" $"($env.HOME)/bin/uvx"
    chmod +x $uv_bin $"($env.HOME)/bin/uvx"
    print $"  uv installed: ($uv_bin)"
}

This matches the pattern for other single-binary tools (typst, nu).

Demo workaround (applied on herodemo 2026-04-24)

Manual download:

curl -sL https://github.com/astral-sh/uv/releases/latest/download/uv-x86_64-unknown-linux-gnu.tar.gz | tar -xz
cp uv-*/uv uv-*/uvx /data/home/driver/bin/
chmod +x /data/home/driver/bin/uv /data/home/driver/bin/uvx

Verified: uv 0.11.7 runs Python scripts correctly.

Adjacent improvement: python_exec should NOT hallucinate execution

Currently when the python_exec tool fails (uv missing, sandbox-blocked, etc.), some LLMs make up a plausible execution output rather than faithfully reporting the tool error. The tool wrapper should:

  • Return a structured error with {tool_error: true, stderr: "..."} that the LLM cannot mistake for success.
  • Ideally gate tool availability via semantic router heuristics — don't offer python_exec if which uv returns nothing at agent startup.
  • home#128 — hero_skills installer missing apt deps (same category: missing install-time steps)
  • home#152 — triage classifier routes to Knowledge path, masking tool-related issues
  • home#153 — tools payload sanitization (related tool-integrity concerns)

Signed-off-by: mik-tf

## Symptom On a fresh herodemo VM installed via `hero_skills service_install_all`, the AI Assistant tool `python_exec` doesn't actually execute. It hallucinates execution — the LLM narrates what it "would do" but no subprocess runs. When the user asks the AI to run a Python script, the answer includes phrases like: > "While `uv` isn't available in this environment, the script uses the standard uv format..." `uv --version` returns `command not found`. ## Root cause `hero_skills/tools/modules/installers/installers.nu` apt-dep list + tool-install logic doesn't install `uv` (the Astral Python script runner). The hero_agent_tools crate's `python_exec` tool wraps `uv run --script <file>`, so without `uv` present, the tool errors on invocation — but the LLM often hallucinates successful execution rather than surfacing the error as a tool_result to the user. ## Fix Add to hero_skills installer: ```nu # After apt install step, install uv from upstream tarball (it's not in apt). let uv_bin = $"($env.HOME)/bin/uv" if not ($uv_bin | path exists) { let url = "https://github.com/astral-sh/uv/releases/latest/download/uv-x86_64-unknown-linux-gnu.tar.gz" let tmp = "/data/tmp" mkdir $tmp http get $url | save -f $"($tmp)/uv.tgz" ^tar -xzf $"($tmp)/uv.tgz" -C $tmp mv $"($tmp)/uv-x86_64-unknown-linux-gnu/uv" $uv_bin mv $"($tmp)/uv-x86_64-unknown-linux-gnu/uvx" $"($env.HOME)/bin/uvx" chmod +x $uv_bin $"($env.HOME)/bin/uvx" print $" uv installed: ($uv_bin)" } ``` This matches the pattern for other single-binary tools (typst, nu). ## Demo workaround (applied on herodemo 2026-04-24) Manual download: ```bash curl -sL https://github.com/astral-sh/uv/releases/latest/download/uv-x86_64-unknown-linux-gnu.tar.gz | tar -xz cp uv-*/uv uv-*/uvx /data/home/driver/bin/ chmod +x /data/home/driver/bin/uv /data/home/driver/bin/uvx ``` Verified: `uv 0.11.7` runs Python scripts correctly. ## Adjacent improvement: python_exec should NOT hallucinate execution Currently when the `python_exec` tool fails (uv missing, sandbox-blocked, etc.), some LLMs make up a plausible execution output rather than faithfully reporting the tool error. The tool wrapper should: - Return a structured error with `{tool_error: true, stderr: "..."}` that the LLM cannot mistake for success. - Ideally gate tool availability via semantic router heuristics — don't offer `python_exec` if `which uv` returns nothing at agent startup. ## Related - [home#128](https://forge.ourworld.tf/lhumina_code/home/issues/128) — hero_skills installer missing apt deps (same category: missing install-time steps) - [home#152](https://forge.ourworld.tf/lhumina_code/home/issues/152) — triage classifier routes to Knowledge path, masking tool-related issues - [home#153](https://forge.ourworld.tf/lhumina_code/home/issues/153) — tools payload sanitization (related tool-integrity concerns) Signed-off-by: mik-tf
Author
Owner

Partially closed by hero_skills #125 (commit 1379cc0)

The apt-side gap is now codified in install_base: libssl-dev, pkg-config, wget, jq, sqlite3, libsqlite3-dev, python3, python3-pip, python3-venv, python3-openpyxl.

Note: this issue mentions multiple specific gaps. Some are now resolved upstream; others (e.g. uv install, ONNX runtime, Chrome) are tracked in the same Phase 2 effort and will land in a follow-up PR — see home#185 for the master tracker.

Signed-off-by: mik-tf

## Partially closed by hero_skills #125 (commit `1379cc0`) The apt-side gap is now codified in `install_base`: libssl-dev, pkg-config, wget, jq, sqlite3, libsqlite3-dev, python3, python3-pip, python3-venv, python3-openpyxl. Note: this issue mentions multiple specific gaps. Some are now resolved upstream; others (e.g. `uv` install, ONNX runtime, Chrome) are tracked in the same Phase 2 effort and will land in a follow-up PR — see [home#185](https://forge.ourworld.tf/lhumina_code/home/issues/185) for the master tracker. Signed-off-by: mik-tf
Author
Owner

Deploy-side resolved: ORT_PREFER_DYNAMIC_LINK=1 is now wired into both hero_voice_server and hero_voice_ui actions in service_voice.nu. Upstream ort/ort-sys static-link issue still tracked separately.

Part of: lhumina_code/hero_skills@7c823d1 (PR lhumina_code/hero_skills#126).
Tracker: #185.

Deploy-side resolved: `ORT_PREFER_DYNAMIC_LINK=1` is now wired into both `hero_voice_server` and `hero_voice_ui` actions in service_voice.nu. Upstream ort/ort-sys static-link issue still tracked separately. Part of: https://forge.ourworld.tf/lhumina_code/hero_skills/commit/7c823d1 (PR https://forge.ourworld.tf/lhumina_code/hero_skills/pulls/126). Tracker: https://forge.ourworld.tf/lhumina_code/home/issues/185.
Author
Owner

Fixed in hero_skills tools/modules/installers/installers.nu.

install_py (L221+) installs uv on first run via the canonical Astral installer script:

if (which uv | is-empty) {
    print "Installing uv..."
    ^bash -c "curl -LsSf https://astral.sh/uv/install.sh | bash"
}
^uv python install

(L223-233.) The mechanism differs from the tarball-extract suggested in the issue body, but the outcome is the same — uv ends up on $PATH, and ^uv python install plus ^uv tool install ipython follow at L233-245.

install_py is wired into install_core at L386, so a fresh service_install_all-style bring-up reaches the uv install before any hero_agent / python_exec invocation.

Verified end-to-end during the herodemo deploy work — python_exec runs real subprocesses now, no more LLM hallucination of execution.

Signed-off-by: mik-tf

Fixed in hero_skills `tools/modules/installers/installers.nu`. `install_py` (L221+) installs uv on first run via the canonical Astral installer script: ``` if (which uv | is-empty) { print "Installing uv..." ^bash -c "curl -LsSf https://astral.sh/uv/install.sh | bash" } ^uv python install ``` (L223-233.) The mechanism differs from the tarball-extract suggested in the issue body, but the outcome is the same — `uv` ends up on `$PATH`, and `^uv python install` plus `^uv tool install ipython` follow at L233-245. `install_py` is wired into `install_core` at L386, so a fresh `service_install_all`-style bring-up reaches the uv install before any hero_agent / python_exec invocation. Verified end-to-end during the herodemo deploy work — `python_exec` runs real subprocesses now, no more LLM hallucination of execution. Signed-off-by: mik-tf
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
lhumina_code/home#170
No description provided.