2026-02-20 23:23:32 -08:00
|
|
|
"""Shared constants for Hermes Agent.
|
|
|
|
|
|
|
|
|
|
Import-safe module with no dependencies — can be imported from anywhere
|
|
|
|
|
without risk of circular imports.
|
|
|
|
|
"""
|
|
|
|
|
|
refactor: consolidate get_hermes_home() and parse_reasoning_effort() (#3062)
Centralizes two widely-duplicated patterns into hermes_constants.py:
1. get_hermes_home() — Path resolution for ~/.hermes (HERMES_HOME env var)
- Was copy-pasted inline across 30+ files as:
Path(os.getenv("HERMES_HOME", Path.home() / ".hermes"))
- Now defined once in hermes_constants.py (zero-dependency module)
- hermes_cli/config.py re-exports it for backward compatibility
- Removed local wrapper functions in honcho_integration/client.py,
tools/website_policy.py, tools/tirith_security.py, hermes_cli/uninstall.py
2. parse_reasoning_effort() — Reasoning effort string validation
- Was copy-pasted in cli.py, gateway/run.py, cron/scheduler.py
- Same validation logic: check against (xhigh, high, medium, low, minimal, none)
- Now defined once in hermes_constants.py, called from all 3 locations
- Warning log for unknown values kept at call sites (context-specific)
31 files changed, net +31 lines (125 insertions, 94 deletions)
Full test suite: 6179 passed, 0 failed
2026-03-25 15:54:28 -07:00
|
|
|
import os
|
|
|
|
|
from pathlib import Path
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_hermes_home() -> Path:
|
|
|
|
|
"""Return the Hermes home directory (default: ~/.hermes).
|
|
|
|
|
|
|
|
|
|
Reads HERMES_HOME env var, falls back to ~/.hermes.
|
|
|
|
|
This is the single source of truth — all other copies should import this.
|
|
|
|
|
"""
|
|
|
|
|
return Path(os.getenv("HERMES_HOME", Path.home() / ".hermes"))
|
|
|
|
|
|
|
|
|
|
|
2026-03-28 15:22:19 -07:00
|
|
|
def get_hermes_dir(new_subpath: str, old_name: str) -> Path:
|
|
|
|
|
"""Resolve a Hermes subdirectory with backward compatibility.
|
|
|
|
|
|
|
|
|
|
New installs get the consolidated layout (e.g. ``cache/images``).
|
|
|
|
|
Existing installs that already have the old path (e.g. ``image_cache``)
|
|
|
|
|
keep using it — no migration required.
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
new_subpath: Preferred path relative to HERMES_HOME (e.g. ``"cache/images"``).
|
|
|
|
|
old_name: Legacy path relative to HERMES_HOME (e.g. ``"image_cache"``).
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
Absolute ``Path`` — old location if it exists on disk, otherwise the new one.
|
|
|
|
|
"""
|
|
|
|
|
home = get_hermes_home()
|
|
|
|
|
old_path = home / old_name
|
|
|
|
|
if old_path.exists():
|
|
|
|
|
return old_path
|
|
|
|
|
return home / new_subpath
|
|
|
|
|
|
|
|
|
|
|
refactor: consolidate get_hermes_home() and parse_reasoning_effort() (#3062)
Centralizes two widely-duplicated patterns into hermes_constants.py:
1. get_hermes_home() — Path resolution for ~/.hermes (HERMES_HOME env var)
- Was copy-pasted inline across 30+ files as:
Path(os.getenv("HERMES_HOME", Path.home() / ".hermes"))
- Now defined once in hermes_constants.py (zero-dependency module)
- hermes_cli/config.py re-exports it for backward compatibility
- Removed local wrapper functions in honcho_integration/client.py,
tools/website_policy.py, tools/tirith_security.py, hermes_cli/uninstall.py
2. parse_reasoning_effort() — Reasoning effort string validation
- Was copy-pasted in cli.py, gateway/run.py, cron/scheduler.py
- Same validation logic: check against (xhigh, high, medium, low, minimal, none)
- Now defined once in hermes_constants.py, called from all 3 locations
- Warning log for unknown values kept at call sites (context-specific)
31 files changed, net +31 lines (125 insertions, 94 deletions)
Full test suite: 6179 passed, 0 failed
2026-03-25 15:54:28 -07:00
|
|
|
VALID_REASONING_EFFORTS = ("xhigh", "high", "medium", "low", "minimal")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def parse_reasoning_effort(effort: str) -> dict | None:
|
|
|
|
|
"""Parse a reasoning effort level into a config dict.
|
|
|
|
|
|
|
|
|
|
Valid levels: "xhigh", "high", "medium", "low", "minimal", "none".
|
|
|
|
|
Returns None when the input is empty or unrecognized (caller uses default).
|
|
|
|
|
Returns {"enabled": False} for "none".
|
|
|
|
|
Returns {"enabled": True, "effort": <level>} for valid effort levels.
|
|
|
|
|
"""
|
|
|
|
|
if not effort or not effort.strip():
|
|
|
|
|
return None
|
|
|
|
|
effort = effort.strip().lower()
|
|
|
|
|
if effort == "none":
|
|
|
|
|
return {"enabled": False}
|
|
|
|
|
if effort in VALID_REASONING_EFFORTS:
|
|
|
|
|
return {"enabled": True, "effort": effort}
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
|
2026-02-20 23:23:32 -08:00
|
|
|
OPENROUTER_BASE_URL = "https://openrouter.ai/api/v1"
|
|
|
|
|
OPENROUTER_MODELS_URL = f"{OPENROUTER_BASE_URL}/models"
|
|
|
|
|
OPENROUTER_CHAT_URL = f"{OPENROUTER_BASE_URL}/chat/completions"
|
2026-03-08 18:40:50 +10:00
|
|
|
|
2026-03-17 00:12:16 -07:00
|
|
|
AI_GATEWAY_BASE_URL = "https://ai-gateway.vercel.sh/v1"
|
|
|
|
|
AI_GATEWAY_MODELS_URL = f"{AI_GATEWAY_BASE_URL}/models"
|
|
|
|
|
AI_GATEWAY_CHAT_URL = f"{AI_GATEWAY_BASE_URL}/chat/completions"
|
|
|
|
|
|
2026-03-08 18:40:50 +10:00
|
|
|
NOUS_API_BASE_URL = "https://inference-api.nousresearch.com/v1"
|
|
|
|
|
NOUS_API_CHAT_URL = f"{NOUS_API_BASE_URL}/chat/completions"
|