diff --git a/app.py b/app.py index b7e1375..18705bb 100644 --- a/app.py +++ b/app.py @@ -1,22 +1,19 @@ """ Hermes Relay v2 — Flask bridge tussen Node-RED en Hermes Agent. -Gebruikt een warme Hermes agent (directe Python import) in plaats van -subprocess per query. Dit bespaart ~3-5 seconden startup tijd per query. +Gebruikt de voice-assistant profiel (minimale chatbot, max_turns=10). +Geen prefix/model override nodig — profiel handelt dit af. -Node-RED stuurt POST met msg.payload → Hermes (direct) → Antwoord + Telegram. - -Endpoints: - POST /ask — main relay endpoint - GET /health — health check +Node-RED stuurt POST met msg.payload → Hermes (direct) → Alleen antwoord terug. """ + import os import sys import time import logging # ── Hermes path toevoegen (zodat we Hermes direct kunnen importeren) ─── -_HERMES_SITE = '/usr/local/lib/hermes-agent/venv/lib/python3.11/site-packages' +_HERMES_SITE = "/usr/local/lib/hermes-agent/venv/lib/python3.11/site-packages" if _HERMES_SITE not in sys.path: sys.path.insert(0, _HERMES_SITE) @@ -24,9 +21,7 @@ from flask import Flask, request, jsonify # ── Config ────────────────────────────────────────────────────────────── TIMEOUT = 120 # max seconden voor Hermes query -TG_TARGET = "telegram" # hermes send target -PROMPT_PREFIX = "/fast" # wordt aan elke prompt toegevoegd -MODEL = "qwen3.7-max" # model override (leeg = default uit config) +TG_TARGET = "telegram" # hermes send target # ── Logging ───────────────────────────────────────────────────────────── logging.basicConfig( @@ -40,24 +35,53 @@ logger = logging.getLogger(__name__) app = Flask(__name__) # ── Hermes warm houden ────────────────────────────────────────────────── -# Import de Hermes client (triggert eenmalige module laden) -HERMES_BIN = "/usr/local/bin/hermes" # nog steeds nodig voor hermes send from hermes_client import call_hermes as _hermes_query +# Laad gateway config eenmalig voor Telegram verzenden +_gw_config_loaded = False +_gw_config = None + +def _ensure_gw_config(): + """Laad gateway config eenmalig.""" + global _gw_config_loaded, _gw_config + if _gw_config_loaded: + return _gw_config + # Forceer default profiel voor gateway config + import os + os.environ["HERMES_HOME"] = "/root/.hermes" + # Zet de echte Telegram token in environment zodat gateway config deze kan laden + os.environ["TELEGRAM_BOT_TOKEN"] = "8736787405:AAGbtAtTPT7Kf3SWdPIhGbiiDVjnAUB_a5c" + os.environ["TELEGRAM_HOME_CHANNEL"] = "5453608010" + from gateway.config import load_gateway_config + _gw_config = load_gateway_config() + _gw_config_loaded = True + return _gw_config + def send_telegram(text): - """Stuur bericht via hermes send subprocess.""" - import subprocess + """Stuur bericht via gateway's send_message_tool (gebruikt gateway config met echte token).""" try: - result = subprocess.run( - [HERMES_BIN, "send", "--to", TG_TARGET, "--quiet", text], - capture_output=True, - text=True, - timeout=30, - ) - return result.returncode == 0 + logger.info("Telegram send: starting...") + _ensure_gw_config() + logger.info("Telegram send: gw config loaded") + from tools.send_message_tool import send_message_tool + import json + + # Stuur via home channel (telegram = default target) + result_json = send_message_tool({ + "action": "send", + "target": "telegram", + "message": text, + }) + logger.info("Telegram send: send_message_tool returned") + result = json.loads(result_json) + if result.get("error"): + logger.error("Telegram send failed: %s", result["error"]) + return False + logger.info("Telegram send: success=%s", result.get("success", False)) + return result.get("success", False) except Exception as e: - logger.error("Telegram send failed: %s", e) + logger.error("Telegram send failed with exception: %s", e, exc_info=True) return False @@ -65,7 +89,7 @@ def send_telegram(text): @app.route("/ask", methods=["POST"]) def ask(): - """Main relay endpoint.""" + """Main relay endpoint — retourneert alleen het antwoord.""" start = time.time() # Parse input @@ -104,13 +128,12 @@ def health(): return jsonify({ "status": "ok", "service": "hermes-relay", - "version": "2.0", + "version": "2.1", "mode": "python-import", - "model": MODEL or "(default)", - "prompt_prefix": PROMPT_PREFIX or "(none)", + "profile": "voice-assistant", }) if __name__ == "__main__": - logger.info("Starting Hermes Relay v2.0 (warme agent)...") - app.run(host="0.0.0.0", port=8650, debug=False) + logger.info("Starting Hermes Relay v2.1 (voice-assistant profiel)...") + app.run(host="0.0.0.0", port=8650, debug=False) \ No newline at end of file diff --git a/hermes_client.py b/hermes_client.py index 6e468a4..5844484 100644 --- a/hermes_client.py +++ b/hermes_client.py @@ -3,6 +3,9 @@ Hermes Client — directe Python import (geen subprocess overhead). Warme agent: eenmalig initialiseren, daarna hergebruiken voor elke query. Dit bespaart de subprocess + Python startup overhead van 2-5 seconden per query. + +Gebruikt het 'voice-assistant' profiel (minimale chatbot: alleen memory tool, +deepseek-v4-flash/opencode-go, max_turns=10). """ import io import os @@ -17,8 +20,8 @@ logger = logging.getLogger(__name__) _HERMES_LOADED = False _cli = None -# Model override voor relay (leeg = default uit config) -MODEL_OVERRIDE = "qwen3.7-plus" +# Voice-assistant profiel home directory +_HERMES_HOME = "/root/.hermes/profiles/voice-assistant" def _ensure_hermes_loaded(): @@ -29,52 +32,50 @@ def _ensure_hermes_loaded(): start = time.time() + # Forceer voice-assistant profiel via HERMES_HOME + os.environ["HERMES_HOME"] = _HERMES_HOME + # Zorg dat Hermes in de Python path zit - hermes_path = '/usr/local/lib/hermes-agent/venv/lib/python3.11/site-packages' + hermes_path = "/usr/local/lib/hermes-agent/venv/lib/python3.11/site-packages" if hermes_path not in sys.path: sys.path.insert(0, hermes_path) # Force UTF-8 - os.environ.setdefault('PYTHONIOENCODING', 'utf-8') + os.environ.setdefault("PYTHONIOENCODING", "utf-8") # Laad .env via Hermes' eigen loader from hermes_cli.env_loader import load_hermes_dotenv from hermes_constants import get_hermes_home _hermes_home = get_hermes_home() - _project_env = os.path.join(os.path.dirname(hermes_path), '.env') + _project_env = os.path.join(os.path.dirname(hermes_path), ".env") load_hermes_dotenv(hermes_home=_hermes_home, project_env=_project_env) - # Load config + # Load config (nu vanuit voice-assistant profiel) from hermes_cli.config import load_config config = load_config() - # Resolve toolsets (geen tools voor snelle relay) - from hermes_cli.tools_config import _get_platform_tools - if MODEL_OVERRIDE: - # Geen tools laden bij model override (DeepSeek heeft incompatible tool schema) - toolsets = [] - else: - toolsets = sorted(_get_platform_tools(config, 'cli')) + # Gebruik messaging toolset zodat agent send_message tool kan gebruiken + toolsets = ["messaging"] # YOLO mode + interactive mode - os.environ['HERMES_YOLO_MODE'] = '1' - os.environ['HERMES_INTERACTIVE'] = '1' - os.environ['HERMES_SESSION_SOURCE'] = 'relay' + os.environ["HERMES_YOLO_MODE"] = "1" + os.environ["HERMES_INTERACTIVE"] = "1" + os.environ["HERMES_SESSION_SOURCE"] = "relay" # Import HermesCLI from cli import HermesCLI # Maak CLI instantie (nog géén agent init — dat gebeurt pas bij eerste query) _cli = HermesCLI( - model=config.get('model', {}).get('default', ''), + model=config.get("model", {}).get("default", ""), toolsets=toolsets, ) # Onderdruk banner/status output - _cli.tool_progress_mode = 'off' + _cli.tool_progress_mode = "off" elapsed = time.time() - start logger.info( - "Hermes loaded in %.2fs (toolsets: %s)", + "Hermes loaded in %.2fs (toolsets: %s, profile: voice-assistant)", elapsed, toolsets ) _HERMES_LOADED = True @@ -90,24 +91,20 @@ def call_hermes(prompt): """ _ensure_hermes_loaded() - # Prefix (zoals /fast) - prefix = os.environ.get('HERMES_RELAY_PREFIX', '/fast') - full_prompt = (prefix + " " + prompt).strip() if prefix else prompt + # Geen prefix meer — voice-assistant profiel handelt dit af via config + full_prompt = prompt.strip() start = time.time() - # ── Agent initialiseren met model override ── + # ── Agent initialiseren ── turn_route = _cli._resolve_turn_agent_config(full_prompt) - - # Override model als MODEL_OVERRIDE is ingesteld - model_override = MODEL_OVERRIDE if MODEL_OVERRIDE else turn_route['model'] - - if turn_route['signature'] != _cli._active_agent_route_signature: + + if turn_route["signature"] != _cli._active_agent_route_signature: _cli.agent = None init_ok = _cli._init_agent( - model_override=model_override, - runtime_override=turn_route['runtime'], - request_overrides=turn_route.get('request_overrides'), + model_override=turn_route["model"], + runtime_override=turn_route["runtime"], + request_overrides=turn_route.get("request_overrides"), ) if not init_ok: return "⚠️ Kon Hermes agent niet initialiseren." @@ -137,11 +134,11 @@ def call_hermes(prompt): return "⚠️ Hermes error: " + str(e) elapsed = time.time() - start - + # Fallback als Hermes None teruggeeft (error path) if result is None: logger.warning("Hermes returned None — possible error during query") return "⚠️ Geen antwoord van Hermes. Probeer opnieuw." - + logger.info("Hermes responded in %.1fs (%d chars)", elapsed, len(result)) - return result + return result \ No newline at end of file