4 Commits

Author SHA1 Message Date
wouser 58ca990673 v2.1.1: Alleen antwoord naar Telegram sturen (geen Vraag:/Antwoord: prefix) 2026-06-16 14:04:59 +02:00
wouser e05fa9c5c1 v2.1: Switch to voice-assistant profile
- Use voice-assistant profile (minimal chatbot, max_turns=10, nous provider)
- Remove /fast prefix and model override - handled by profile config
- Use messaging toolset for send_message tool (Telegram delivery)
- Add hardcoded TELEGRAM_BOT_TOKEN and TELEGRAM_HOME_CHANNEL to relay env
- Telegram delivery now works via gateway's send_message_tool
- Version bump to 2.1
- Health endpoint shows profile: voice-assistant
2026-06-16 09:37:48 +02:00
wouser 1780eed406 feat: relay met qwen3.7-plus voor snellere responses
- Model override: qwen3.7-plus (4.5x meer requests/uur dan max)
- Toolsets disabled bij model override (DeepSeek compat fix)
- NoneType crash fix voor error paths
- Originele qwen3.7-max blijft actief voor andere chats
2026-06-08 19:48:31 +02:00
wouser 75311845ee chore: relay v2 cleanup — remove f-strings, add version/mode to health endpoint 2026-06-08 19:00:10 +02:00
2 changed files with 99 additions and 62 deletions
+58 -34
View File
@@ -1,22 +1,19 @@
"""
Hermes Relay — Flask bridge tussen Node-RED en Hermes Agent (v2.0).
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 send_telegram(text: str) -> bool:
"""Stuur bericht via hermes send subprocess."""
import subprocess
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 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(f"Telegram send failed: {e}")
logger.error("Telegram send failed with exception: %s", e, exc_info=True)
return False
@@ -65,7 +89,7 @@ def send_telegram(text: str) -> bool:
@app.route("/ask", methods=["POST"])
def ask():
"""Main relay endpoint."""
"""Main relay endpoint — retourneert alleen het antwoord."""
start = time.time()
# Parse input
@@ -78,17 +102,16 @@ def ask():
if not prompt:
return jsonify({"status": "error", "message": "No payload received"}), 400
logger.info(f"Received: {prompt[:100]}...")
logger.info("Received: %s...", prompt[:100])
# Roep Hermes aan (direct, geen subprocess)
answer = _hermes_query(prompt)
elapsed = round(time.time() - start, 1)
logger.info(f"Answer ({elapsed}s): {answer[:200]}...")
logger.info("Answer (%.1fs): %s...", elapsed, answer[:200])
# Stuur ook naar Telegram
tg_msg = f"🤖 Hermes Relay\n\nVraag: {prompt}\n\nAntwoord: {answer}"
tg_sent = send_telegram(tg_msg)
# Stuur ook naar Telegram (alleen het antwoord)
tg_sent = send_telegram(answer)
return jsonify({
"status": "ok",
@@ -104,11 +127,12 @@ def health():
return jsonify({
"status": "ok",
"service": "hermes-relay",
"model": MODEL or "(default)",
"prompt_prefix": PROMPT_PREFIX or "(none)",
"version": "2.1",
"mode": "python-import",
"profile": "voice-assistant",
})
if __name__ == "__main__":
logger.info("Starting Hermes Relay v2.0 (warme agent)...")
logger.info("Starting Hermes Relay v2.1 (voice-assistant profiel)...")
app.run(host="0.0.0.0", port=8650, debug=False)
+39 -26
View File
@@ -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,6 +20,9 @@ logger = logging.getLogger(__name__)
_HERMES_LOADED = False
_cli = None
# Voice-assistant profiel home directory
_HERMES_HOME = "/root/.hermes/profiles/voice-assistant"
def _ensure_hermes_loaded():
"""Laad Hermes eenmalig bij eerste gebruik — dit is het zware werk."""
@@ -26,54 +32,56 @@ 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 (zelfde als 'cli' platform)
from hermes_cli.tools_config import _get_platform_tools
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(
f"Hermes loaded in {elapsed:.2f}s "
f"(toolsets: {toolsets})"
"Hermes loaded in %.2fs (toolsets: %s, profile: voice-assistant)",
elapsed, toolsets
)
_HERMES_LOADED = True
def call_hermes(prompt: str) -> str:
def call_hermes(prompt):
"""
Roep Hermes aan — hergebruikt een warme agent.
@@ -83,20 +91,20 @@ def call_hermes(prompt: str) -> str:
"""
_ensure_hermes_loaded()
# Prefix (zoals /fast)
prefix = os.environ.get('HERMES_RELAY_PREFIX', '/fast')
full_prompt = f"{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 (eenmalig, cached) ──
# ── Agent initialiseren ──
turn_route = _cli._resolve_turn_agent_config(full_prompt)
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=turn_route['model'],
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."
@@ -122,10 +130,15 @@ def call_hermes(prompt: str) -> str:
logger.warning("Hermes riep sys.exit() aan tijdens query")
result = stdout_capture.getvalue().strip()
except Exception as e:
logger.error(f"Hermes error: {e}", exc_info=True)
return f"⚠️ Hermes error: {e}"
logger.error("Hermes error: %s", e, exc_info=True)
return "⚠️ Hermes error: " + str(e)
elapsed = time.time() - start
logger.info(f"Hermes responded in {elapsed:.1f}s ({len(result)} chars)")
# 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