- Hermes agent configuration, skills and MCP client wrappers - Hardware Lab Node SSE server with ESP32, PPK2, Xgecu adapters - Olhos-de-Orpheu launcher and sci-bot wrapper scripts - Demo scenario, integration test and orchestrator task definitions - Add local .gitignore for venvs, build outputs and runtime logs
145 lines
4.5 KiB
Python
145 lines
4.5 KiB
Python
#!/usr/bin/env python3
|
||
"""Configure Hermes Agent to use Olhos-de-Orpheu as its single MCP gateway."""
|
||
|
||
import os
|
||
import subprocess
|
||
import sys
|
||
import urllib.request
|
||
from pathlib import Path
|
||
|
||
HERMES_HOME = Path.home() / ".hermes"
|
||
CONFIG_PATH = HERMES_HOME / "config.yaml"
|
||
ENV_PATH = HERMES_HOME / ".env"
|
||
|
||
REPO_ROOT = Path(__file__).resolve().parent.parent
|
||
VENV_PYTHON = REPO_ROOT / ".venv" / "bin" / "python"
|
||
HERMES_BIN = REPO_ROOT / ".venv" / "bin" / "hermes"
|
||
OLHOS_DE_ORPHEU_STDIO = REPO_ROOT / "scripts" / "olhos_de_orpheu_mcp.py"
|
||
|
||
OLLAMA_HOST = os.getenv("OLLAMA_HOST", "192.168.0.104")
|
||
OLLAMA_PORT = os.getenv("OLLAMA_PORT", "11434")
|
||
OLLAMA_MODEL = os.getenv("OLLAMA_MODEL", "llama3.2")
|
||
|
||
OLHOS_SSE_URL = os.getenv("MCP_OLHOS_DE_ORPHEU_URL", "http://192.168.0.16:8001/sse")
|
||
|
||
|
||
def ensure_hermes_home():
|
||
HERMES_HOME.mkdir(parents=True, exist_ok=True)
|
||
|
||
|
||
def _url_reachable(url: str, timeout: float = 3.0) -> bool:
|
||
try:
|
||
with urllib.request.urlopen(url, timeout=timeout) as resp:
|
||
return resp.status < 500
|
||
except Exception:
|
||
return False
|
||
|
||
|
||
def write_env(nvidia_key: str | None, stripe_key: str | None):
|
||
lines = []
|
||
if nvidia_key:
|
||
lines.append(f'NVIDIA_API_KEY="{nvidia_key}"')
|
||
else:
|
||
lines.append('# NVIDIA_API_KEY="nvapi-..." # get from https://build.nvidia.com')
|
||
if stripe_key:
|
||
lines.append(f'STRIPE_SECRET_KEY="{stripe_key}"')
|
||
else:
|
||
lines.append('# STRIPE_SECRET_KEY="sk_test_..." # get from https://dashboard.stripe.com/test/apikeys')
|
||
lines.append(f'OLLAMA_HOST="{OLLAMA_HOST}"')
|
||
lines.append(f'OLLAMA_PORT="{OLLAMA_PORT}"')
|
||
lines.append(f'OLLAMA_MODEL="{OLLAMA_MODEL}"')
|
||
lines.append('DEEPSEEK_API_KEY="ollama"')
|
||
lines.append(f'MCP_OLHOS_DE_ORPHEU_URL="{OLHOS_SSE_URL}"')
|
||
ENV_PATH.write_text("\n".join(lines) + "\n", encoding="utf-8")
|
||
print(f"Wrote {ENV_PATH}")
|
||
|
||
|
||
def write_config(nvidia_key: str | None, use_olhos_sse: bool):
|
||
if nvidia_key:
|
||
model_block = """model:
|
||
default: nvidia/nemotron-3-ultra-550b-a55b
|
||
provider: custom
|
||
base_url: https://integrate.api.nvidia.com/v1"""
|
||
else:
|
||
model_block = f"""model:
|
||
default: {OLLAMA_MODEL}
|
||
provider: custom
|
||
base_url: http://{OLLAMA_HOST}:{OLLAMA_PORT}/v1"""
|
||
|
||
if use_olhos_sse:
|
||
olhos_block = f""" olhos-de-orpheu:
|
||
url: "{OLHOS_SSE_URL}"
|
||
transport: "sse"
|
||
timeout: 120
|
||
connect_timeout: 10"""
|
||
else:
|
||
olhos_block = f""" olhos-de-orpheu:
|
||
command: "{VENV_PYTHON}"
|
||
args:
|
||
- "{OLHOS_DE_ORPHEU_STDIO}"
|
||
transport: "stdio"
|
||
timeout: 120
|
||
connect_timeout: 30"""
|
||
|
||
config = f"""# Hermes configuration for Portugal Futurista Aurelio × Hermes hackathon
|
||
# Single MCP gateway: Olhos-de-Orpheu (Proxmox CT 206)
|
||
{model_block}
|
||
providers: {{}}
|
||
fallback_providers: []
|
||
toolsets:
|
||
- hermes-cli
|
||
max_iterations: 90
|
||
quiet_mode: true
|
||
|
||
mcp_servers:
|
||
{olhos_block}
|
||
"""
|
||
CONFIG_PATH.write_text(config, encoding="utf-8")
|
||
print(f"Wrote {CONFIG_PATH}")
|
||
|
||
|
||
def verify_hermes():
|
||
result = subprocess.run(
|
||
[str(HERMES_BIN), "doctor"],
|
||
capture_output=True,
|
||
text=True,
|
||
)
|
||
print(result.stdout)
|
||
if result.returncode != 0:
|
||
print(result.stderr, file=sys.stderr)
|
||
sys.exit(1)
|
||
|
||
|
||
def main():
|
||
nvidia_key = os.getenv("NVIDIA_NIM_API_KEY") or os.getenv("NVIDIA_API_KEY")
|
||
stripe_key = os.getenv("STRIPE_SECRET_KEY")
|
||
|
||
if not nvidia_key:
|
||
print("WARNING: NVIDIA_NIM_API_KEY not set; Hermes will fall back to local Ollama.")
|
||
print("Get a free key at https://build.nvidia.com and re-run with NVIDIA_NIM_API_KEY set.")
|
||
|
||
print(f"Checking Olhos-de-Orpheu gateway at {OLHOS_SSE_URL}...")
|
||
use_olhos_sse = _url_reachable(OLHOS_SSE_URL)
|
||
if use_olhos_sse:
|
||
print("Gateway reachable — using SSE transport.")
|
||
else:
|
||
print("Gateway unreachable — falling back to local stdio wrapper.")
|
||
if not OLHOS_DE_ORPHEU_STDIO.exists():
|
||
print(f"ERROR: stdio wrapper not found at {OLHOS_DE_ORPHEU_STDIO}", file=sys.stderr)
|
||
sys.exit(1)
|
||
|
||
ensure_hermes_home()
|
||
write_env(nvidia_key, stripe_key)
|
||
write_config(nvidia_key, use_olhos_sse)
|
||
|
||
# Let Hermes migrate/expand the config with its current defaults
|
||
subprocess.run([str(HERMES_BIN), "doctor", "--fix"], check=False)
|
||
|
||
print("\nHermes configuration complete.")
|
||
print("Run: hermes doctor")
|
||
print("Then: hermes mcp list")
|
||
print("Then: hermes chat -q 'Aurelio, help the Alentejo cooperative recover the watermill and deploy a soil sensor.'")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|