# syntax=docker/dockerfile:1 # dockerfiles/Dockerfile.hermes-webui FROM ghcr.io/nesquena/hermes-webui@sha256:a83a3893111dcb250e7aa7aa657d3d6f4570b0e2fd00d9b7569246fc5e7339b2 AS webui FROM registry.bstein.dev/bstein/hermes-agent@sha256:15c5c538c0b58686af2e54e10bc870b23284789d485a609349df24ed3053622f USER root # Keep WebUI and Hermes pinned together. The WebUI imports Hermes internals, # while the gateway remains the only process that owns an agent conversation. COPY --from=webui /apptoo /opt/hermes-webui # The account policy caps user-selected reasoning at xhigh even when a provider # advertises a newer, more expensive level. RUN /opt/hermes/.venv/bin/python - <<'PY' from pathlib import Path config = Path("/opt/hermes-webui/api/config.py") source = config.read_text(encoding="utf-8") before = 'VALID_REASONING_EFFORTS = ("minimal", "low", "medium", "high", "xhigh", "max")' after = 'VALID_REASONING_EFFORTS = ("minimal", "low", "medium", "high", "xhigh")' if before not in source: raise SystemExit("Hermes WebUI reasoning-effort patch context changed") config.write_text(source.replace(before, after, 1), encoding="utf-8") index = Path("/opt/hermes-webui/static/index.html") source = index.read_text(encoding="utf-8") before = '
Max
\n' if before not in source: raise SystemExit("Hermes WebUI xhigh UI patch context changed") index.write_text(source.replace(before, "", 1), encoding="utf-8") PY RUN /opt/hermes/.venv/bin/python -c 'import cryptography, yaml' \ && grep -Fq 'VALID_REASONING_EFFORTS = ("minimal", "low", "medium", "high", "xhigh")' \ /opt/hermes-webui/api/config.py \ && ! grep -Fq 'data-effort="max"' /opt/hermes-webui/static/index.html # Exercise the real server process in the target architecture before publish. RUN set -eu; \ mkdir -p /tmp/hermes-webui-smoke/home /tmp/hermes-webui-smoke/state /tmp/hermes-webui-smoke/workspace; \ HERMES_HOME=/tmp/hermes-webui-smoke/home \ HOME=/tmp/hermes-webui-smoke/home \ HERMES_WEBUI_STATE_DIR=/tmp/hermes-webui-smoke/state \ HERMES_WEBUI_DEFAULT_WORKSPACE=/tmp/hermes-webui-smoke/workspace \ HERMES_WEBUI_HOST=127.0.0.1 \ HERMES_WEBUI_PORT=18787 \ HERMES_WEBUI_SKIP_ONBOARDING=1 \ /opt/hermes/.venv/bin/python /opt/hermes-webui/server.py >/tmp/hermes-webui-smoke.log 2>&1 & \ server_pid=$!; \ ready=0; \ for attempt in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30; do \ if /opt/hermes/.venv/bin/python -c 'from urllib.request import urlopen; urlopen("http://127.0.0.1:18787/health", timeout=2).read()' >/dev/null 2>&1; then ready=1; break; fi; \ sleep 1; \ done; \ kill "${server_pid}" 2>/dev/null || true; \ wait "${server_pid}" 2>/dev/null || true; \ if [ "${ready}" != "1" ]; then cat /tmp/hermes-webui-smoke.log; exit 1; fi; \ rm -rf /tmp/hermes-webui-smoke /tmp/hermes-webui-smoke.log ENV HERMES_WEBUI_AGENT_DIR=/opt/hermes \ HERMES_WEBUI_HOST=0.0.0.0 \ HERMES_WEBUI_PORT=8787 \ HERMES_WEBUI_CHAT_BACKEND=gateway \ HERMES_WEBUI_GATEWAY_BASE_URL=http://127.0.0.1:8642 \ HERMES_WEBUI_GATEWAY_USE_RUNS_API=true \ HERMES_WEBUI_SKIP_ONBOARDING=1 \ HERMES_WEBUI_SECURE=1 \ PYTHONDONTWRITEBYTECODE=1 \ PYTHONUNBUFFERED=1 WORKDIR /opt/hermes-webui USER 10000:10000 EXPOSE 8787 ENTRYPOINT ["/opt/hermes/.venv/bin/python", "/opt/hermes-webui/server.py"]