From c5c0bb9a732c11b786e1595af98d5faa06048899 Mon Sep 17 00:00:00 2001 From: Teknium <127238744+teknium1@users.noreply.github.com> Date: Fri, 17 Apr 2026 21:16:33 -0700 Subject: [PATCH] fix: point optional-dep install hints at the venv's python (#11938) Error messages that tell users to install optional extras now use {sys.executable} -m pip install ... instead of a bare 'pip install hermes-agent[extra]' string. Under the curl installer, bare 'pip' resolves to system pip, which either fails with PEP 668 externally-managed-environment or installs into the wrong Python. Affects: hermes dashboard, hermes web server startup, mcp_serve, hermes doctor Bedrock check, CLI voice mode, voice_mode tool runtime error, Discord voice-channel join failure message. --- cli.py | 6 ++---- gateway/run.py | 3 +-- hermes_cli/doctor.py | 4 ++-- hermes_cli/main.py | 2 +- hermes_cli/web_server.py | 2 +- mcp_serve.py | 4 ++-- tools/voice_mode.py | 4 ++-- 7 files changed, 11 insertions(+), 14 deletions(-) diff --git a/cli.py b/cli.py index 2456c7754..ea76991ac 100644 --- a/cli.py +++ b/cli.py @@ -7017,8 +7017,7 @@ class HermesCLI: ) raise RuntimeError( "Voice mode requires sounddevice and numpy.\n" - "Install with: pip install sounddevice numpy\n" - "Or: pip install hermes-agent[voice]" + f"Install with: {sys.executable} -m pip install sounddevice numpy" ) if not reqs.get("stt_available", reqs.get("stt_key_set")): raise RuntimeError( @@ -7294,8 +7293,7 @@ class HermesCLI: _cprint(f" {_DIM}Then install/update the Termux:API Android app for microphone capture{_RST}") _cprint(f" {_BOLD}Option 2: pkg install python-numpy portaudio && python -m pip install sounddevice{_RST}") else: - _cprint(f"\n {_BOLD}Install: pip install {' '.join(reqs['missing_packages'])}{_RST}") - _cprint(f" {_DIM}Or: pip install hermes-agent[voice]{_RST}") + _cprint(f"\n {_BOLD}Install: {sys.executable} -m pip install {' '.join(reqs['missing_packages'])}{_RST}") return with self._voice_lock: diff --git a/gateway/run.py b/gateway/run.py index b3270d958..e09dbde26 100644 --- a/gateway/run.py +++ b/gateway/run.py @@ -5520,8 +5520,7 @@ class GatewayRunner: if "pynacl" in err_lower or "nacl" in err_lower or "davey" in err_lower: return ( "Voice dependencies are missing (PyNaCl / davey). " - "Install or reinstall Hermes with the messaging extra, e.g. " - "`pip install hermes-agent[messaging]`." + f"Install with: `{sys.executable} -m pip install PyNaCl`" ) return f"Failed to join voice channel: {e}" diff --git a/hermes_cli/doctor.py b/hermes_cli/doctor.py index 28c4af1fa..4138aeaa2 100644 --- a/hermes_cli/doctor.py +++ b/hermes_cli/doctor.py @@ -895,8 +895,8 @@ def run_doctor(args): _model_count = len(_br_resp.get("modelSummaries", [])) print(f"\r {color('✓', Colors.GREEN)} {_label} {color(f'({_auth_var}, {_region}, {_model_count} models)', Colors.DIM)} ") except ImportError: - print(f"\r {color('⚠', Colors.YELLOW)} {_label} {color('(boto3 not installed — pip install hermes-agent[bedrock])', Colors.DIM)} ") - issues.append("Install boto3 for Bedrock: pip install hermes-agent[bedrock]") + print(f"\r {color('⚠', Colors.YELLOW)} {_label} {color(f'(boto3 not installed — {sys.executable} -m pip install boto3)', Colors.DIM)} ") + issues.append(f"Install boto3 for Bedrock: {sys.executable} -m pip install boto3") except Exception as _e: _err_name = type(_e).__name__ print(f"\r {color('⚠', Colors.YELLOW)} {_label} {color(f'({_err_name}: {_e})', Colors.DIM)} ") diff --git a/hermes_cli/main.py b/hermes_cli/main.py index e2e2a774f..81b27e4a1 100644 --- a/hermes_cli/main.py +++ b/hermes_cli/main.py @@ -6029,7 +6029,7 @@ def cmd_dashboard(args): import uvicorn # noqa: F401 except ImportError: print("Web UI dependencies not installed.") - print("Install them with: pip install hermes-agent[web]") + print(f"Install them with: {sys.executable} -m pip install 'fastapi' 'uvicorn[standard]'") sys.exit(1) if not _build_web_ui(PROJECT_ROOT / "web", fatal=True): diff --git a/hermes_cli/web_server.py b/hermes_cli/web_server.py index e5f2eb537..0d0dc4a66 100644 --- a/hermes_cli/web_server.py +++ b/hermes_cli/web_server.py @@ -56,7 +56,7 @@ try: except ImportError: raise SystemExit( "Web UI requires fastapi and uvicorn.\n" - "Run 'hermes web' to auto-install, or: pip install hermes-agent[web]" + f"Install with: {sys.executable} -m pip install 'fastapi' 'uvicorn[standard]'" ) WEB_DIST = Path(__file__).parent / "web_dist" diff --git a/mcp_serve.py b/mcp_serve.py index e8294d1f9..e0aeb7061 100644 --- a/mcp_serve.py +++ b/mcp_serve.py @@ -433,7 +433,7 @@ def create_mcp_server(event_bridge: Optional[EventBridge] = None) -> "FastMCP": if not _MCP_SERVER_AVAILABLE: raise ImportError( "MCP server requires the 'mcp' package. " - "Install with: pip install 'hermes-agent[mcp]'" + f"Install with: {sys.executable} -m pip install 'mcp'" ) mcp = FastMCP( @@ -838,7 +838,7 @@ def run_mcp_server(verbose: bool = False) -> None: if not _MCP_SERVER_AVAILABLE: print( "Error: MCP server requires the 'mcp' package.\n" - "Install with: pip install 'hermes-agent[mcp]'", + f"Install with: {sys.executable} -m pip install 'mcp'", file=sys.stderr, ) sys.exit(1) diff --git a/tools/voice_mode.py b/tools/voice_mode.py index 50515fc69..66ecb242c 100644 --- a/tools/voice_mode.py +++ b/tools/voice_mode.py @@ -15,6 +15,7 @@ import platform import re import shutil import subprocess +import sys import tempfile import threading import time @@ -582,8 +583,7 @@ class AudioRecorder: except (ImportError, OSError) as e: raise RuntimeError( "Voice mode requires sounddevice and numpy.\n" - "Install with: pip install sounddevice numpy\n" - "Or: pip install hermes-agent[voice]" + f"Install with: {sys.executable} -m pip install sounddevice numpy" ) from e with self._lock: