Add helpful ImportError messages for optional dependencies

When optional dependencies are missing, raise ImportError with
installation
instructions pointing to the relevant extras group (e.g. `[messaging]`,
`[cli]`, `[mcp]`, etc.) instead of letting the import fail silently.
This commit is contained in:
alt-glitch 2026-04-23 04:46:01 +05:30
parent 847ffca715
commit 850973295e
9 changed files with 81 additions and 14 deletions

View file

@ -185,7 +185,13 @@ def proxy_kwargs_for_bot(proxy_url: str | None) -> dict:
if not proxy_url:
return {}
if proxy_url.lower().startswith("socks"):
from aiohttp_socks import ProxyConnector
try:
from aiohttp_socks import ProxyConnector
except ImportError:
raise ImportError(
"aiohttp-socks is required for SOCKS proxy support. "
"Install with: pip install hermes-agent[messaging]"
) from None
connector = ProxyConnector.from_url(proxy_url, rdns=True)
return {"connector": connector}
@ -210,7 +216,13 @@ def proxy_kwargs_for_aiohttp(proxy_url: str | None) -> tuple[dict, dict]:
if not proxy_url:
return {}, {}
if proxy_url.lower().startswith("socks"):
from aiohttp_socks import ProxyConnector
try:
from aiohttp_socks import ProxyConnector
except ImportError:
raise ImportError(
"aiohttp-socks is required for SOCKS proxy support. "
"Install with: pip install hermes-agent[messaging]"
) from None
connector = ProxyConnector.from_url(proxy_url, rdns=True)
return {"connector": connector}, {}

View file

@ -1194,7 +1194,13 @@ class DiscordAdapter(BasePlatformAdapter):
try:
import base64
from mutagen.oggopus import OggOpus
try:
from mutagen.oggopus import OggOpus
except ImportError:
raise ImportError(
"mutagen is required for Discord voice messages. "
"Install with: pip install hermes-agent[messaging]"
) from None
duration_secs = 5.0
try:

View file

@ -395,7 +395,13 @@ def _wayland_save(dest: Path) -> bool:
def _convert_to_png(path: Path) -> bool:
"""Convert an image file to PNG in-place (requires Pillow or ImageMagick)."""
from PIL import Image
try:
from PIL import Image
except ImportError:
raise ImportError(
"Pillow is required for clipboard image conversion. "
"Install with: pip install hermes-agent[cli]"
) from None
try:
img = Image.open(path)
img.save(path, "PNG")

View file

@ -754,7 +754,13 @@ def _estimate_tool_tokens() -> Dict[str, int]:
if _tool_token_cache is not None:
return _tool_token_cache
import tiktoken
try:
import tiktoken
except ImportError:
raise ImportError(
"tiktoken is required for tool token estimation. "
"Install with: pip install hermes-agent[cli]"
) from None
enc = tiktoken.get_encoding("cl100k_base")
try:

View file

@ -1501,7 +1501,13 @@ def _snapshot_child_pids() -> set:
pass
# Fallback: psutil
import psutil
try:
import psutil
except ImportError:
raise ImportError(
"psutil is required for MCP child process tracking. "
"Install with: pip install hermes-agent[mcp]"
) from None
try:
return {c.pid for c in psutil.Process(my_pid).children()}
except psutil.Error:

View file

@ -71,7 +71,13 @@ def main():
ref_text = ref_text_path.read_text(encoding="utf-8").strip()
from neutts import NeuTTS
try:
from neutts import NeuTTS
except ImportError:
raise ImportError(
"neutts is required for local TTS synthesis. "
"Install with: pip install hermes-agent[tts-local]"
) from None
tts = NeuTTS(
backbone_repo=args.model,
@ -86,7 +92,13 @@ def main():
out_path = Path(args.out)
out_path.parent.mkdir(parents=True, exist_ok=True)
import soundfile as sf
try:
import soundfile as sf
except ImportError:
raise ImportError(
"soundfile is required for audio output. "
"Install with: pip install hermes-agent[tts-local]"
) from None
sf.write(str(out_path), wav, 24000)
print(f"OK: {out_path}", file=sys.stderr)

View file

@ -335,10 +335,17 @@ class ProcessRegistry:
)
if use_pty:
if _IS_WINDOWS:
from winpty import PtyProcess as _PtyProcessCls
else:
from ptyprocess import PtyProcess as _PtyProcessCls
try:
if _IS_WINDOWS:
from winpty import PtyProcess as _PtyProcessCls
else:
from ptyprocess import PtyProcess as _PtyProcessCls
except ImportError:
pkg = "winpty" if _IS_WINDOWS else "ptyprocess"
raise ImportError(
f"{pkg} is required for PTY mode. "
"Install with: pip install hermes-agent[pty]"
) from None
try:
user_shell = _find_shell()
pty_env = _sanitize_subprocess_env(os.environ, env_vars)

View file

@ -318,7 +318,13 @@ def _resize_image_for_vision(image_path: Path, mime_type: Optional[str] = None,
else:
data_url = None # defer full encode; try Pillow resize first
from PIL import Image
try:
from PIL import Image
except ImportError:
raise ImportError(
"Pillow is required for image resizing. "
"Install with: pip install hermes-agent[cli]"
) from None
import io as _io
logger.info("Image file is %.1f MB (estimated base64 %.1f MB, limit %.1f MB), auto-resizing...",

View file

@ -192,7 +192,13 @@ def _estimate_image_tokens(width: int, height: int) -> int:
def _image_meta(path: Path) -> dict:
from PIL import Image
try:
from PIL import Image
except ImportError:
raise ImportError(
"Pillow is required for image metadata extraction. "
"Install with: pip install hermes-agent[cli]"
) from None
meta = {"name": path.name}
try: