Files
6krrt/deploy/llm-router.service
adlee-was-taken 65dd3138ed fix(deploy): redirect HF_HOME into the repo, same crash on a fresh install
The live incident this branch is already fixing (#48's gated model
default) had a second layer once that was fixed: HuggingFace's default
cache (~/.cache/huggingface) falls outside ProtectHome=read-only's
ReadWritePaths exception, so the service crash-looped with
"OSError: Read-only file system" the moment local_encoder tried to
download a model -- even with a working model id.

Redirect HF_HOME into the already-writable repo path instead of
widening the sandbox to a new home-directory location, keeping the
"only the repo is writable" invariant intact. Applied live already;
this backports it to the tracked deploy template so a fresh install
doesn't hit the same crash.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VRQXz5SYZYVWscxS1QqF6U
2026-09-06 21:26:49 -04:00

45 lines
1.9 KiB
Desktop File

[Unit]
Description=Local LLM model router (FastAPI dispatcher)
Documentation=file:%h/llm-router/CLAUDE.md
# The classifier talks to Ollama on localhost and the dispatcher talks to
# NeuralWatt, so both loopback and real network need to be up. Ollama is a
# system service and cannot be ordered against from a user unit, so a failed
# classifier call is handled by Restart= below rather than by ordering.
After=network-online.target
Wants=network-online.target
[Service]
Type=exec
# router.db and router.log are referenced as relative paths, so this has to be
# the repo root. config.yaml now lives under config/ and the Python modules
# under src/, so PYTHONPATH points at src/ for the dispatcher import to resolve.
WorkingDirectory=%h/llm-router
# Resolves `dispatcher` (and any other src/ module) for the ExecStart below.
Environment=PYTHONPATH=%h/llm-router/src
# Holds NEURALWATT_API_KEY. Create it with:
# echo "NEURALWATT_API_KEY=$NEURALWATT_API_KEY" > .env && chmod 600 .env
EnvironmentFile=%h/llm-router/.env
# HuggingFace's default cache (~/.cache/huggingface) falls outside
# ReadWritePaths below; redirect it into the repo instead of widening the
# sandbox to a new home-directory path. Only classifier.mode: local_encoder
# ever touches this. Caught live 2026-09-06: without this, the service
# crash-loops with "OSError: Read-only file system" the moment
# local_encoder needs to download a model.
Environment=HF_HOME=%h/llm-router/.hf-cache
ExecStart=%h/llm-router/.venv/bin/uvicorn dispatcher:app --host 127.0.0.1 --port 8080 --timeout-graceful-shutdown 5
Restart=always
RestartSec=5s
# Loopback only by default — this service holds a billable API key and has no
# auth of its own. Anything that widens --host should add auth first.
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=read-only
# ...except the repo, which needs to be writable for router.db and router.log.
ReadWritePaths=%h/llm-router
[Install]
WantedBy=default.target