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
This commit is contained in:
@@ -1,22 +1,19 @@
|
|||||||
"""
|
"""
|
||||||
Hermes Relay v2 — Flask bridge tussen Node-RED en Hermes Agent.
|
Hermes Relay v2 — Flask bridge tussen Node-RED en Hermes Agent.
|
||||||
|
|
||||||
Gebruikt een warme Hermes agent (directe Python import) in plaats van
|
Gebruikt de voice-assistant profiel (minimale chatbot, max_turns=10).
|
||||||
subprocess per query. Dit bespaart ~3-5 seconden startup tijd per query.
|
Geen prefix/model override nodig — profiel handelt dit af.
|
||||||
|
|
||||||
Node-RED stuurt POST met msg.payload → Hermes (direct) → Antwoord + Telegram.
|
Node-RED stuurt POST met msg.payload → Hermes (direct) → Alleen antwoord terug.
|
||||||
|
|
||||||
Endpoints:
|
|
||||||
POST /ask — main relay endpoint
|
|
||||||
GET /health — health check
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
# ── Hermes path toevoegen (zodat we Hermes direct kunnen importeren) ───
|
# ── 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:
|
if _HERMES_SITE not in sys.path:
|
||||||
sys.path.insert(0, _HERMES_SITE)
|
sys.path.insert(0, _HERMES_SITE)
|
||||||
|
|
||||||
@@ -24,9 +21,7 @@ from flask import Flask, request, jsonify
|
|||||||
|
|
||||||
# ── Config ──────────────────────────────────────────────────────────────
|
# ── Config ──────────────────────────────────────────────────────────────
|
||||||
TIMEOUT = 120 # max seconden voor Hermes query
|
TIMEOUT = 120 # max seconden voor Hermes query
|
||||||
TG_TARGET = "telegram" # hermes send target
|
TG_TARGET = "telegram" # hermes send target
|
||||||
PROMPT_PREFIX = "/fast" # wordt aan elke prompt toegevoegd
|
|
||||||
MODEL = "qwen3.7-max" # model override (leeg = default uit config)
|
|
||||||
|
|
||||||
# ── Logging ─────────────────────────────────────────────────────────────
|
# ── Logging ─────────────────────────────────────────────────────────────
|
||||||
logging.basicConfig(
|
logging.basicConfig(
|
||||||
@@ -40,24 +35,53 @@ logger = logging.getLogger(__name__)
|
|||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
|
|
||||||
# ── Hermes warm houden ──────────────────────────────────────────────────
|
# ── 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
|
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):
|
def send_telegram(text):
|
||||||
"""Stuur bericht via hermes send subprocess."""
|
"""Stuur bericht via gateway's send_message_tool (gebruikt gateway config met echte token)."""
|
||||||
import subprocess
|
|
||||||
try:
|
try:
|
||||||
result = subprocess.run(
|
logger.info("Telegram send: starting...")
|
||||||
[HERMES_BIN, "send", "--to", TG_TARGET, "--quiet", text],
|
_ensure_gw_config()
|
||||||
capture_output=True,
|
logger.info("Telegram send: gw config loaded")
|
||||||
text=True,
|
from tools.send_message_tool import send_message_tool
|
||||||
timeout=30,
|
import json
|
||||||
)
|
|
||||||
return result.returncode == 0
|
# 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:
|
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
|
return False
|
||||||
|
|
||||||
|
|
||||||
@@ -65,7 +89,7 @@ def send_telegram(text):
|
|||||||
|
|
||||||
@app.route("/ask", methods=["POST"])
|
@app.route("/ask", methods=["POST"])
|
||||||
def ask():
|
def ask():
|
||||||
"""Main relay endpoint."""
|
"""Main relay endpoint — retourneert alleen het antwoord."""
|
||||||
start = time.time()
|
start = time.time()
|
||||||
|
|
||||||
# Parse input
|
# Parse input
|
||||||
@@ -104,13 +128,12 @@ def health():
|
|||||||
return jsonify({
|
return jsonify({
|
||||||
"status": "ok",
|
"status": "ok",
|
||||||
"service": "hermes-relay",
|
"service": "hermes-relay",
|
||||||
"version": "2.0",
|
"version": "2.1",
|
||||||
"mode": "python-import",
|
"mode": "python-import",
|
||||||
"model": MODEL or "(default)",
|
"profile": "voice-assistant",
|
||||||
"prompt_prefix": PROMPT_PREFIX or "(none)",
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
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)
|
app.run(host="0.0.0.0", port=8650, debug=False)
|
||||||
+31
-34
@@ -3,6 +3,9 @@ Hermes Client — directe Python import (geen subprocess overhead).
|
|||||||
|
|
||||||
Warme agent: eenmalig initialiseren, daarna hergebruiken voor elke query.
|
Warme agent: eenmalig initialiseren, daarna hergebruiken voor elke query.
|
||||||
Dit bespaart de subprocess + Python startup overhead van 2-5 seconden per 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 io
|
||||||
import os
|
import os
|
||||||
@@ -17,8 +20,8 @@ logger = logging.getLogger(__name__)
|
|||||||
_HERMES_LOADED = False
|
_HERMES_LOADED = False
|
||||||
_cli = None
|
_cli = None
|
||||||
|
|
||||||
# Model override voor relay (leeg = default uit config)
|
# Voice-assistant profiel home directory
|
||||||
MODEL_OVERRIDE = "qwen3.7-plus"
|
_HERMES_HOME = "/root/.hermes/profiles/voice-assistant"
|
||||||
|
|
||||||
|
|
||||||
def _ensure_hermes_loaded():
|
def _ensure_hermes_loaded():
|
||||||
@@ -29,52 +32,50 @@ def _ensure_hermes_loaded():
|
|||||||
|
|
||||||
start = time.time()
|
start = time.time()
|
||||||
|
|
||||||
|
# Forceer voice-assistant profiel via HERMES_HOME
|
||||||
|
os.environ["HERMES_HOME"] = _HERMES_HOME
|
||||||
|
|
||||||
# Zorg dat Hermes in de Python path zit
|
# 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:
|
if hermes_path not in sys.path:
|
||||||
sys.path.insert(0, hermes_path)
|
sys.path.insert(0, hermes_path)
|
||||||
|
|
||||||
# Force UTF-8
|
# Force UTF-8
|
||||||
os.environ.setdefault('PYTHONIOENCODING', 'utf-8')
|
os.environ.setdefault("PYTHONIOENCODING", "utf-8")
|
||||||
|
|
||||||
# Laad .env via Hermes' eigen loader
|
# Laad .env via Hermes' eigen loader
|
||||||
from hermes_cli.env_loader import load_hermes_dotenv
|
from hermes_cli.env_loader import load_hermes_dotenv
|
||||||
from hermes_constants import get_hermes_home
|
from hermes_constants import get_hermes_home
|
||||||
_hermes_home = 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_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
|
from hermes_cli.config import load_config
|
||||||
config = load_config()
|
config = load_config()
|
||||||
|
|
||||||
# Resolve toolsets (geen tools voor snelle relay)
|
# Gebruik messaging toolset zodat agent send_message tool kan gebruiken
|
||||||
from hermes_cli.tools_config import _get_platform_tools
|
toolsets = ["messaging"]
|
||||||
if MODEL_OVERRIDE:
|
|
||||||
# Geen tools laden bij model override (DeepSeek heeft incompatible tool schema)
|
|
||||||
toolsets = []
|
|
||||||
else:
|
|
||||||
toolsets = sorted(_get_platform_tools(config, 'cli'))
|
|
||||||
|
|
||||||
# YOLO mode + interactive mode
|
# YOLO mode + interactive mode
|
||||||
os.environ['HERMES_YOLO_MODE'] = '1'
|
os.environ["HERMES_YOLO_MODE"] = "1"
|
||||||
os.environ['HERMES_INTERACTIVE'] = '1'
|
os.environ["HERMES_INTERACTIVE"] = "1"
|
||||||
os.environ['HERMES_SESSION_SOURCE'] = 'relay'
|
os.environ["HERMES_SESSION_SOURCE"] = "relay"
|
||||||
|
|
||||||
# Import HermesCLI
|
# Import HermesCLI
|
||||||
from cli import HermesCLI
|
from cli import HermesCLI
|
||||||
|
|
||||||
# Maak CLI instantie (nog géén agent init — dat gebeurt pas bij eerste query)
|
# Maak CLI instantie (nog géén agent init — dat gebeurt pas bij eerste query)
|
||||||
_cli = HermesCLI(
|
_cli = HermesCLI(
|
||||||
model=config.get('model', {}).get('default', ''),
|
model=config.get("model", {}).get("default", ""),
|
||||||
toolsets=toolsets,
|
toolsets=toolsets,
|
||||||
)
|
)
|
||||||
# Onderdruk banner/status output
|
# Onderdruk banner/status output
|
||||||
_cli.tool_progress_mode = 'off'
|
_cli.tool_progress_mode = "off"
|
||||||
|
|
||||||
elapsed = time.time() - start
|
elapsed = time.time() - start
|
||||||
logger.info(
|
logger.info(
|
||||||
"Hermes loaded in %.2fs (toolsets: %s)",
|
"Hermes loaded in %.2fs (toolsets: %s, profile: voice-assistant)",
|
||||||
elapsed, toolsets
|
elapsed, toolsets
|
||||||
)
|
)
|
||||||
_HERMES_LOADED = True
|
_HERMES_LOADED = True
|
||||||
@@ -90,24 +91,20 @@ def call_hermes(prompt):
|
|||||||
"""
|
"""
|
||||||
_ensure_hermes_loaded()
|
_ensure_hermes_loaded()
|
||||||
|
|
||||||
# Prefix (zoals /fast)
|
# Geen prefix meer — voice-assistant profiel handelt dit af via config
|
||||||
prefix = os.environ.get('HERMES_RELAY_PREFIX', '/fast')
|
full_prompt = prompt.strip()
|
||||||
full_prompt = (prefix + " " + prompt).strip() if prefix else prompt
|
|
||||||
|
|
||||||
start = time.time()
|
start = time.time()
|
||||||
|
|
||||||
# ── Agent initialiseren met model override ──
|
# ── Agent initialiseren ──
|
||||||
turn_route = _cli._resolve_turn_agent_config(full_prompt)
|
turn_route = _cli._resolve_turn_agent_config(full_prompt)
|
||||||
|
|
||||||
# Override model als MODEL_OVERRIDE is ingesteld
|
if turn_route["signature"] != _cli._active_agent_route_signature:
|
||||||
model_override = MODEL_OVERRIDE if MODEL_OVERRIDE else turn_route['model']
|
|
||||||
|
|
||||||
if turn_route['signature'] != _cli._active_agent_route_signature:
|
|
||||||
_cli.agent = None
|
_cli.agent = None
|
||||||
init_ok = _cli._init_agent(
|
init_ok = _cli._init_agent(
|
||||||
model_override=model_override,
|
model_override=turn_route["model"],
|
||||||
runtime_override=turn_route['runtime'],
|
runtime_override=turn_route["runtime"],
|
||||||
request_overrides=turn_route.get('request_overrides'),
|
request_overrides=turn_route.get("request_overrides"),
|
||||||
)
|
)
|
||||||
if not init_ok:
|
if not init_ok:
|
||||||
return "⚠️ Kon Hermes agent niet initialiseren."
|
return "⚠️ Kon Hermes agent niet initialiseren."
|
||||||
@@ -137,11 +134,11 @@ def call_hermes(prompt):
|
|||||||
return "⚠️ Hermes error: " + str(e)
|
return "⚠️ Hermes error: " + str(e)
|
||||||
|
|
||||||
elapsed = time.time() - start
|
elapsed = time.time() - start
|
||||||
|
|
||||||
# Fallback als Hermes None teruggeeft (error path)
|
# Fallback als Hermes None teruggeeft (error path)
|
||||||
if result is None:
|
if result is None:
|
||||||
logger.warning("Hermes returned None — possible error during query")
|
logger.warning("Hermes returned None — possible error during query")
|
||||||
return "⚠️ Geen antwoord van Hermes. Probeer opnieuw."
|
return "⚠️ Geen antwoord van Hermes. Probeer opnieuw."
|
||||||
|
|
||||||
logger.info("Hermes responded in %.1fs (%d chars)", elapsed, len(result))
|
logger.info("Hermes responded in %.1fs (%d chars)", elapsed, len(result))
|
||||||
return result
|
return result
|
||||||
Reference in New Issue
Block a user