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
45 lines
1.9 KiB
Desktop File
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
|