Compare commits

...

22 Commits

Author SHA1 Message Date
48c4c1b232 fix: remove stale COPY line for deleted run-multi-gateways.sh
Some checks failed
Build Hermes agent / build (pull_request) Has been cancelled
2026-07-07 22:33:35 -04:00
53fcd59dff Merge pull request 'Simplify: use native s6 multi-profile supervision, drop custom scripts' (#58) from fix/hermes-entrypoint-simple into master
Some checks failed
Build Hermes agent / build (push) Has been cancelled
Build ollama (gfx906) / build (push) Has been cancelled
Reviewed-on: #58
2026-07-08 02:25:01 +00:00
3f82c9d321 Simplify: drop custom scripts, use native s6 multi-profile supervision
Some checks failed
Build Hermes agent / build (pull_request) Has been cancelled
Build ollama (gfx906) / build (pull_request) Has been cancelled
- Remove patch_tts_tool.py (orphaned)
- Remove run-multi-gateways.sh (replaced by s6 native profile supervision)
- Remove start-hermes.sh (overengineered — the image does this natively)
- compose.yml: command: gateway run, drop HERMES_PROFILES env var
- Dockerfile: revert start-hermes.sh COPY addition

Per-profile gateways are now managed by s6-overlay natively:
profiles with desired_state=running in gateway_state.json are
auto-started by the s6 reconciler on boot.
2026-07-07 22:21:36 -04:00
a7e463dcd6 Merge pull request 'fix: s6-overlay entrypoint crash — replace broken bash→tini→entrypoint.sh chain' (#56) from fix/hermes-s6-overlay-entrypoint-crash into master
Some checks failed
Build Hermes agent / build (push) Has been cancelled
Build ollama (gfx906) / build (push) Has been cancelled
Reviewed-on: #56
2026-07-08 02:04:44 +00:00
59fc28401e Merge pull request 'fix/hermes-gosu-honcho-ssh' (#57) from fix/hermes-gosu-honcho-ssh into master
Some checks failed
Build Hermes agent / build (push) Has been cancelled
Reviewed-on: #57
2026-07-08 02:02:50 +00:00
f4dd57fa60 fix: add start-hermes.sh baked into image, replace broken ENTRYPOINT
Some checks failed
Build Hermes agent / build (pull_request) Has been cancelled
Build ollama (gfx906) / build (pull_request) Has been cancelled
- New start-hermes.sh: multi-profile launcher that works with s6-overlay's
  main-program model (replaces bash->tini->entrypoint.sh chain)
- Dockerfile: COPY start-hermes.sh into /usr/local/bin/ alongside
  run-multi-gateways.sh (which is now unused but kept for reference)
- compose.yml: remove entrypoint override, CMD now points at
  /usr/local/bin/start-hermes.sh via the image default ENTRYPOINT

Fixes the SIGTERM crash loop caused by S6_CMD_ARG0 being unset when the
deprecated entrypoint.sh shim bypassed s6-overlay's /init.
2026-07-07 21:58:41 -04:00
a6b25ee84d fix: replace broken ENTRYPOINT override with s6-overlay compatible CMD
The custom ENTRYPOINT chained bash -> tini -g -> deprecated entrypoint.sh,
bypassing s6-overlay's /init entirely. This left S6_CMD_ARG0 unset and the
orphan -g flag crashed rc.init with '-g: not found' -> SIGTERM -> restart loop.

Fix:
- Remove the ENTRYPOINT override so the image default is used:
  ENTRYPOINT ['/init', '/opt/hermes/docker/main-wrapper.sh']
- Change CMD to point at /opt/data/start-hermes.sh, a new launcher
  that starts per-profile gateways in background then the default
  gateway in foreground (via s6-overlay's main-program model).

The old /usr/local/bin/run-multi-gateways.sh is no longer called.
2026-07-07 21:56:33 -04:00
8df60c2911 fix: update openconcho SHA (template literal for ReactNode)
Some checks failed
Build Hermes agent / build (pull_request) Has been cancelled
2026-07-07 20:52:38 -04:00
a79f0610ec fix: update openconcho SHA (cast metadata.name to string) 2026-07-07 20:50:02 -04:00
be757bc683 chore: update openconcho to upstream v0.16.0 (rebase feat/fix-openconcho on upstream/main)
- rebased custom commits on upstream v0.16.0
- 125 upstream commits since fork base (e490d91)
- includes dashboard, fleet, dark mode, deps consolidation
- our TS fixes preserved on top
2026-07-07 20:43:02 -04:00
3508ae2481 fix: update openconcho SHA to HEAD of feat/fix-openconcho branch (was force-pushed) 2026-07-07 20:09:16 -04:00
eb5176949e fix: add gosu to hermes SSH keyscan to honcho builder
- Install gosu in hermes image so run-multi-gateways.sh can drop
  privileges when USER hermes is set at the end of the Dockerfile.
- Add openssh-client + ssh-keyscan to the openconcho-builder stage
  so SSH host key verification passes during the first Gitea clone.
2026-07-07 19:48:59 -04:00
5db33ddfd2 fix: bump llama.cpp from b9596 to b9890 for slot/stability fixes
b9596 (Dec 2024) has known slot deadlock on first request causing
container freeze. b9890 includes:
- Slot selection consolidation (#24755)
- HIP fast-math support (#23862)
- Hundreds of stability and memory fixes

Compatible with ROCm 6.1 + gfx906 (same build flags).
2026-07-07 16:57:22 -04:00
2d2787f404 Merge pull request 'fix: add missing USER hermes at end of Dockerfile' (#55) from local_changes into master
Some checks failed
Build Hermes agent / build (push) Has been cancelled
Build ollama (gfx906) / build (push) Has been cancelled
Reviewed-on: #55
2026-07-07 19:41:28 +00:00
dab1ecfdd7 Merge remote-tracking branch 'origin/master' into local_changes
Some checks failed
Build Hermes agent / build (pull_request) Has been cancelled
Build ollama (gfx906) / build (pull_request) Has been cancelled
# Conflicts:
#	ai/compose.yml
2026-07-07 15:04:00 -04:00
e595381173 Merge pull request 'feat: add llama-cpp-hermes service with ROCm 6.1 + gfx906 support' (#54) from feat/hermes43-llamacpp into master
Some checks failed
Build Hermes agent / build (push) Has been cancelled
Build ollama (gfx906) / build (push) Has been cancelled
Reviewed-on: #54
2026-07-07 19:00:03 +00:00
54e0661396 fix: add missing USER hermes at end of Dockerfile
The Dockerfile was switching to USER root for the final chown but never
switched back to USER hermes. This caused ALL container processes to run
as root (uid 0) instead of the hermes user (uid 10000).

The entrypoint's gosu privilege drop only caught the main exec chain,
leaving backgrounded subprocesses (dashboard PTY sessions, workers with
start_new_session=True) running as root — creating files owned by root
in ExoKortex and breaking Syncthing sync.

Adding USER hermes at the end ensures the container runs unprivileged
and ALL child processes inherit uid 10000 from the start.
2026-07-07 14:58:44 -04:00
1e290410de Merge branch 'master' into feat/hermes43-llamacpp
Some checks failed
Build Hermes agent / build (pull_request) Has been cancelled
Build ollama (gfx906) / build (pull_request) Has been cancelled
Conflicts resolved:
- hermes env: keep OLLAMA_HOST=ollama-cpu + master's new vars
- ollama→ollama-cpu rename: keep our rename over master's ollama
2026-07-07 14:50:59 -04:00
80c9906757 fix: rename ollama→ollama-cpu, fix llama-cpp-hermes YAML indentation
Some checks failed
Build Hermes agent / build (pull_request) Has been cancelled
Build ollama (gfx906) / build (pull_request) Has been cancelled
- Rename ollama service to ollama-cpu (CPU-only for bge-m3 embeddings)
- Fix llama-cpp-hermes indented under networks instead of as top-level service
- Update hermes OLLAMA_HOST to point to ollama-cpu

Part of PR #54 GPU/ROCm refactor
2026-07-07 14:29:23 -04:00
2571930efa Local change dump 2026-07-07 14:11:31 -04:00
e525145057 Merge pull request 'feat: add Honcho memory provider with Traefik + Authelia' (#48) from feat/honcho-only into master
Some checks failed
Build Hermes agent / build (push) Has been cancelled
Build ollama (gfx906) / build (push) Has been cancelled
Reviewed-on: #48
2026-06-15 17:08:20 +00:00
3c92d93366 feat: add llama-cpp-hermes service with ROCm 6.1 + gfx906 support
Some checks failed
Build Hermes agent / build (pull_request) Has been cancelled
Build ollama (gfx906) / build (pull_request) Has been cancelled
- Add custom llama.cpp Dockerfile with ROCm 6.1 + gfx906 (MI50) build
- Add llama-cpp-hermes service serving Hermes 4.3 on dual MI50 GPUs
- Strip GPU devices/ROCm env from ollama service (CPU-only for embeddings)

Hermes 4.3 runs at ~19 t/s on dual MI50s with 160K context.
2026-06-11 11:41:42 -04:00
18 changed files with 197 additions and 409 deletions

View File

@@ -1,48 +1,19 @@
services:
# webui:
# image: ghcr.io/open-webui/open-webui:main
# volumes:
# - /mnt/HoardingCow_docker_data/Ollama/open-webui:/app/backend/data
# restart: always
# environment:
# - OLLAMA_API_BASE_URL=http://ollama:11434/api
# networks:
# - ai_net
# - ai_backend
# labels:
# - "traefik.enable=true"
# # Router for HTTP + redirection to HTTPS
# - "traefik.http.routers.webui-http.rule=Host(`ai.lazyworkhorse.net`)"
# - "traefik.http.routers.webui-http.entrypoints=web"
# - "traefik.http.routers.webui-http.middlewares=redirect-to-https"
# # Router for HTTPS with TLS
# - "traefik.http.routers.webui-https.rule=Host(`ai.lazyworkhorse.net`)"
# - "traefik.http.routers.webui-https.entrypoints=websecure"
# - "traefik.http.routers.webui-https.tls=true"
# - "traefik.http.routers.webui-https.tls.certresolver=njalla"
hermes:
build:
context: ./hermes
ssh:
- default
container_name: hermes
entrypoint: ["/bin/bash", "-c",
"bash /opt/data/hermes-tools/install.sh && bash /usr/local/bin/run-multi-gateways.sh && exec /usr/bin/tini -g -- /opt/hermes/docker/entrypoint.sh \"$@\"",
"hermes-entrypoint"]
restart: always
# Gateway run enables the internal API server on port 8642
command: gateway run
environment:
- OLLAMA_HOST=http://ollama:11434
- HERMES_UID=10000
- HERMES_GID=10000
- OLLAMA_HOST=http://ollama-cpu:11434
- HERMES_DASHBOARD=1
# Multi-profile: comma-separated list of profiles to run as gateways.
# The entrypoint reads this and starts one gateway per profile.
# Add profiles here when they exist on disk (e.g. default,researcher,writer)
- HERMES_PROFILES=ashley,claire,finn,matt,paul
- API_SERVER_ENABLED=true
- API_SERVER_PORT=8642
- API_SERVER_HOST=0.0.0.0
@@ -59,10 +30,7 @@ services:
- TZ=America/Montreal
volumes:
- /mnt/HoardingCow_docker_data/Hermes/data:/opt/data
# Syncthing-shared org files — read-only view of user's agenda
- /mnt/HoardingCow_docker_data/Syncthing/telos-ro:/opt/data/telos-ro:ro
# Syncthing-shared inbox — write tasks here, they sync to user's laptop
- /mnt/HoardingCow_docker_data/Syncthing/telos-rw:/opt/data/telos-rw:rw
- /mnt/HoardingCow_docker_data/Hermes/Syncthing/ExoKortex:/opt/data/ExoKortex
devices:
- /dev/kfd:/dev/kfd
- /dev/dri:/dev/dri
@@ -109,31 +77,36 @@ services:
- "21027:21027/udp"
environment:
- TZ=America/Montreal
- PUID=10000
- PGID=10000
volumes:
- /mnt/HoardingCow_docker_data/Syncthing/config:/var/syncthing/config
- /mnt/HoardingCow_docker_data/Syncthing/telos-ro:/telos-ro
- /mnt/HoardingCow_docker_data/Syncthing/telos-rw:/telos-rw
- /mnt/HoardingCow_docker_data/Hermes/Syncthing/config:/var/syncthing/config
- /mnt/HoardingCow_docker_data/Hermes/Syncthing/ExoKortex:/ExoKortex
networks:
- ai_backend
- ai_net
labels:
- "traefik.enable=true"
- "traefik.docker.network=ai_net"
- "traefik.http.routers.syncthing-http.rule=Host(`syncthing.lazyworkhorse.net`)"
- "traefik.http.routers.syncthing-http.entrypoints=web"
- "traefik.http.routers.syncthing-http.middlewares=redirect-to-https"
- "traefik.http.routers.syncthing-https.rule=Host(`syncthing.lazyworkhorse.net`)"
- "traefik.http.routers.syncthing-https.entrypoints=websecure"
- "traefik.http.routers.syncthing-https.tls=true"
- "traefik.http.routers.syncthing-https.tls.certresolver=njalla"
- "traefik.http.routers.syncthing-https.middlewares=hermes-auth"
- "traefik.http.services.syncthing.loadbalancer.server.port=8384"
ollama:
ollama-cpu:
build:
context: ./ollama
dockerfile: Dockerfile
image: ollama/ollama:rocm-gfx906
container_name: ollama
container_name: ollama-cpu
tty: true
restart: always
ports:
@@ -144,22 +117,42 @@ services:
- /mnt/HoardingCow_docker_data/Ollama/ollama:/root/.ollama
environment:
- OLLAMA_VULKAN=0
- HSA_OVERRIDE_GFX_VERSION=9.0.6
- HCC_AMDGPU_TARGET=gfx906
- HIP_VISIBLE_DEVICES=0,1
- ROCR_VISIBLE_DEVICES=0,1
- HSA_ENABLE_SDMA=0
- OLLAMA_HOST=0.0.0.0
- OLLAMA_DEBUG=1
- OLLAMA_FLASH_ATTENTION=1
- OLLAMA_NUM_PARALLEL=2
llama-cpp-hermes:
image: llama-cpp:rocm-gfx906
container_name: llama-cpp-hermes
restart: unless-stopped
networks:
- ai_backend
ports:
- "127.0.0.1:8300:8080"
ipc: host
devices:
# Map the render nodes and KFD for ROCm to work inside the container
- /dev/kfd:/dev/kfd
- /dev/dri:/dev/dri
group_add:
- "303"
- "26"
environment:
- HSA_OVERRIDE_GFX_VERSION=9.0.6
- HSA_ENABLE_SDMA=0
- HIP_VISIBLE_DEVICES=0,1
- LLAMA_CACHE=/models
volumes:
- /mnt/HoardingCow_docker_data/Llama_cpp/models:/models
- /mnt/HoardingCow_docker_data/Ollama/ollama/models/blobs/sha256-17823599694fa3503ef54bf748d5078c6ce881f4d01616cafa255dc05d215a08:/model.gguf:ro
command: >
-m /model.gguf
--host 0.0.0.0
--port 8080
--gpu-layers 99
--ctx-size 163840
-ctk f16 -ctv f16
--flash-attn on
--split-mode layer
--no-mmap
--n-predict -1
# --- Honcho + OpenConcho combiné: API + Web UI nginx/FastAPI ---
honcho:
@@ -203,10 +196,8 @@ services:
# Service Loadbalancer (nginx port)
- "traefik.http.services.honcho.loadbalancer.server.port=80"
depends_on:
honcho-db:
condition: service_healthy
honcho-redis:
condition: service_healthy
- honcho-db
- honcho-redis
honcho-db:
image: pgvector/pgvector:pg15
@@ -225,11 +216,6 @@ services:
- ./honcho/init-db.sql:/docker-entrypoint-initdb.d/init.sql:ro
networks:
- ai_backend
healthcheck:
test: ["CMD-SHELL", "pg_isready -U honcho -d honcho"]
interval: 5s
timeout: 5s
retries: 5
honcho-redis:
image: redis:8
@@ -241,15 +227,10 @@ services:
- /mnt/HoardingCow_docker_data/Honcho/redis:/data
networks:
- ai_backend
healthcheck:
test: ["CMD-SHELL", "redis-cli ping"]
interval: 5s
timeout: 5s
retries: 5
networks:
ai_net:
external: true
driver: bridge
name: ai_net
ai_backend:
driver: bridge
@@ -257,51 +238,9 @@ networks:
volumes:
honcho_data:
external: true
driver: bridge
name: honcho_data
# llama_cpp_devstral:
# image: ghcr.io/ggml-org/llama.cpp:server-rocm
# container_name: llama_cpp_devstral
# restart: unless-stopped
# networks:
# - ai_backend
# ports:
# - "8300:8080"
# ipc: host
# devices:
# - "/dev/kfd:/dev/kfd"
# - "/dev/dri:/dev/dri"
# group_add:
# - "303" # video
# - "26" # render
# environment:
# HSA_OVERRIDE_GFX_VERSION: 9.0.6
# HIP_VISIBLE_DEVICES: 0,1
# LLAMA_CACHE: /models
# volumes:
# - /mnt/HoardingCow_docker_data/Llama_cpp/models:/models
# - /mnt/HoardingCow_docker_data/Llama_cpp/devstral-agent.jinja:/template.jinja
# command: >
# -hf unsloth/Devstral-Small-2-24B-Instruct-2512-GGUF:Devstral-Small-2-24B-Instruct-2512-Q8_0.gguf
# -a devstral-2-small-llama_cpp
# --chat-template-file /template.jinja
# --host 0.0.0.0
# --port 8080
# --n-gpu-layers 99
# --ctx-size 163840
# --batch-size 4096
# --ubatch-size 4096
# --cache-type-k f16
# --cache-type-v f16
# --cache-reuse 256
# --flash-attn on
# --context-shift
# --split-mode layer
# --no-mmap
# --n-predict -1
# --parallel 2
# vllm:
# image: nalanzeyu/vllm-gfx906:v0.9.0-rocm6.3
# container_name: vllm
@@ -348,6 +287,30 @@ volumes:
# - /mnt/HoardingCow_docker_data/vllm/models:/root/.cache/huggingface
# restart: unless-stopped
# webui:
# image: ghcr.io/open-webui/open-webui:main
# volumes:
# - /mnt/HoardingCow_docker_data/Ollama/open-webui:/app/backend/data
# restart: always
# environment:
# - OLLAMA_API_BASE_URL=http://ollama:11434/api
# networks:
# - ai_net
# - ai_backend
# labels:
# - "traefik.enable=true"
# # Router for HTTP + redirection to HTTPS
# - "traefik.http.routers.webui-http.rule=Host(`ai.lazyworkhorse.net`)"
# - "traefik.http.routers.webui-http.entrypoints=web"
# - "traefik.http.routers.webui-http.middlewares=redirect-to-https"
# # Router for HTTPS with TLS
# - "traefik.http.routers.webui-https.rule=Host(`ai.lazyworkhorse.net`)"
# - "traefik.http.routers.webui-https.entrypoints=websecure"
# - "traefik.http.routers.webui-https.tls=true"
# - "traefik.http.routers.webui-https.tls.certresolver=njalla"
# n8n:
# image: n8nio/n8n:latest
# container_name: n8n

View File

@@ -9,30 +9,11 @@
# ---------- Base: official Hermes image (system deps, npm, uv, Playwright) ----------
FROM nousresearch/hermes-agent:latest
# ---------- Overlay our forked source ----------
# Uses SSH agent forwarding from the build host (no key baked into image).
# --exclude node_modules/.venv keeps the base image's pre-built layers intact.
# Only the Python source, web UI source, and config change.
RUN --mount=type=ssh \
mkdir -p /root/.ssh && \
ssh-keyscan -p 2222 code.lazyworkhorse.net >> /root/.ssh/known_hosts 2>/dev/null && \
cd /tmp && \
GIT_SSH_COMMAND='ssh -p 2222 -o StrictHostKeyChecking=no' \
git clone --depth 1 --branch main \
git@code.lazyworkhorse.net:gortium/hermes-agent.git fork && \
rm -rf fork/node_modules fork/.venv fork/.git && \
cp -a fork/. /opt/hermes/ && \
rm -rf /tmp/fork /root/.ssh/
# ---------- Reinstall Python package (editable) ----------
# Picks up source changes from our fork.
RUN . /opt/hermes/.venv/bin/activate && \
uv pip install --no-cache-dir --no-deps -e /opt/hermes
# ---------- Extra system deps ----------
USER root
RUN apt-get update && \
apt-get install -y --no-install-recommends \
gosu \
libportaudio2 ca-certificates poppler-utils imagemagick \
libolm-dev \
texlive-latex-base texlive-latex-extra texlive-fonts-recommended \
@@ -43,12 +24,6 @@ RUN apt-get update && \
# ---------- UV ----------
COPY --chmod=0755 --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/
# ---------- Matrix bridge + extra pip deps ----------
# Previously installed inline at container startup and persisted via volume mount.
# Now baked into the image so the fragile venv volume mount can be removed.
RUN . /opt/hermes/.venv/bin/activate && \
uv pip install --no-cache-dir 'mautrix[encryption]' openai
WORKDIR /opt/hermes
# ---------- Matrix bridge + extra pip deps ----------
@@ -84,10 +59,6 @@ os.remove(tgz)
print('himalaya v1.2.0 installed')
PYEOF
# ---------- Install multi-gateway launcher ----------
# Launches one gateway process per profile (HERMES_PROFILES env var)
COPY --chmod=0755 run-multi-gateways.sh /usr/local/bin/run-multi-gateways.sh
# ---------- Runtime ----------
USER hermes
ENV HERMES_HOME=/opt/data
@@ -100,4 +71,9 @@ ENV CHROME_EXECUTABLE=/opt/hermes/.playwright/chromium/chrome-linux/chrome
USER root
RUN chown -R hermes:hermes /opt/hermes/tools /opt/hermes/toolsets.py
VOLUME [ "/opt/data" ]
VOLUME [ "/opt/data" ]
# Switch to the hermes user so the container runs unprivileged.
# All child processes inherit this user — no more orphan root processes
# that escape gosu privilege drops in the entrypoint.
USER hermes

View File

@@ -1,181 +0,0 @@
#!/usr/bin/env python3
"""Patch Hermes TTS tool: add Piper TTS provider, remove Edge TTS as default.
Patches ALL copies of tts_tool.py found (venv site-packages + /opt/hermes/tools/).
Searches multiple paths for tts_tool.py so it works both at build time
(in the image venv) and at runtime (on the mounted data volume).
Idempotent: if already patched, does nothing.
"""
import sys
import os
# ---------------------------------------------------------------------------
# Search for all copies of tts_tool.py
# ---------------------------------------------------------------------------
CANDIDATE_PATHS = [
"/opt/hermes/.venv/lib/python3.13/site-packages/tools/tts_tool.py",
"/opt/hermes/tools/tts_tool.py",
]
found_paths = []
for p in CANDIDATE_PATHS:
if os.path.exists(p):
found_paths.append(p)
print(f"Found tts_tool.py at: {p}")
# Also try to find via Python import
import subprocess
try:
result = subprocess.run(
[sys.executable, "-c", "import tools.tts_tool; print(tools.tts_tool.__file__)"],
capture_output=True, text=True, timeout=5
)
if result.returncode == 0:
p = result.stdout.strip()
if os.path.exists(p) and p not in found_paths:
found_paths.append(p)
print(f"Found tts_tool.py via import at: {p}")
except Exception:
pass
if not found_paths:
print("WARNING: tts_tool.py not found anywhere. Patching deferred to runtime.")
print(f"Searched: {CANDIDATE_PATHS}")
sys.exit(0)
# ---------------------------------------------------------------------------
# Old else block: the Edge TTS default fallback to replace
# ---------------------------------------------------------------------------
old_else = ''' else:
# Default: Edge TTS (free), with NeuTTS as local fallback
edge_available = True
try:
_import_edge_tts()
except ImportError:
edge_available = False
if edge_available:
logger.info("Generating speech with Edge TTS...")
try:
import concurrent.futures
with concurrent.futures.ThreadPoolExecutor(max_workers=1) as pool:
pool.submit(
lambda: asyncio.run(_generate_edge_tts(text, file_str, tts_config))
).result(timeout=60)
except RuntimeError:
asyncio.run(_generate_edge_tts(text, file_str, tts_config))
elif _check_neutts_available():
logger.info("Edge TTS not available, falling back to NeuTTS (local)...")
provider = "neutts"
_generate_neutts(text, file_str, tts_config)
else:
return json.dumps({
"success": False,
"error": "No TTS provider available. Install edge-tts (pip install edge-tts) "
"or set up NeuTTS for local synthesis."
}, ensure_ascii=False)'''
# ---------------------------------------------------------------------------
# New block: elif provider == "piper" + else: fallback with Piper only
# ---------------------------------------------------------------------------
new_block = ''' elif provider == "piper":
# Piper TTS (local, CPU, no cloud, no Microsoft)
piper_binary = "/opt/hermes/.venv/bin/piper"
piper_config = tts_config.get("piper", {})
voice = piper_config.get("voice", "en_US-lessac-medium")
model_dir = piper_config.get("model_dir", "/opt/hermes/.venv/share/piper/voices")
model_path = os.path.join(model_dir, f"{voice}.onnx")
if not os.path.exists(model_path):
return json.dumps({
"success": False,
"error": "Piper TTS voice model not found. "
"Install Piper TTS and download a voice model."
}, ensure_ascii=False)
logger.info("Generating speech with Piper TTS (local, CPU)...")
import subprocess as _sp
cmd = [piper_binary, "--model", model_path, "--output-raw"]
try:
proc = _sp.Popen(cmd, stdin=_sp.PIPE, stdout=_sp.PIPE, stderr=_sp.PIPE)
raw_audio, stderr = proc.communicate(input=text.encode(), timeout=60)
if proc.returncode != 0:
raise RuntimeError(f"Piper TTS failed: {stderr.decode()[:200]}")
ffmpeg_cmd = ["ffmpeg", "-f", "s16le", "-ar", "22050", "-ac", "1", "-i", "-", "-y", file_str]
_sp.run(ffmpeg_cmd, input=raw_audio, capture_output=True, timeout=30)
except Exception as e:
return json.dumps({
"success": False,
"error": f"Piper TTS failed: {e}"
}, ensure_ascii=False)
else:
# Default: Piper TTS (local, CPU, no cloud, no Microsoft)
piper_binary = "/opt/hermes/.venv/bin/piper"
piper_config = tts_config.get("piper", {})
voice = piper_config.get("voice", "en_US-lessac-medium")
model_dir = piper_config.get("model_dir", "/opt/hermes/.venv/share/piper/voices")
model_path = os.path.join(model_dir, f"{voice}.onnx")
if os.path.exists(model_path) and os.path.exists(piper_binary):
logger.info("Generating speech with Piper TTS (local, CPU)...")
import subprocess as _sp
cmd = [piper_binary, "--model", model_path, "--output-raw"]
try:
proc = _sp.Popen(cmd, stdin=_sp.PIPE, stdout=_sp.PIPE, stderr=_sp.PIPE)
raw_audio, stderr = proc.communicate(input=text.encode(), timeout=60)
if proc.returncode != 0:
raise RuntimeError(stderr.decode()[:200])
ffmpeg_cmd = ["ffmpeg", "-f", "s16le", "-ar", "22050", "-ac", "1", "-i", "-", "-y", file_str]
_sp.run(ffmpeg_cmd, input=raw_audio, capture_output=True, timeout=30)
except Exception:
pass
else:
return json.dumps({
"success": False,
"error": "Piper TTS not available. Install piper-tts and download a voice model."
}, ensure_ascii=False)'''
# ---------------------------------------------------------------------------
# Apply the patch to all copies found
# ---------------------------------------------------------------------------
patched_any = False
for tts_path in found_paths:
with open(tts_path) as f:
code = f.read()
if 'provider == "piper"' in code:
print(f"ALREADY PATCHED: {tts_path}")
continue
if old_else in code:
code = code.replace(old_else, new_block, 1)
with open(tts_path, 'w') as f:
f.write(code)
print(f"PATCHED: {tts_path}")
patched_any = True
else:
print(f"SKIP {tts_path}: Edge fallback pattern not found")
import re
for m in re.finditer(r' else:\n # Default:', code):
start = max(0, m.start() - 100)
end = min(len(code), m.end() + 300)
print(f" Found 'else:/# Default:' at position {m.start()}:")
print(f" {code[start:end]}")
print(" ---")
# Don't exit with error — if one copy isn't patchable, try the others
if not patched_any:
all_patched = all(
'provider == "piper"' in open(p).read()
for p in found_paths
)
if all_patched:
print("All copies already patched.")
sys.exit(0)
print("WARNING: Could not patch any copy of tts_tool.py")
sys.exit(1)
print("tts_tool.py patched successfully across all copies.")

View File

@@ -1,32 +0,0 @@
#!/bin/bash
# Multi-gateway launcher for HERMES_PROFILES env var.
# Reads comma-separated profile names, spawns one gateway per profile.
# Designed to run before the main entrypoint — gateways run in background.
set -e
if [ -z "${HERMES_PROFILES}" ]; then
echo "HERMES_PROFILES not set — skipping multi-gateway launch"
exit 0
fi
# Source venv to make 'hermes' available (entrypoint.sh sources it later,
# but we need it NOW for the background gateways)
HERMES_BIN="/opt/hermes/.venv/bin/hermes"
if [ ! -x "$HERMES_BIN" ]; then
echo "ERROR: hermes binary not found at $HERMES_BIN"
exit 1
fi
mkdir -p /opt/data/logs
IFS=',' read -ra PROFILES <<< "${HERMES_PROFILES}"
for profile in "${PROFILES[@]}"; do
profile="$(echo "${profile}" | xargs)" # trim whitespace
[ -z "${profile}" ] && continue
echo "Starting gateway for profile: ${profile}"
nohup env API_SERVER_ENABLED=false API_SERVER_KEY= gosu hermes "$HERMES_BIN" --profile "${profile}" gateway run \
>> "/opt/data/logs/gateway-${profile}.log" 2>&1 &
done
echo "All gateways launched: ${HERMES_PROFILES}"

View File

@@ -28,9 +28,10 @@ ENV PATH=$PNPM_HOME:$PATH
RUN corepack enable && corepack prepare pnpm@latest --activate
WORKDIR /app
RUN apt-get update && apt-get install -y git && rm -rf /var/lib/apt/lists/*
RUN apt-get update && apt-get install -y git openssh-client && rm -rf /var/lib/apt/lists/**
ARG OPENCONCHO_SHA=3b5c3293fc18d768dbe85285264a8d66c896bd81
ARG OPENCONCHO_SHA=148766312e6a5256f399c64171854f0ff6173b6d
RUN mkdir -p -m 0700 ~/.ssh && ssh-keyscan -p 2222 code.lazyworkhorse.net >> ~/.ssh/known_hosts 2>/dev/null
RUN --mount=type=ssh git clone --depth 1 ssh://git@code.lazyworkhorse.net:2222/gortium/openconcho.git /app && \
git -C /app fetch --depth 1 origin ${OPENCONCHO_SHA} && \
git -C /app checkout ${OPENCONCHO_SHA}

32
ai/llama-cpp/Dockerfile Normal file
View File

@@ -0,0 +1,32 @@
# llama-cpp-rocm6/Dockerfile
# Custom llama.cpp server with ROCm 6.1 + gfx906 (MI50) support.
# Build: docker build -t llama-cpp:rocm-gfx906 .
# NOTE: Update LLAMACPP_VERSION for bugfixes, but verify ROCm 6.1 compat
# by checking if recent releases still support -DGGML_HIP=ON.
FROM rocm/dev-ubuntu-22.04:6.1.2-complete AS builder
RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y curl git build-essential pkg-config cmake make && rm -rf /var/lib/apt/lists/*
ARG LLAMACPP_VERSION=b9890
RUN git clone --depth 1 --branch ${LLAMACPP_VERSION} https://github.com/ggml-org/llama.cpp.git /build
WORKDIR /build
ENV HIP_PATH=/opt/rocm ROCM_PATH=/opt/rocm PATH=/opt/rocm/bin:/opt/rocm/llvm/bin:${PATH} CMAKE_PREFIX_PATH=/opt/rocm
RUN mkdir build && cd build && \
cmake .. -DGGML_HIP=ON -DCMAKE_BUILD_TYPE=Release \
-DAMDGPU_TARGETS="gfx906:xnack-" \
-DCMAKE_POSITION_INDEPENDENT_CODE=ON \
-DGGML_CUDA=OFF -DGGML_VULKAN=OFF -DGGML_METAL=OFF \
-DBUILD_SHARED_LIBS=OFF && \
cmake --build . --target llama-server -- -j $(nproc)
FROM ubuntu:24.04
RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y \
ca-certificates curl libstdc++6 libgomp1 libopenblas0 \
libnuma1 libelf1 libdrm2 libdrm-amdgpu1 \
&& rm -rf /var/lib/apt/lists/*
COPY --from=builder /opt/rocm/lib/ /opt/rocm/lib/
COPY --from=builder /opt/rocm/share/ /opt/rocm/share/
COPY --from=builder /build/build/bin/llama-server /usr/local/bin/llama-server
RUN echo /opt/rocm/lib > /etc/ld.so.conf.d/rocm.conf && ldconfig
ENV HSA_OVERRIDE_GFX_VERSION=9.0.6 HCC_AMDGPU_TARGET=gfx906 HSA_ENABLE_SDMA=0
EXPOSE 8080
ENTRYPOINT ["/usr/local/bin/llama-server"]

View File

@@ -32,5 +32,5 @@ services:
networks:
auth_net:
external: true
driver: bridge
name: auth_net

View File

@@ -68,33 +68,33 @@ services:
labels:
- "traefik.enable=false" # Internal only, accessed by restic-browser
restic-browser:
image: embergarage/restic-browser:latest
container_name: restic-browser
restart: always
environment:
- TZ=America/Montreal
- RESTIC_REPOSITORY=http://restic-server:8080
- RESTIC_PASSWORD=${RESTIC_PASSWORD}
networks:
- backup_net
labels:
- "traefik.enable=true"
# 1. HTTP to HTTPS Redirect
- "traefik.http.routers.restic-browser-http.rule=Host(`backup.lazyworkhorse.net`)"
- "traefik.http.routers.restic-browser-http.entrypoints=web"
- "traefik.http.routers.restic-browser-http.middlewares=redirect-to-https@docker"
# 2. HTTPS Configuration
- "traefik.http.routers.restic-browser.rule=Host(`backup.lazyworkhorse.net`)"
- "traefik.http.routers.restic-browser.entrypoints=websecure"
- "traefik.http.routers.restic-browser.tls=true"
- "traefik.http.routers.restic-browser.tls.certresolver=njalla"
# 3. Backend Service Config
- "traefik.http.services.restic-browser.loadbalancer.server.port=8000"
# restic-browser:
# image: mazzolino/restic-browser:latest
# container_name: restic-browser
# restart: always
# environment:
# - TZ=America/Montreal
# - RESTIC_REPOSITORY=http://restic-server:8080
# - RESTIC_PASSWORD=${RESTIC_PASSWORD}
# networks:
# - backup_net
# labels:
# - "traefik.enable=true"
# # 1. HTTP to HTTPS Redirect
# - "traefik.http.routers.restic-browser-http.rule=Host(`backup.lazyworkhorse.net`)"
# - "traefik.http.routers.restic-browser-http.entrypoints=web"
# - "traefik.http.routers.restic-browser-http.middlewares=redirect-to-https@docker"
#
# # 2. HTTPS Configuration
# - "traefik.http.routers.restic-browser.rule=Host(`backup.lazyworkhorse.net`)"
# - "traefik.http.routers.restic-browser.entrypoints=websecure"
# - "traefik.http.routers.restic-browser.tls=true"
# - "traefik.http.routers.restic-browser.tls.certresolver=njalla"
#
# # 3. Backend Service Config
# - "traefik.http.services.restic-browser.loadbalancer.server.port=8000"
networks:
backup_net:
external: true
driver: bridge
name: backup_net

View File

@@ -6,6 +6,7 @@ services:
restart: always
networks:
- cloud_net
- cloud_internal
environment:
- PUID=1000
- PGID=1000
@@ -51,7 +52,7 @@ services:
container_name: nextcloud_cron
restart: always
networks:
- cloud_net
- cloud_internal
entrypoint: /cron.sh
volumes:
- /mnt/HoardingCow_docker_data/NextCloud/data:/var/www/html:rw
@@ -75,7 +76,7 @@ services:
networks:
cloud_net:
external: true
driver: bridge
name: cloud_net
cloud_internal:
driver: bridge

View File

@@ -103,7 +103,7 @@ services:
networks:
coms_net:
external: true
driver: bridge
name: coms_net
coms_backend:
driver: bridge

View File

@@ -37,4 +37,5 @@ services:
networks:
finance_net:
external: true
driver: bridge
name: finance_net

View File

@@ -12,8 +12,8 @@ services:
volumes:
- /mnt/HoardingCow_docker_data/Home_Assistant:/config:rw
networks:
- home_auto_net
- home_auto_backend
- home_net
- home_backend
labels:
- "traefik.enable=true"
@@ -34,7 +34,7 @@ services:
volumes:
- /mnt/HoardingCow_docker_data/Mosquitto:/mosquitto
networks:
- home_auto_backend
- home_backend
# ports:
# - 1883:1883
# - 9001:9001
@@ -43,7 +43,7 @@ services:
image: registry.gitlab.com/hydroqc/hydroqc2mqtt:1.3.0
restart: always
networks:
- home_auto_backend
- home_backend
environment:
MQTT_USERNAME: hass
MQTT_PASSWORD: ${MQTT_PASSWORD}
@@ -88,8 +88,9 @@ services:
# restart: unless-stopped
networks:
home_auto_net:
external: true
home_auto_backend:
home_net:
driver: bridge
name: home_auto_backend
name: home_net
home_backend:
driver: bridge
name: home_backend

View File

@@ -38,4 +38,5 @@ services:
networks:
homepage_net:
external: true
driver: bridge
name: homepage_net

View File

@@ -36,9 +36,9 @@ services:
- cloud_net
- coms_net
- finance_net
- home_auto_net
- home_net
- homepage_net
- passman_net
- pass_net
- tak_net
- vc_net
@@ -99,15 +99,15 @@ networks:
finance_net:
driver: bridge
name: finance_net
home_auto_net:
home_net:
driver: bridge
name: home_auto_net
name: home_net
homepage_net:
driver: bridge
name: homepage_net
passman_net:
pass_net:
driver: bridge
name: passman_net
name: pass_net
tak_net:
driver: bridge
name: tak_net

View File

@@ -13,7 +13,7 @@ services:
volumes:
- /mnt/HoardingCow_docker_data/BitWarden/data:/data:rw
networks:
- passman_net
- pass_net
restart: always
labels:
- "traefik.enable=true"
@@ -32,5 +32,6 @@ services:
# Internal service
- "traefik.http.services.pass.loadbalancer.server.port=80"
networks:
passman_net:
external: true
pass_net:
driver: bridge
name: pass_net

View File

@@ -92,7 +92,8 @@ services:
networks:
tak_net:
external: true
driver: bridge
name: tak_net
tak_backend:
driver: bridge
name: tak_backend

View File

@@ -61,4 +61,5 @@ services:
networks:
vc_net:
external: true
driver: bridge
name: vc_net

View File

@@ -12,7 +12,7 @@ services:
- SYS_MODULE
environment:
- WG_HOST=vpn.lazyworkhorse.net
- PASSWORD=${WG_PASSWORD}
- PASSWORD_HASH=${WG_PASSWORD_HASH}
- WG_PORT=51820
- WG_DEFAULT_ADDRESS=10.8.0.x
- WG_DEFAULT_DNS=1.1.1.1,8.8.8.8
@@ -22,7 +22,6 @@ services:
- UI_CHART_TYPE=0
ports:
- "51820:51820/udp"
- "51821:51821/tcp"
volumes:
- /mnt/HoardingCow_docker_data/WireGuard:/etc/wireguard:rw
sysctls:
@@ -31,8 +30,31 @@ services:
restart: unless-stopped
networks:
- vpn_net
labels:
- "traefik.enable=true"
# HTTP → HTTPS redirect
- "traefik.http.routers.wireguard-http.rule=Host(vpn.lazyworkhorse.net)"
- "traefik.http.routers.wireguard-http.entrypoints=web"
- "traefik.http.routers.wireguard-http.middlewares=redirect-to-https"
- "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"
# HTTPS router — protégé par Authelia
- "traefik.http.routers.wireguard-https.rule=Host(vpn.lazyworkhorse.net)"
- "traefik.http.routers.wireguard-https.entrypoints=websecure"
- "traefik.http.routers.wireguard-https.tls=true"
- "traefik.http.routers.wireguard-https.tls.certresolver=njalla"
- "traefik.http.routers.wireguard-https.middlewares=wireguard-auth"
# Authelia forwardAuth
- "traefik.http.middlewares.wireguard-auth.forwardauth.address=http://authelia:9091/api/verify?rd=https://auth.lazyworkhorse.net/"
- "traefik.http.middlewares.wireguard-auth.forwardauth.trustforwardheader=true"
- "traefik.http.middlewares.wireguard-auth.forwardauth.authresponseheaders=X-Forwarded-User,X-Forwarded-Groups"
# Port interne du web UI wg-easy
- "traefik.http.services.wireguard.loadbalancer.server.port=51821"
networks:
vpn_net:
external: true
driver: bridge
name: vpn_net