#!/usr/bin/env bash set -Eeuo pipefail HERMES_CONTAINER=${HERMES_CONTAINER:-mike-ai-hermes} SECRETS_DIR=${SECRETS_DIR:-/etc/mike-ai} HERMES_DATA_DIR=${HERMES_DATA_DIR:-/data/hermes} die() { printf 'FEHLER: %s\n' "$*" >&2; exit 1; } docker inspect "$HERMES_CONTAINER" >/dev/null 2>&1 || \ die "Hermes-Container fehlt: $HERMES_CONTAINER" deadline=$((SECONDS + 180)) until [[ $(docker inspect --format '{{if .State.Health}}{{.State.Health.Status}}{{else}}{{.State.Status}}{{end}}' \ "$HERMES_CONTAINER" 2>/dev/null || true) == healthy ]]; do (( SECONDS < deadline )) || die "Hermes wurde nicht rechtzeitig gesund." sleep 2 done create_profile() { local name=$1 model=$2 context=$3 description=$4 if ! docker exec "$HERMES_CONTAINER" hermes profile show "$name" >/dev/null 2>&1; then docker exec "$HERMES_CONTAINER" hermes profile create "$name" \ --clone-from default --description "$description" fi docker exec "$HERMES_CONTAINER" hermes -p "$name" config set model.default "$model" docker exec "$HERMES_CONTAINER" hermes -p "$name" config set model.context_length "$context" # These are platform-wide latency and loop safeguards, not model-specific # tuning. Enforce them on old profiles as well as newly cloned profiles. docker exec "$HERMES_CONTAINER" hermes -p "$name" config set model.max_tokens 8192 docker exec "$HERMES_CONTAINER" hermes -p "$name" config set agent.max_turns 64 docker exec "$HERMES_CONTAINER" hermes -p "$name" config set agent.reasoning_effort minimal docker exec "$HERMES_CONTAINER" hermes -p "$name" config set auxiliary.title_generation.enabled false docker exec "$HERMES_CONTAINER" hermes -p "$name" config unset compression.threshold_tokens || true docker exec "$HERMES_CONTAINER" hermes -p "$name" config set compression.threshold 0.82 docker exec "$HERMES_CONTAINER" hermes -p "$name" config set compression.target_ratio 0.35 docker exec "$HERMES_CONTAINER" hermes -p "$name" config set compression.protect_last_n 20 docker exec "$HERMES_CONTAINER" hermes -p "$name" config set compression.protect_first_n 0 docker exec "$HERMES_CONTAINER" hermes -p "$name" config set compression.micro_compact true docker exec "$HERMES_CONTAINER" hermes -p "$name" config set compression.micro_compact_every_n_turns 5 docker exec "$HERMES_CONTAINER" hermes -p "$name" config set compression.micro_compact_defrag_threshold_tokens 2000 # The selected 27B profile performs production compaction. A separate small # compressor was removed after it lost exact technical state in benchmarks. docker exec "$HERMES_CONTAINER" hermes -p "$name" config unset auxiliary.compression || true docker exec "$HERMES_CONTAINER" hermes -p "$name" config set platform_toolsets.cli \ '["web","terminal","file","skills","todo","memory","vision","tts"]' [[ $(docker exec "$HERMES_CONTAINER" hermes -p "$name" config get model.default) == "$model" ]] || \ die "Modellalias von Profil $name konnte nicht verifiziert werden." [[ $(docker exec "$HERMES_CONTAINER" hermes -p "$name" config get model.context_length) == "$context" ]] || \ die "Kontext von Profil $name konnte nicht verifiziert werden." } create_profile fast qwen-fast 76800 \ "Schnelles Qwen3.8-27B-Profil mit 76,8K Kontext fuer kurze Chats und schnelle Aufgaben." create_profile medium qwen-medium 160000 \ "Ausgewogenes Qwen3.8-27B-Standardprofil mit 160K Kontext fuer Alltag und agentische Aufgaben." create_profile large qwen-large 192000 \ "Grosses Qwen3.8-27B-Profil mit 192K Kontext fuer umfangreiche Dokumente und lange Aufgaben." create_profile ultra qwen-ultra 262144 \ "Maximales Qwen3.8-27B-Profil mit 262K Kontext fuer sehr grosse Kontexte; langsamer als die Standardprofile." create_profile uncensored qwen-uncensored 80000 \ "Unzensiertes Qwen3.8-27B-Profil mit 80K Kontext fuer spezielle Anfragen." # Existing profiles may predate managed secret rendering and therefore contain # the literal ${ROUTER_API_KEY}. Repair only that exact placeholder; never log # or commit the secret itself. [[ -s $SECRETS_DIR/router-api-key ]] || die "Router-API-Key fehlt." router_key=$(<"$SECRETS_DIR/router-api-key") ROUTER_API_KEY="$router_key" HERMES_DATA_DIR="$HERMES_DATA_DIR" python3 <<'PY' import os import pathlib root = pathlib.Path(os.environ["HERMES_DATA_DIR"]) / "profiles" placeholder = "${ROUTER_API_KEY}" paths = [pathlib.Path(os.environ["HERMES_DATA_DIR"]) / "config.yaml"] paths.extend(sorted(root.glob("*/config.yaml"))) for path in paths: if not path.exists(): continue text = path.read_text() if placeholder in text: text = text.replace(placeholder, os.environ["ROUTER_API_KEY"], 1) # Avoid collision with Hermes' disabled built-in `homeassistant` toolset. # The collision filters the healthy external MCP out of agent snapshots. text = text.replace( "\n homeassistant:\n url: http://mcp-homeassistant:8000/mcp\n", "\n homeassistant-admin:\n url: http://mcp-homeassistant:8000/mcp\n", ) path.write_text(text) PY sync_args=(--registry "${STACK_DIR:-/opt/mike-ai/stack}/config/mcp-registry.json") while IFS= read -r config; do sync_args+=(--hermes "$config") done < <(find "$HERMES_DATA_DIR" -name config.yaml -type f -print) python3 "${STACK_DIR:-/opt/mike-ai/stack}/platform/mcp/sync-clients.py" "${sync_args[@]}" "${STACK_DIR:-/opt/mike-ai/stack}/platform/hermes/install-skills.sh" docker exec "$HERMES_CONTAINER" hermes profile list printf 'HERMES_PROFILES_OK\n'