{"repo": "chrisjohnson/local-ai-machine", "files": {"docker/docker-compose.yml": {"b7fed2f9d4f62120273d99f6aefb5f3da361253d": {"t": 1786068610.9841177, "n": 69283, "h": "d30b534cebbb5fb4", "c": "version: '3.8'\n\n# Two-tier design (M-001): every verified model+engine build is its own compose\n# service with a fixed port. LiteLLM aliases role names (coder, judge) to\n# whichever localhost: is the current pick — switching roles is a LiteLLM\n# config edit + reload, not a compose change.\n#\n# Port allocation (per-engine-family, sequential within family):\n# vLLM builds: 8000-8099\n# llama.cpp-server: 8100-8199\n# Ollama: 11434 (single shared instance, model switch via API)\n# Infrastructure: 4000 (LiteLLM), 9090 (Prometheus),\n# 3000 (Grafana), 3001 (Open WebUI)\n#\n# All model-serving ports bind to 127.0.0.1 only. LiteLLM uses\n# network_mode: host to reach them directly. See vllm-primary comment\n# block (now removed, preserved in git history) for the NixOS firewall\n# reasoning. Reach via SSH tunnel or LiteLLM on 4000.\n#\n# Only start the service you want — everything else stays defined-but-stopped.\n# GPU contention means at most one vLLM + one llama-server can co-reside;\n# Ollama is always safe alongside either (small GPU footprint).\n#\n# Always-up vs. exclusive/benchmarkable (see catalog/OPERATIONS.md for full\n# rationale): infra services below carry\n# labels:\n# com.local-ai-machine.always-up: \"true\"\n# scripts/benchmark_orchestrator.py stops every service WITHOUT that label\n# once for the whole sweep (not per-build — that caused a real full-system\n# hang, 2026-07-29), then restores exactly whichever ones were running\n# beforehand at the very end. New model-serving builds need no label at\n# all — they're exclusive/stoppable by default; only genuine infra should\n# ever carry always-up: \"true\".\n#\n# Deliberately no `restart: unless-stopped` on model-serving services\n# (2026-07-29): a hard reboot caught mid-sweep brought every model\n# container back up simultaneously (~111GB/124GB used), since none of\n# them were in an explicit \"stopped\" state at the moment of the crash.\n# Infra services below keep the restart policy — no GPU-contention risk\n# there. Which model should auto-start on boot (if any) is an open\n# question, deferred until the sweep/exclusivity design is more settled.\n#\n# --gpu-memory-utilization: judge-sized models (roughly <25B — the ones\n# almost always run co-resident alongside a larger candidate model, e.g.\n# qwen3.5-4b-judge at 0.20, qwen2.5-vl-7b-instruct at 0.20 for the M-024\n# LLM-judge role) should carry an explicit low cap so they don't starve\n# whatever they're running alongside — vLLM's default (0.90) assumes it\n# owns the whole GPU, which is only true for a standalone candidate. Larger\n# models that might reasonably serve standalone (no separate judge needed)\n# intentionally don't get a cap here — see M-025 for the followup pass\n# systematically reviewing which services need one, including borderline\n# MoE cases where total params exceed the ~25B judge threshold but active\n# params don't.\n\nservices:\n\n # ---------------------------------------------------------------------------\n # vLLM builds (kyuz0/vllm-therock-gfx1151) — ports 8000-8012\n # ---------------------------------------------------------------------------\n # Common vLLM config (from catalog/engines/vllm-therock-gfx1151-v1.yaml):\n # image: kyuz0/vllm-therock-gfx1151:latest\n # devices: /dev/kfd, /dev/dri\n # group_add: video, render (named groups — image has them)\n # security_opt: seccomp:unconfined\n # ipc: host\n # volumes: /var/lib/ai-models:/models\n # common_flags: --host 0.0.0.0, --trust-remote-code, --enable-prefix-caching,\n # --dtype auto, --tensor-parallel-size 1\n # Each service below adds build-specific flags from its catalog entry.\n # Container always listens on :8000 internally; host port varies.\n\n qwen3.6-35b-a3b--vllm-therock-gfx1151-v1:\n image: docker.io/kyuz0/vllm-therock-gfx1151:latest\n container_name: qwen3.6-35b-a3b--vllm-therock-gfx1151-v1\n devices:\n - /dev/kfd:/dev/kfd\n - /dev/dri:/dev/dri\n group_add:\n - video\n - render\n security_opt:\n - seccomp:unconfined\n ipc: host\n volumes:\n - /var/lib/ai-models:/models\n - vllm-qwen3.6-35b-a3b-cache:/root/.cache/vllm\n command: >\n vllm serve /models/qwen3.6-35b-a3b\n --served-model-name qwen3.6-35b-a3b\n --host 0.0.0.0\n --port 8000\n --tensor-parallel-size 1\n --gpu-memory-utilization 0.70\n --dtype auto\n --trust-remote-code\n --max-num-seqs 64\n --enable-prefix-caching\n --max-model-len 131072\n --enable-auto-tool-choice\n --tool-call-parser qwen3_coder\n --reasoning-parser qwen3\n ports:\n - \"127.0.0.1:8000:8000\"\n\n qwen3.5-4b--vllm-therock-gfx1151-v1:\n image: docker.io/kyuz0/vllm-therock-gfx1151:latest\n container_name: qwen3.5-4b--vllm-therock-gfx1151-v1\n devices:\n - /dev/kfd:/dev/kfd\n - /dev/dri:/dev/dri\n group_add:\n - video\n - render\n security_opt:\n - seccomp:unconfined\n ipc: host\n volumes:\n - /var/lib/ai-models:/models\n - vllm-qwen3.5-4b-cache:/root/.cache/vllm\n command: >\n vllm serve /models/qwen3.5-4b\n --served-model-name qwen3.5-4b-judge\n --host 0.0.0.0\n --port 8000\n --tensor-parallel-size 1\n --gpu-memory-utilization 0.20\n --dtype auto\n --trust-remote-code\n --max-num-seqs 64\n --enable-prefix-caching\n --max-model-len 131072\n --enable-auto-tool-choice\n --tool-call-parser qwen3_coder\n --reasoning-parser qwen3\n ports:\n - \"127.0.0.1:8001:8000\"\n\n qwen3.6-27b--vllm-therock-gfx1151-v1:\n image: docker.io/kyuz0/vllm-therock-gfx1151:latest\n container_name: qwen3.6-27b--vllm-therock-gfx1151-v1\n devices:\n - /dev/kfd:/dev/kfd\n - /dev/dri:/dev/dri\n group_add:\n - video\n - render\n security_opt:\n - seccomp:unconfined\n ipc: host\n volumes:\n - /var/lib/ai-models:/models\n - vllm-qwen3.6-27b-cache:/root/.cache/vllm\n command: >\n vllm serve /models/qwen3.6-27b\n --served-model-name qwen3.6-27b\n --host 0.0.0.0\n --port 8000\n --tensor-parallel-size 1\n --gpu-memory-utilization 0.90\n --dtype auto\n --trust-remote-code\n --enable-prefix-caching\n --max-model-len 131072\n --enable-auto-tool-choice\n --tool-call-parser qwen3_coder\n --reasoning-parser qwen3\n ports:\n - \"127.0.0.1:8002:8000\"\n\n glm-4.7-flash-awq--vllm-therock-gfx1151-v1:\n image: docker.io/kyuz0/vllm-therock-gfx1151:latest\n container_name: glm-4.7-flash-awq--vllm-therock-gfx1151-v1\n devices:\n - /dev/kfd:/dev/kfd\n - /dev/dri:/dev/dri\n group_add:\n - video\n - render\n security_opt:\n - seccomp:unconfined\n ipc: host\n volumes:\n - /var/lib/ai-models:/models\n - vllm-glm-4.7-flash-awq-cache:/root/.cache/vllm\n command: >\n vllm serve /models/glm-4.7-flash-awq\n --served-model-name glm-4.7-flash-awq\n --host 0.0.0.0\n --port 8000\n --tensor-parallel-size 1\n --dtype auto\n --trust-remote-code\n --enable-prefix-caching\n --enable-auto-tool-choice\n --tool-call-parser glm47\n --reasoning-parser glm45\n --max-model-len 65536\n ports:\n - \"127.0.0.1:8003:8000\"\n\n # Promoted from swap-in comparison tier to the standing `coder` role\n # (2026-07-31, Chris's direction) — replaces qwen3.6-35b-a3b-mtp\n # (llamacpp-vulkan-radv-mtp, port 8102, now stopped). Its catalog-benchmark\n # config used --gpu-memory-utilization 0.70 (~87GB) as the sole GPU\n # occupant (see catalog/builds/qwen3-coder-next-gptq4bit--...yaml, \"not a\n # standing compose service, swap-in only\") — that doesn't fit alongside\n # the other two standing roles (judge: glm-4.7-flash ~18GB, vision:\n # qwen2.5-vl-7b-instruct ~22.8GB confirmed via rocm-smi). Trimmed to 0.60\n # (~74GB) so the three standing roles' combined reservation (~114-115GB)\n # stays under the box's ~124GB unified GTT pool with a safety margin.\n qwen3-coder-next-gptq4bit--vllm-therock-gfx1151-v1:\n image: docker.io/kyuz0/vllm-therock-gfx1151:latest\n container_name: qwen3-coder-next-gptq4bit--vllm-therock-gfx1151-v1\n devices:\n - /dev/kfd:/dev/kfd\n - /dev/dri:/dev/dri\n group_add:\n - video\n - render\n security_opt:\n - seccomp:unconfined\n ipc: host\n volumes:\n - /var/lib/ai-models:/models\n - vllm-qwen3-coder-next-gptq4bit-cache:/root/.cache/vllm\n command: >\n vllm serve /models/qwen3-coder-next-gptq4bit\n --served-model-name qwen3-coder-next-gptq4bit\n --host 0.0.0.0\n --port 8000\n --tensor-parallel-size 1\n --gpu-memory-utilization 0.60\n --dtype auto\n --trust-remote-code\n --enable-prefix-caching\n --max-model-len 131072\n --enable-auto-tool-choice\n --tool-call-parser qwen3_coder\n ports:\n - \"127.0.0.1:8004:8000\"\n\n gpt-oss-20b--vllm-therock-gfx1151-v1:\n image: docker.io/kyuz0/vllm-therock-gfx1151:latest\n container_name: gpt-oss-20b--vllm-therock-gfx1151-v1\n devices:\n - /dev/kfd:/dev/kfd\n - /dev/dri:/dev/dri\n group_add:\n - video\n - render\n security_opt:\n - seccomp:unconfined\n ipc: host\n volumes:\n - /var/lib/ai-models:/models\n - vllm-gpt-oss-20b-cache:/root/.cache/vllm\n command: >\n vllm serve /models/gpt-oss-20b\n --served-model-name gpt-oss-20b\n --host 0.0.0.0\n --port 8000\n --tensor-parallel-size 1\n --gpu-memory-utilization 0.90\n --dtype auto\n --trust-remote-code\n --enable-prefix-caching\n --max-model-len 131072\n --enable-auto-tool-choice\n --tool-call-parser openai\n --reasoning-parser openai_gptoss\n ports:\n - \"127.0.0.1:8005:8000\"\n\n gpt-oss-120b--vllm-therock-gfx1151-v1:\n image: docker.io/kyuz0/vllm-therock-gfx1151:latest\n container_name: gpt-oss-120b--vllm-therock-gfx1151-v1\n devices:\n - /dev/kfd:/dev/kfd\n - /dev/dri:/dev/dri\n group_add:\n - video\n - render\n security_opt:\n - seccomp:unconfined\n ipc: host\n volumes:\n - /var/lib/ai-models:/models\n - vllm-gpt-oss-120b-cache:/root/.cache/vllm\n command: >\n vllm serve /models/gpt-oss-120b\n --served-model-name gpt-oss-120b\n --host 0.0.0.0\n --port 8000\n --tensor-parallel-size 1\n --gpu-memory-utilization 0.90\n --dtype auto\n --trust-remote-code\n --enable-prefix-caching\n --max-model-len 131072\n --enable-auto-tool-choice\n --tool-call-parser openai\n --reasoning-parser openai_gptoss\n ports:\n - \"127.0.0.1:8006:8000\"\n\n qwen3.5-122b-a10b-awq4bit--vllm-therock-gfx1151-v1:\n image: docker.io/kyuz0/vllm-therock-gfx1151:latest\n container_name: qwen3.5-122b-a10b-awq4bit--vllm-therock-gfx1151-v1\n devices:\n - /dev/kfd:/dev/kfd\n - /dev/dri:/dev/dri\n group_add:\n - video\n - render\n security_opt:\n - seccomp:unconfined\n ipc: host\n volumes:\n - /var/lib/ai-models:/models\n - vllm-qwen3.5-122b-a10b-awq4bit-cache:/root/.cache/vllm\n environment:\n - VLLM_USE_TRITON_AWQ=1\n command: >\n vllm serve /models/qwen3.5-122b-a10b-awq4bit\n --served-model-name qwen3.5-122b-a10b-awq4bit\n --host 0.0.0.0\n --port 8000\n --tensor-parallel-size 1\n --dtype auto\n --trust-remote-code\n --enable-prefix-caching\n --enable-auto-tool-choice\n --tool-call-parser qwen3_coder\n --reasoning-parser qwen3\n --max-model-len 65536\n ports:\n - \"127.0.0.1:8007:8000\"\n\n gemma-4-26b-a4b-it--vllm-therock-gfx1151-v1:\n image: docker.io/kyuz0/vllm-therock-gfx1151:latest\n container_name: gemma-4-26b-a4b-it--vllm-therock-gfx1151-v1\n devices:\n - /dev/kfd:/dev/kfd\n - /dev/dri:/dev/dri\n group_add:\n - video\n - render\n security_opt:\n - seccomp:unconfined\n ipc: host\n volumes:\n - /var/lib/ai-models:/models\n - vllm-gemma-4-26b-a4b-it-cache:/root/.cache/vllm\n command: >\n vllm serve /models/gemma-4-26b-a4b-it\n --served-model-name gemma-4-26b-a4b-it\n --host 0.0.0.0\n --port 8000\n --tensor-parallel-size 1\n --gpu-memory-utilization 0.90\n --dtype auto\n --trust-remote-code\n --enable-prefix-caching\n --max-model-len 65536\n --enable-auto-tool-choice\n --tool-call-parser gemma4\n --reasoning-parser gemma4\n ports:\n - \"127.0.0.1:8008:8000\"\n\n gemma-4-31b-it--vllm-therock-gfx1151-v1:\n image: docker.io/kyuz0/vllm-therock-gfx1151:latest\n container_name: gemma-4-31b-it--vllm-therock-gfx1151-v1\n devices:\n - /dev/kfd:/dev/kfd\n - /dev/dri:/dev/dri\n group_add:\n - video\n - render\n security_opt:\n - seccomp:unconfined\n ipc: host\n volumes:\n - /var/lib/ai-models:/models\n - vllm-gemma-4-31b-it-cache:/root/.cache/vllm\n command: >\n vllm serve /models/gemma-4-31b-it\n --served-model-name gemma-4-31b-it\n --host 0.0.0.0\n --port 8000\n --tensor-parallel-size 1\n --gpu-memory-utilization 0.90\n --dtype auto\n --trust-remote-code\n --enable-prefix-caching\n --max-model-len 65536\n --enable-auto-tool-choice\n --tool-call-parser gemma4\n --reasoning-parser gemma4\n ports:\n - \"127.0.0.1:8009:8000\"\n\n qwen2.5-vl-7b-instruct--vllm-therock-gfx1151-v1:\n image: docker.io/kyuz0/vllm-therock-gfx1151:latest\n container_name: qwen2.5-vl-7b-instruct--vllm-therock-gfx1151-v1\n devices:\n - /dev/kfd:/dev/kfd\n - /dev/dri:/dev/dri\n group_add:\n - video\n - render\n security_opt:\n - seccomp:unconfined\n ipc: host\n volumes:\n - /var/lib/ai-models:/models\n - vllm-qwen2.5-vl-7b-instruct-cache:/root/.cache/vllm\n command: >\n vllm serve /models/qwen2.5-vl-7b-instruct\n --served-model-name qwen2.5-vl-7b-instruct\n --host 0.0.0.0\n --port 8000\n --tensor-parallel-size 1\n --gpu-memory-utilization 0.20\n --dtype auto\n --trust-remote-code\n --enable-prefix-caching\n --max-model-len 32768\n ports:\n - \"127.0.0.1:8010:8000\"\n\n north-mini-code-1.0-w4a16--vllm-therock-gfx1151-v1:\n image: docker.io/kyuz0/vllm-therock-gfx1151:latest\n container_name: north-mini-code-1.0-w4a16--vllm-therock-gfx1151-v1\n devices:\n - /dev/kfd:/dev/kfd\n - /dev/dri:/dev/dri\n group_add:\n - video\n - render\n security_opt:\n - seccomp:unconfined\n ipc: host\n volumes:\n - /var/lib/ai-models:/models\n - vllm-north-mini-code-1.0-w4a16-cache:/root/.cache/vllm\n command: >\n vllm serve /models/north-mini-code-1.0-w4a16\n --served-model-name north-mini-code-1.0\n --host 0.0.0.0\n --port 8000\n --tensor-parallel-size 1\n --gpu-memory-utilization 0.90\n --dtype auto\n --trust-remote-code\n --enable-prefix-caching\n --max-model-len 320000\n --enable-auto-tool-choice\n --tool-call-parser cohere_command4\n ports:\n - \"127.0.0.1:8011:8000\"\n\n # ---------------------------------------------------------------------------\n # llama.cpp-server build — port 8100\n # ---------------------------------------------------------------------------\n # Engine: llamacpp-vulkan-radv-server-v1 (llama-server, OpenAI-compatible\n # HTTP API with continuous batching / parallel slots). Only llamacpp build\n # that can run as a standing service — the other 10 use llama-bench\n # (single-shot benchmarking tool that exits after running).\n qwen3.6-27b--llamacpp-vulkan-radv-server-v1:\n image: docker.io/kyuz0/amd-strix-halo-toolboxes:vulkan-radv\n container_name: qwen3.6-27b--llamacpp-vulkan-radv-server-v1\n devices:\n - /dev/kfd:/dev/kfd\n - /dev/dri:/dev/dri\n group_add:\n - \"26\"\n - \"303\"\n volumes:\n - /var/lib/ai-models/ollama-qwen3.6-27b:/models:ro\n command: >\n llama-server\n -m /models/Qwen3.6-27B-Q4_K_M.gguf\n -ngl 999\n -fa 1\n -lm none\n -cb\n -kvu\n --host 0.0.0.0\n --port 8080\n -np 4\n -c 32768\n --metrics\n ports:\n - \"127.0.0.1:8100:8080\"\n\n laguna-s-2.1-118b-q4km--llamacpp-vulkan-radv-v1:\n image: docker.io/kyuz0/amd-strix-halo-toolboxes:vulkan-radv\n container_name: laguna-s-2.1-118b-q4km--llamacpp-vulkan-radv-v1\n devices:\n - /dev/kfd:/dev/kfd\n - /dev/dri:/dev/dri\n group_add:\n - \"26\"\n - \"303\"\n volumes:\n - /var/lib/ai-models/laguna-s-2.1-118b-q4km:/models:ro\n command: >\n llama-server\n -m /models/UD-Q4_K_M/Laguna-S-2.1-UD-Q4_K_M-00001-of-00003.gguf\n -ngl 999\n -fa 1\n --swa-full\n --reasoning-format deepseek\n -n 8192\n -c 131072\n --host 0.0.0.0\n --port 8080\n -a laguna-s-2.1-118b-q4km\n --metrics\n ports:\n - \"127.0.0.1:8101:8080\"\n\n # v2 of the laguna-s-2.1 plain build: same engine recipe, same model artifact,\n # serving-config-only change — quantized q8_0 KV cache (-ctk/-ctv) to halve the\n # ~24GiB KV footprint at -c 131072, so laguna can co-reside with qwen3.6-35b-a3b-mtp\n # (both running = ~120GiB/124GiB before, OOM-killed qwen 2026-08-02). v1's f16 KV\n # config is preserved untouched (its benchmark_runs[] stay valid). v2 has no\n # benchmark_runs yet — run one before treating any v2 number as comparable.\n laguna-s-2.1-118b-q4km--llamacpp-vulkan-radv-v2:\n image: docker.io/kyuz0/amd-strix-halo-toolboxes:vulkan-radv\n container_name: laguna-s-2.1-118b-q4km--llamacpp-vulkan-radv-v2\n devices:\n - /dev/kfd:/dev/kfd\n - /dev/dri:/dev/dri\n group_add:\n - \"26\"\n - \"303\"\n volumes:\n - /var/lib/ai-models/laguna-s-2.1-118b-q4km:/models:ro\n command: >\n llama-server\n -m /models/UD-Q4_K_M/Laguna-S-2.1-UD-Q4_K_M-00001-of-00003.gguf\n -ngl 999\n -fa 1\n --swa-full\n --reasoning-format deepseek\n -n 8192\n -c 131072\n -ctk q8_0\n -ctv q8_0\n --host 0.0.0.0\n --port 8080\n -a laguna-s-2.1-118b-q4km\n --metrics\n ports:\n - \"127.0.0.1:8108:8080\"\n\n # MTP (multi-token-prediction) speculative-decoding variant of\n # qwen3.6-35b-a3b, using the MTP-tensor-bearing GGUF (a separate download\n # from the plain quant — see configuration.nix). --spec-type draft-mtp\n # requires -np/--parallel 1 (MTP supports only a single server slot).\n # For comparison against the already-benchmarked plain llama.cpp build\n # (qwen3.6-35b-a3b--llamacpp-vulkan-radv, tg128 63.43 tok/s).\n qwen3.6-35b-a3b--llamacpp-vulkan-radv-mtp-v1:\n image: docker.io/kyuz0/amd-strix-halo-toolboxes:vulkan-radv\n container_name: qwen3.6-35b-a3b--llamacpp-vulkan-radv-mtp-v1\n devices:\n - /dev/kfd:/dev/kfd\n - /dev/dri:/dev/dri\n group_add:\n - \"26\"\n - \"303\"\n volumes:\n - /var/lib/ai-models/llamacpp-qwen3.6-35b-a3b-mtp:/models:ro\n command: >\n llama-server\n -m /models/Qwen3.6-35B-A3B-UD-Q4_K_M.gguf\n -ngl 999\n -fa 1\n --spec-type draft-mtp\n --spec-draft-n-max 3\n -np 1\n -c 131072\n -n 8192\n --host 0.0.0.0\n --port 8080\n -a qwen3.6-35b-a3b-mtp\n --metrics\n ports:\n - \"127.0.0.1:8102:8080\"\n\n # Same as v1, but 8bit KV quantization to reduce memory footprint\n qwen3.6-35b-a3b--llamacpp-vulkan-radv-mtp-v2:\n image: docker.io/kyuz0/amd-strix-halo-toolboxes:vulkan-radv\n container_name: qwen3.6-35b-a3b--llamacpp-vulkan-radv-mtp-v2\n devices:\n - /dev/kfd:/dev/kfd\n - /dev/dri:/dev/dri\n group_add:\n - \"26\"\n - \"303\"\n volumes:\n - /var/lib/ai-models/llamacpp-qwen3.6-35b-a3b-mtp:/models:ro\n command: >\n llama-server\n -m /models/Qwen3.6-35B-A3B-UD-Q4_K_M.gguf\n -ngl 999\n -fa 1\n --spec-type draft-mtp\n --spec-draft-n-max 3\n -np 1\n -c 131072\n -n 8192\n -ctk q8_0\n -ctv q8_0\n --host 0.0.0.0\n --port 8080\n -a qwen3.6-35b-a3b-mtp\n --metrics\n ports:\n - \"127.0.0.1:8109:8080\"\n\n # M-049: Laguna XS 2.1, poolside's smaller MoE sibling of Laguna-S-2.1 —\n # 33B total / 3B active (same active-param class as qwen3.6-35b-a3b-mtp\n # above), 256K native context, official first-party GGUF from poolside\n # (unlike Laguna-S-2.1's community/just-merged-PR support). No MTP build\n # exists for this model, so no --spec-type flags here. -c 262144 matches\n # this model's native 256K context (not 131072 like the Qwen builds).\n laguna-xs-2.1-33b-q4km--llamacpp-vulkan-radv-v1:\n image: docker.io/kyuz0/amd-strix-halo-toolboxes:vulkan-radv\n container_name: laguna-xs-2.1-33b-q4km--llamacpp-vulkan-radv-v1\n devices:\n - /dev/kfd:/dev/kfd\n - /dev/dri:/dev/dri\n group_add:\n - \"26\"\n - \"303\"\n volumes:\n - /var/lib/ai-models/laguna-xs-2.1-33b-q4km:/models:ro\n command: >\n llama-server\n -m /models/Laguna-XS-2.1-Q4_K_M.gguf\n -ngl 999\n -fa 1\n -c 262144\n --host 0.0.0.0\n --port 8080\n -a laguna-xs-2.1-33b-q4km\n --metrics\n ports:\n - \"127.0.0.1:8106:8080\"\n\n # M-050: Laguna-S-2.1 target + Laguna-XS-2.1 as a classic llama.cpp\n # speculative-decoding draft model (-md / --spec-type draft-simple).\n # Recorded here for posterity/reproducibility even though the real\n # benchmark result was NEGATIVE — do not point any role at this service.\n # Tokenizer/vocab compatibility between the two models is confirmed real,\n # and speculative decoding does actively engage (draft acceptance\n # 40.8-72.4% measured), but real generation throughput (16.9-28.1 tok/s)\n # came in below the plain laguna-s-2.1-118b-q4km baseline (30.0 tok/s) at\n # every context depth tested. -c is reduced to 8192 from the plain\n # baseline's 131072 because this box's Strix Halo unified-memory GPU could\n # not fit both models' KV caches at full context (OOM/device-lost at\n # 131072 and 16384 respectively — see catalog entry for exact errors). No\n # --ctx-size-draft-equivalent flag exists on this llama-server build to\n # give the draft model its own smaller context, so -c applies to both.\n # Full writeup: catalog/builds/laguna-s-2.1-118b-q4km--llamacpp-vulkan-radv-speculative-xs.yaml\n # (status: TESTED_NOT_VIABLE) and .fleet/board/done/M-050-laguna-speculative-decoding-experiment.md.\n laguna-s-2.1-118b-q4km--llamacpp-vulkan-radv-speculative-xs-v1:\n image: docker.io/kyuz0/amd-strix-halo-toolboxes:vulkan-radv\n container_name: laguna-s-2.1-118b-q4km--llamacpp-vulkan-radv-speculative-xs-v1\n devices:\n - /dev/kfd:/dev/kfd\n - /dev/dri:/dev/dri\n group_add:\n - \"26\"\n - \"303\"\n volumes:\n - /var/lib/ai-models/laguna-s-2.1-118b-q4km:/models-s:ro\n - /var/lib/ai-models/laguna-xs-2.1-33b-q4km:/models-xs:ro\n command: >\n llama-server\n -m /models-s/UD-Q4_K_M/Laguna-S-2.1-UD-Q4_K_M-00001-of-00003.gguf\n -md /models-xs/Laguna-XS-2.1-Q4_K_M.gguf\n --spec-type draft-simple\n -ngl 999\n -fa 1\n --reasoning-format deepseek\n -n 8192\n -c 8192\n --host 0.0.0.0\n --port 8080\n -a laguna-s-2.1-118b-speculative-xs\n --metrics\n ports:\n - \"127.0.0.1:8107:8080\"\n\n # GLM-4.7-Flash judge model (M-040). Chris asked for \"the strongest\"\n # judge candidate — per the catalog's own benchmark record, that's this\n # llama.cpp/GGUF build, not the AWQ/vLLM one already registered in\n # litellm (glm-4.7-flash-awq, port 8003): 20/22 on seven-tier-coding-v2\n # vs. 18/22, and 70.1 tok/s vs. ~19-33 tok/s. Single slot (-np default 1,\n # no -cb/-kvu) since a judge doesn't need concurrent-serving complexity -\n # matches the MTP coder service's simplicity, not the 4-slot server\n # engine's template. -lm none matches the exact flags the catalog's own\n # benchmark-of-record run used (llamacpp-vulkan-radv-v1's common_flags),\n # for as close a match to the measured 70.1 tok/s as possible.\n glm-4.7-flash--llamacpp-vulkan-radv-v1:\n image: docker.io/kyuz0/amd-strix-halo-toolboxes:vulkan-radv\n container_name: glm-4.7-flash--llamacpp-vulkan-radv-v1\n devices:\n - /dev/kfd:/dev/kfd\n - /dev/dri:/dev/dri\n group_add:\n - \"26\"\n - \"303\"\n volumes:\n - /var/lib/ai-models/ollama-glm-4.7-flash:/models:ro\n command: >\n llama-server\n -m /models/GLM-4.7-Flash-Q4_K_M.gguf\n -ngl 999\n -fa 1\n -lm none\n -c 65536\n --host 0.0.0.0\n --port 8080\n -a glm-4.7-flash-judge\n --metrics\n ports:\n - \"127.0.0.1:8103:8080\"\n\n # M-048: Qwen3.5-122B-A10B (plain, UD-Q5_K_XL, non-MTP) live-server\n # deployment, for a same-quant, same-methodology comparison against the\n # MTP variant below. Already benchmarked once via llamacpp-bench-v1\n # (tg128 22.16 tok/s, see catalog/builds/qwen3.5-122b-a10b--llamacpp-vulkan-radv.yaml)\n # but llama-bench can't run the MTP side (no --spec-type support), so\n # this service re-measures the plain variant via live-server request\n # timing to match the MTP variant's only-available methodology.\n # Swap-in comparison tier, not a standing service — not wired into any\n # litellm role.\n qwen3.5-122b-a10b--llamacpp-vulkan-radv-v1:\n image: docker.io/kyuz0/amd-strix-halo-toolboxes:vulkan-radv\n container_name: qwen3.5-122b-a10b--llamacpp-vulkan-radv-v1\n devices:\n - /dev/kfd:/dev/kfd\n - /dev/dri:/dev/dri\n group_add:\n - \"26\"\n - \"303\"\n volumes:\n - /var/lib/ai-models/llamacpp-qwen3.5-122b-a10b:/models:ro\n command: >\n llama-server\n -m /models/UD-Q5_K_XL/Qwen3.5-122B-A10B-UD-Q5_K_XL-00001-of-00003.gguf\n -ngl 999\n -fa 1\n -c 131072\n --host 0.0.0.0\n --port 8080\n -a qwen3.5-122b-a10b\n --metrics\n ports:\n - \"127.0.0.1:8104:8080\"\n\n # M-048: Qwen3.5-122B-A10B MTP (multi-token-prediction) speculative-decoding\n # variant, same UD-Q5_K_XL quant tier as the plain build above (chosen\n # specifically to make this a clean same-quant comparison). --spec-type\n # draft-mtp requires -np/--parallel 1 (MTP supports only a single server\n # slot), matching the proven qwen3.6-35b-a3b-mtp flags. llama-bench does\n # not support --spec-type, so this is benchmarked via live-server request\n # timing, same as the plain build above — not blended with the\n # llamacpp-bench-v1 pp512/tg128 harness. Swap-in comparison tier, not\n # wired into any litellm role.\n qwen3.5-122b-a10b-mtp--llamacpp-vulkan-radv-mtp-v1:\n image: docker.io/kyuz0/amd-strix-halo-toolboxes:vulkan-radv\n container_name: qwen3.5-122b-a10b-mtp--llamacpp-vulkan-radv-mtp-v1\n devices:\n - /dev/kfd:/dev/kfd\n - /dev/dri:/dev/dri\n group_add:\n - \"26\"\n - \"303\"\n volumes:\n - /var/lib/ai-models/llamacpp-qwen3.5-122b-a10b-mtp:/models:ro\n command: >\n llama-server\n -m /models/UD-Q5_K_XL/Qwen3.5-122B-A10B-UD-Q5_K_XL-00001-of-00003.gguf\n -ngl 999\n -fa 1\n --spec-type draft-mtp\n --spec-draft-n-max 3\n -np 1\n -c 131072\n --host 0.0.0.0\n --port 8080\n -a qwen3.5-122b-a10b-mtp\n --metrics\n ports:\n - \"127.0.0.1:8105:8080\"\n\n # M-079: Ornith-1.0-35B (deepreinforce-ai) MTP graft (singulared) —\n # Qwen3.5-MoE agentic coder, same family as the proven qwen3.6-35b-a3b.\n # MTP head embedded in the GGUF (verified) — no separate -md draft.\n # Flags from singulared's own Strix-Halo-measured recipe: --spec-type\n # draft-mtp (requires -np 1, single server slot), --spec-draft-n-max 4,\n # --spec-draft-p-min 0.6; they recommend sweeping n-max on-box since this\n # MoE peaks around 4. -ctk/-ctv q8_0 halves the KV footprint (matching the\n # qwen3.6-35b-a3b-mtp v2 build).\n ornith-1.0-35b-mtp-q8--llamacpp-vulkan-radv-v1:\n image: docker.io/kyuz0/amd-strix-halo-toolboxes:vulkan-radv\n container_name: ornith-1.0-35b-mtp-q8--llamacpp-vulkan-radv-v1\n devices:\n - /dev/kfd:/dev/kfd\n - /dev/dri:/dev/dri\n group_add:\n - \"26\"\n - \"303\"\n volumes:\n - /var/lib/ai-models/llamacpp-ornith-1.0-35b-mtp-q8:/models:ro\n command: >\n llama-server\n -m /models/ornith-1.0-35b-MTP-Q8_0.gguf\n -ngl 999\n -fa 1\n --spec-type draft-mtp\n --spec-draft-n-max 4\n --spec-draft-p-min 0.6\n -np 1\n -c 131072\n -n 8192\n -ctk q8_0\n -ctv q8_0\n --host 0.0.0.0\n --port 8080\n -a ornith-1.0-35b-mtp-q8\n --metrics\n ports:\n - \"127.0.0.1:8110:8080\"\n\n # M-089: Ornith-1.0-35B MTP Q4_K_M — same singulared repo/graft as the Q8_0\n # service above, added for GPU memory headroom (both big-moe + medium-moe\n # loaded simultaneously left ~zero headroom and OOM-killed the Q8_0\n # container twice under concurrent agentic load; M-079's decision log\n # already flagged this quant as \"cheap to switch later\"). Same flags as\n # the Q8_0 entry — the repo's own README recommends byte-identical\n # spec-decoding flags for both quants; MTP head is kept at Q8_0 precision\n # in this build too, so acceptance barely drops (0.847 vs 0.859) while\n # decode throughput roughly follows the smaller body (~80 vs ~63-66 tok/s\n # per singulared's own Strix-Halo Vulkan numbers). Additive only — the\n # Q8_0 service above is untouched and both are defined; only one would\n # typically run at a time given the memory constraint that's the whole\n # point of this card. Not started here — deploy step only defines +\n # triggers the download; starting the container is a later smoke-test\n # step.\n ornith-1.0-35b-mtp-q4--llamacpp-vulkan-radv-v1:\n image: docker.io/kyuz0/amd-strix-halo-toolboxes:vulkan-radv\n container_name: ornith-1.0-35b-mtp-q4--llamacpp-vulkan-radv-v1\n devices:\n - /dev/kfd:/dev/kfd\n - /dev/dri:/dev/dri\n group_add:\n - \"26\"\n - \"303\"\n volumes:\n - /var/lib/ai-models/llamacpp-ornith-1.0-35b-mtp-q4:/models:ro\n command: >\n llama-server\n -m /models/ornith-1.0-35b-MTP-Q4_K_M.gguf\n -ngl 999\n -fa 1\n --spec-type draft-mtp\n --spec-draft-n-max 4\n --spec-draft-p-min 0.6\n -np 1\n -c 131072\n -n 8192\n -ctk q8_0\n -ctv q8_0\n --host 0.0.0.0\n --port 8080\n -a ornith-1.0-35b-mtp-q4\n --metrics\n ports:\n - \"127.0.0.1:8113:8080\"\n\n # M-079: KAT-Coder-V2.5-Dev (Kwaipilot) plain Q8_0 — no MTP (the source\n # model ships mtp_num_hidden_layers: 0; see the -mtp service below for\n # the grafted-head variant). Plain llama-server, no spec flags, -np 1 not\n # required.\n kat-coder-v2.5-dev-q8--llamacpp-vulkan-radv-v1:\n image: docker.io/kyuz0/amd-strix-halo-toolboxes:vulkan-radv\n container_name: kat-coder-v2.5-dev-q8--llamacpp-vulkan-radv-v1\n devices:\n - /dev/kfd:/dev/kfd\n - /dev/dri:/dev/dri\n group_add:\n - \"26\"\n - \"303\"\n volumes:\n - /var/lib/ai-models/llamacpp-kat-coder-v2.5-dev-q8:/models:ro\n command: >\n llama-server\n -m /models/Kwaipilot_KAT-Coder-V2.5-Dev-Q8_0.gguf\n -ngl 999\n -fa 1\n -c 131072\n -n 8192\n -ctk q8_0\n -ctv q8_0\n --host 0.0.0.0\n --port 8080\n -a kat-coder-v2.5-dev-q8\n --metrics\n ports:\n - \"127.0.0.1:8111:8080\"\n\n # M-079: KAT-Coder-V2.5-Dev MTP graft (gbuzhf) Q6_K — per gbuzhf's\n # measured notes MTP ALONE is a net loss on this arch, so this uses their\n # coordinate-ascent recipe: draft-mtp + ngram-mod together, --spec-draft-\n # n-max 1, --spec-draft-p-min 0.75, ngram window 8-24/match 48. Note the\n # box's llama-server build 10154 DEFAULTS are n-min 48 / n-max 64 /\n # n-match 24 — the explicit flags below are required, not cosmetic.\n # -np 1 per draft-mtp. gbuzhf's tok/s figures are from an RTX 3070 Ti\n # laptop (35/40 MoE layers on CPU); real Strix-Halo numbers need an\n # on-box benchmark.\n kat-coder-v2.5-dev-mtp-q6--llamacpp-vulkan-radv-v1:\n image: docker.io/kyuz0/amd-strix-halo-toolboxes:vulkan-radv\n container_name: kat-coder-v2.5-dev-mtp-q6--llamacpp-vulkan-radv-v1\n devices:\n - /dev/kfd:/dev/kfd\n - /dev/dri:/dev/dri\n group_add:\n - \"26\"\n - \"303\"\n volumes:\n - /var/lib/ai-models/llamacpp-kat-coder-v2.5-dev-mtp-q6:/models:ro\n command: >\n llama-server\n -m /models/Kwaipilot_KAT-Coder-V2.5-Dev-MTP-UD-Q6_K.gguf\n -ngl 999\n -fa 1\n --spec-type draft-mtp,ngram-mod\n --spec-draft-n-max 1\n --spec-draft-n-min 0\n --spec-draft-p-min 0.75\n --spec-ngram-mod-n-min 8\n --spec-ngram-mod-n-max 24\n --spec-ngram-mod-n-match 48\n -np 1\n -c 131072\n -n 8192\n -ctk q8_0\n -ctv q8_0\n --host 0.0.0.0\n --port 8080\n -a kat-coder-v2.5-dev-mtp-q6\n --metrics\n ports:\n - \"127.0.0.1:8112:8080\"\n\n # ---------------------------------------------------------------------------\n # Additional llama.cpp / laguna-fork / ds4 builds (catalog parity, M-102).\n # These mirror the build's catalog entry (status in the build.yaml) and were\n # previously only run via ad hoc `docker run` or were absent entirely.\n # ---------------------------------------------------------------------------\n\n # Best-effort standing service for a catalog build previously run via\n # llama-bench only. Catalog status WORKING.\n deepseek-v4-flash-iq2xxs--llamacpp-vulkan-radv-v1:\n image: docker.io/kyuz0/amd-strix-halo-toolboxes:vulkan-radv\n container_name: deepseek-v4-flash-iq2xxs--llamacpp-vulkan-radv-v1\n devices:\n - /dev/kfd:/dev/kfd\n - /dev/dri:/dev/dri\n group_add:\n - \"26\"\n - \"303\"\n volumes:\n - /var/lib/ai-models/llamacpp-deepseek-v4-flash-iq2xxs:/models:ro\n command: >\n llama-server\n -m /models/UD-IQ2_XXS/DeepSeek-V4-Flash-UD-IQ2_XXS-00001-of-00003.gguf\n -ngl 999\n -fa 1\n -np 1\n -c 131072\n -n 8192\n -ctk q8_0\n -ctv q8_0\n --host 0.0.0.0\n --port 8080\n -a deepseek-v4-flash-iq2xxs\n --metrics\n ports:\n - \"127.0.0.1:8114:8080\"\n\n gemma-4-26b-a4b-it--llamacpp-vulkan-radv-v1:\n image: docker.io/kyuz0/amd-strix-halo-toolboxes:vulkan-radv\n container_name: gemma-4-26b-a4b-it--llamacpp-vulkan-radv-v1\n devices:\n - /dev/kfd:/dev/kfd\n - /dev/dri:/dev/dri\n group_add:\n - \"26\"\n - \"303\"\n volumes:\n - /var/lib/ai-models/ollama-gemma-4-26b-a4b-it:/models:ro\n command: >\n llama-server\n -m /models/gemma-4-26B-A4B-it-UD-Q4_K_M.gguf\n -ngl 999\n -fa 1\n -np 1\n -c 131072\n -n 8192\n -ctk q8_0\n -ctv q8_0\n --host 0.0.0.0\n --port 8080\n -a gemma-4-26b-a4b-it\n --metrics\n ports:\n - \"127.0.0.1:8115:8080\"\n\n gpt-oss-120b--llamacpp-vulkan-radv-v1:\n image: docker.io/kyuz0/amd-strix-halo-toolboxes:vulkan-radv\n container_name: gpt-oss-120b--llamacpp-vulkan-radv-v1\n devices:\n - /dev/kfd:/dev/kfd\n - /dev/dri:/dev/dri\n group_add:\n - \"26\"\n - \"303\"\n volumes:\n - /var/lib/ai-models/llamacpp-gpt-oss-120b:/models:ro\n command: >\n llama-server\n -m /models/gpt-oss-120b-MXFP4.gguf\n -ngl 999\n -fa 1\n -np 1\n -c 131072\n -n 8192\n -ctk q8_0\n -ctv q8_0\n --host 0.0.0.0\n --port 8080\n -a gpt-oss-120b-llamacpp\n --metrics\n ports:\n - \"127.0.0.1:8116:8080\"\n\n minimax-m2.7--llamacpp-vulkan-radv-v1:\n image: docker.io/kyuz0/amd-strix-halo-toolboxes:vulkan-radv\n container_name: minimax-m2.7--llamacpp-vulkan-radv-v1\n devices:\n - /dev/kfd:/dev/kfd\n - /dev/dri:/dev/dri\n group_add:\n - \"26\"\n - \"303\"\n volumes:\n - /var/lib/ai-models/llamacpp-minimax-m2.7:/models:ro\n command: >\n llama-server\n -m /models/UD-Q3_K_S/MiniMax-M2.7-UD-Q3_K_S-00001-of-00003.gguf\n -ngl 999\n -fa 1\n -np 1\n -c 131072\n -n 8192\n -ctk q8_0\n -ctv q8_0\n --host 0.0.0.0\n --port 8080\n -a minimax-m2.7\n --metrics\n ports:\n - \"127.0.0.1:8117:8080\"\n\n nvidia-nemotron-3-super-120b-a12b--llamacpp-vulkan-radv-v1:\n image: docker.io/kyuz0/amd-strix-halo-toolboxes:vulkan-radv\n container_name: nvidia-nemotron-3-super-120b-a12b--llamacpp-vulkan-radv-v1\n devices:\n - /dev/kfd:/dev/kfd\n - /dev/dri:/dev/dri\n group_add:\n - \"26\"\n - \"303\"\n volumes:\n - /var/lib/ai-models/llamacpp-nemotron-3-super-120b:/models:ro\n command: >\n llama-server\n -m /models/UD-Q4_K_XL/NVIDIA-Nemotron-3-Super-120B-A12B-UD-Q4_K_XL-00001-of-00003.gguf\n -ngl 999\n -fa 1\n -np 1\n -c 131072\n -n 8192\n -ctk q8_0\n -ctv q8_0\n --host 0.0.0.0\n --port 8080\n -a nemotron-3-super-120b\n --metrics\n ports:\n - \"127.0.0.1:8118:8080\"\n\n qwen3.6-27b--llamacpp-vulkan-radv-v1:\n image: docker.io/kyuz0/amd-strix-halo-toolboxes:vulkan-radv\n container_name: qwen3.6-27b--llamacpp-vulkan-radv-v1\n devices:\n - /dev/kfd:/dev/kfd\n - /dev/dri:/dev/dri\n group_add:\n - \"26\"\n - \"303\"\n volumes:\n - /var/lib/ai-models/ollama-qwen3.6-27b:/models:ro\n command: >\n llama-server\n -m /models/Qwen3.6-27B-Q4_K_M.gguf\n -ngl 999\n -fa 1\n -np 1\n -c 131072\n -n 8192\n -ctk q8_0\n -ctv q8_0\n --host 0.0.0.0\n --port 8080\n -a qwen3.6-27b-llamacpp\n --metrics\n ports:\n - \"127.0.0.1:8119:8080\"\n\n # MTP-tensor-bearing GGUF (Qwen3.6-27B-Q4_K_M.gguf from the mtp dir is the\n # MTP variant, not the plain one — serves with draft-mtp speculation).\n qwen3.6-27b--llamacpp-vulkan-radv-mtp-v1:\n image: docker.io/kyuz0/amd-strix-halo-toolboxes:vulkan-radv\n container_name: qwen3.6-27b--llamacpp-vulkan-radv-mtp-v1\n devices:\n - /dev/kfd:/dev/kfd\n - /dev/dri:/dev/dri\n group_add:\n - \"26\"\n - \"303\"\n volumes:\n - /var/lib/ai-models/llamacpp-qwen3.6-27b-mtp:/models:ro\n command: >\n llama-server\n -m /models/Qwen3.6-27B-Q4_K_M.gguf\n -ngl 999\n -fa 1\n --spec-type draft-mtp\n --spec-draft-n-max 3\n -np 1\n -c 131072\n -n 8192\n -ctk q8_0\n -ctv q8_0\n --host 0.0.0.0\n --port 8080\n -a qwen3.6-27b-mtp\n --metrics\n ports:\n - \"127.0.0.1:8120:8080\"\n\n qwen3.6-35b-a3b--llamacpp-vulkan-radv-v1:\n image: docker.io/kyuz0/amd-strix-halo-toolboxes:vulkan-radv\n container_name: qwen3.6-35b-a3b--llamacpp-vulkan-radv-v1\n devices:\n - /dev/kfd:/dev/kfd\n - /dev/dri:/dev/dri\n group_add:\n - \"26\"\n - \"303\"\n volumes:\n - /var/lib/ai-models/ollama-qwen3.6-35b-a3b:/models:ro\n command: >\n llama-server\n -m /models/Qwen3.6-35B-A3B-UD-Q4_K_M.gguf\n -ngl 999\n -fa 1\n -np 1\n -c 131072\n -n 8192\n -ctk q8_0\n -ctv q8_0\n --host 0.0.0.0\n --port 8080\n -a qwen3.6-35b-a3b-llamacpp\n --metrics\n ports:\n - \"127.0.0.1:8121:8080\"\n\n # ---------------------------------------------------------------------------\n # Laguna-S-2.1 + DFlash draft (poolside laguna fork) — catalog parity\n # ---------------------------------------------------------------------------\n\n laguna-s-2.1-118b-q4km--llamacpp-laguna-fork-rocm-7.2.2-dflash-v1:\n image: local-ai-machine/llamacpp-laguna-fork:rocm-7.2.2\n container_name: laguna-s-2.1-118b-q4km--llamacpp-laguna-fork-rocm-7.2.2-dflash-v1\n devices:\n - /dev/kfd:/dev/kfd\n - /dev/dri:/dev/dri\n group_add:\n - \"26\"\n - \"303\"\n mem_limit: 110g\n volumes:\n - /var/lib/ai-models/laguna-s-2.1-118b-q4km:/models:ro\n - /var/lib/ai-models/laguna-s-2.1-dflash-draft:/models-draft:ro\n command: >\n /build/build/bin/llama-server\n -m /models/UD-Q4_K_M/Laguna-S-2.1-UD-Q4_K_M-00001-of-00003.gguf\n -md /models-draft/laguna-s-2.1-DFlash-BF16.gguf\n --spec-type draft-dflash\n --spec-draft-n-max 15\n -ngl 999\n -fa 1\n --no-mmap\n -dio\n --swa-full\n --reasoning-format deepseek\n -np 1\n -c 131072\n -n 8192\n -ctk q8_0\n -ctv q8_0\n --host 0.0.0.0\n --port 8080\n -a laguna-dflash-rocm-722\n --metrics\n ports:\n - \"127.0.0.1:8122:8080\"\n\n # M-055 ROCm control build — never got past loading (see engine gotchas).\n laguna-s-2.1-118b-q4km--llamacpp-laguna-fork-rocm-dflash-v1:\n image: local-ai-machine/llamacpp-laguna-fork:rocm-7.14\n container_name: laguna-s-2.1-118b-q4km--llamacpp-laguna-fork-rocm-dflash-v1\n devices:\n - /dev/kfd:/dev/kfd\n - /dev/dri:/dev/dri\n group_add:\n - \"26\"\n - \"303\"\n volumes:\n - /var/lib/ai-models/laguna-s-2.1-118b-q4km:/models:ro\n - /var/lib/ai-models/laguna-s-2.1-dflash-draft:/models-draft:ro\n command: >\n /build/build/bin/llama-server\n -m /models/UD-Q4_K_M/Laguna-S-2.1-UD-Q4_K_M-00001-of-00003.gguf\n -md /models-draft/laguna-s-2.1-DFlash-BF16.gguf\n --spec-type draft-dflash\n --spec-draft-n-max 15\n -ngl 999\n -fa 0\n --swa-full\n --reasoning-format deepseek\n -np 1\n -c 131072\n -n 8192\n -ctk q8_0\n -ctv q8_0\n --host 0.0.0.0\n --port 8080\n -a laguna-dflash-rocm\n --metrics\n ports:\n - \"127.0.0.1:8123:8080\"\n\n # Original Vulkan DFlash build — measured negative (M-053), kept for parity.\n laguna-s-2.1-118b-q4km--llamacpp-laguna-fork-vulkan-dflash-v1:\n image: local-ai-machine/llamacpp-laguna-fork:vulkan-radv\n container_name: laguna-s-2.1-118b-q4km--llamacpp-laguna-fork-vulkan-dflash-v1\n devices:\n - /dev/kfd:/dev/kfd\n - /dev/dri:/dev/dri\n group_add:\n - \"26\"\n - \"303\"\n volumes:\n - /var/lib/ai-models/laguna-s-2.1-118b-q4km:/models:ro\n - /var/lib/ai-models/laguna-s-2.1-dflash-draft:/models-draft:ro\n command: >\n /build/build/bin/llama-server\n -m /models/UD-Q4_K_M/Laguna-S-2.1-UD-Q4_K_M-00001-of-00003.gguf\n -md /models-draft/laguna-s-2.1-DFlash-BF16.gguf\n --spec-type draft-dflash\n --spec-draft-n-max 15\n -ngl 999\n -fa 1\n --swa-full\n --reasoning-format deepseek\n -np 1\n -c 131072\n -n 8192\n -ctk q8_0\n -ctv q8_0\n --host 0.0.0.0\n --port 8080\n -a laguna-dflash-vulkan\n --metrics\n ports:\n - \"127.0.0.1:8124:8080\"\n\n # v2 = quantized-draft result (Q4_K_M draft, n2, 33-35 tok/s) — the ONE\n # DFlash config that beats the 30.0 tok/s plain baseline (M-086).\n laguna-s-2.1-118b-q4km--llamacpp-laguna-fork-vulkan-dflash-v2:\n image: local-ai-machine/llamacpp-laguna-fork:vulkan-radv\n container_name: laguna-s-2.1-118b-q4km--llamacpp-laguna-fork-vulkan-dflash-v2\n devices:\n - /dev/kfd:/dev/kfd\n - /dev/dri:/dev/dri\n group_add:\n - \"26\"\n - \"303\"\n volumes:\n - /var/lib/ai-models/laguna-s-2.1-118b-q4km:/models:ro\n - /var/lib/ai-models/laguna-s-2.1-dflash-draft-quant:/models-draft:ro\n command: >\n /build/build/bin/llama-server\n -m /models/UD-Q4_K_M/Laguna-S-2.1-UD-Q4_K_M-00001-of-00003.gguf\n -md /models-draft/laguna-s-2.1-DFlash-Q4_K_M.gguf\n --spec-type draft-dflash\n --spec-draft-n-max 2\n -ngl 999\n -fa 1\n --swa-full\n --reasoning-format deepseek\n -np 1\n -c 131072\n -n 8192\n -ctk q8_0\n -ctv q8_0\n --host 0.0.0.0\n --port 8080\n -a laguna-dflash-vulkan-v2\n --metrics\n ports:\n - \"127.0.0.1:8125:8080\"\n\n # BROKEN — classic-speculative attempt with the poolside DFlash draft\n # checkpoint on the plain toolbox (M-0xx); no draft-server support there.\n laguna-s-2.1-118b-q4km--llamacpp-vulkan-radv-speculative-dflash-v1:\n image: docker.io/kyuz0/amd-strix-halo-toolboxes:vulkan-radv\n container_name: laguna-s-2.1-118b-q4km--llamacpp-vulkan-radv-speculative-dflash-v1\n devices:\n - /dev/kfd:/dev/kfd\n - /dev/dri:/dev/dri\n group_add:\n - \"26\"\n - \"303\"\n volumes:\n - /var/lib/ai-models/laguna-s-2.1-118b-q4km:/models:ro\n - /var/lib/ai-models/laguna-s-2.1-dflash-draft:/models-draft:ro\n command: >\n llama-server\n -m /models/UD-Q4_K_M/Laguna-S-2.1-UD-Q4_K_M-00001-of-00003.gguf\n -md /models-draft/laguna-s-2.1-DFlash-BF16.gguf\n --spec-type draft-dflash\n --spec-draft-n-max 15\n -ngl 999\n -fa 1\n --swa-full\n --reasoning-format deepseek\n -np 1\n -c 131072\n -n 8192\n -ctk q8_0\n -ctv q8_0\n --host 0.0.0.0\n --port 8080\n -a laguna-dflash-classic\n --metrics\n ports:\n - \"127.0.0.1:8126:8080\"\n\n # ---------------------------------------------------------------------------\n # DS4 (DwarfStar) engine builds — antirez/ds4, ad hoc docker run previously\n # ---------------------------------------------------------------------------\n\n ds4-deepseek-v4-flash-iq2xxs--ds4-strix-halo-v1:\n image: kyuz0/strix-halo-ds4-toolbox:rocm-7.14\n container_name: ds4-deepseek-v4-flash-iq2xxs--ds4-strix-halo-v1\n devices:\n - /dev/kfd:/dev/kfd\n - /dev/dri:/dev/dri\n group_add:\n - \"26\"\n - \"303\"\n volumes:\n - /var/lib/ai-models/ds4-deepseek-v4-flash-iq2xxs:/models:ro\n command: >\n ds4-server\n -m /models/DeepSeek-V4-Flash-IQ2XXS-w2Q2K-AProjQ8-SExpQ8-OutQ8-chat-v2.gguf\n --ctx 128000\n --prefill-chunk 1024\n --host 0.0.0.0\n --port 8080\n ports:\n - \"127.0.0.1:8127:8080\"\n\n # Recommended imatrix GGUF variant (STRIXHALO.md) — M-051 corrected re-run.\n ds4-deepseek-v4-flash-iq2xxs-imatrix--ds4-strix-halo-v1:\n image: kyuz0/strix-halo-ds4-toolbox:rocm-7.14\n container_name: ds4-deepseek-v4-flash-iq2xxs-imatrix--ds4-strix-halo-v1\n devices:\n - /dev/kfd:/dev/kfd\n - /dev/dri:/dev/dri\n group_add:\n - \"26\"\n - \"303\"\n volumes:\n - /var/lib/ai-models/ds4-deepseek-v4-flash-iq2xxs-imatrix:/models:ro\n command: >\n ds4-server\n -m /models/DeepSeek-V4-Flash-IQ2XXS-w2Q2K-AProjQ8-SExpQ8-OutQ8-chat-v2-imatrix.gguf\n --ctx 128000\n --prefill-chunk 1024\n --host 0.0.0.0\n --port 8080\n ports:\n - \"127.0.0.1:8128:8080\"\n\n # ---------------------------------------------------------------------------\n # Ollama — shared single instance, port 11434\n # ---------------------------------------------------------------------------\n # Ollama can only serve one instance on one port. Model switching is\n # API-level: `ollama run ` or `ollama pull ` via the\n # container's API. Individual catalog builds document which model to use.\n # Plain tag, NOT :0.17.7-rocm — the -rocm tag crashes on gfx1151 and\n # doesn't even bundle Vulkan libs.\n ollama:\n image: ollama/ollama:0.17.7\n container_name: ollama\n restart: unless-stopped\n labels:\n com.local-ai-machine.always-up: \"true\"\n environment:\n - OLLAMA_VULKAN=1\n - HIP_VISIBLE_DEVICES=-1\n - ROCR_VISIBLE_DEVICES=-1\n devices:\n - /dev/kfd:/dev/kfd\n - /dev/dri:/dev/dri\n group_add:\n - \"26\"\n - \"303\"\n ports:\n - \"127.0.0.1:11434:11434\"\n volumes:\n - ollama_data:/root/.ollama\n - /var/lib/ai-models:/gguf-source:ro\n\n # ---------------------------------------------------------------------------\n # Dedicated ollama instances — one per ollama catalog build (M-102).\n # Each has its own container_name + port so the orchestrator can stop/start\n # it independently for benchmarking. All share the ollama_data model store\n # (models are registered once via scripts/ollama_register_model.sh) and the\n # /var/lib/ai-models read-only GGUF source for re-registration. No\n # always-up label: these are benchmark targets.\n # ---------------------------------------------------------------------------\n\n gemma-4-26b-a4b-it--ollama-vulkan-0177-v1:\n image: ollama/ollama:0.17.7\n container_name: gemma-4-26b-a4b-it--ollama-vulkan-0177-v1\n environment:\n - OLLAMA_VULKAN=1\n - HIP_VISIBLE_DEVICES=-1\n - ROCR_VISIBLE_DEVICES=-1\n devices:\n - /dev/kfd:/dev/kfd\n - /dev/dri:/dev/dri\n group_add:\n - \"26\"\n - \"303\"\n ports:\n - \"127.0.0.1:11435:11434\"\n volumes:\n - ollama_data:/root/.ollama\n - /var/lib/ai-models:/gguf-source:ro\n\n glm-4.7-flash--ollama-vulkan-0177-v1:\n image: ollama/ollama:0.17.7\n container_name: glm-4.7-flash--ollama-vulkan-0177-v1\n environment:\n - OLLAMA_VULKAN=1\n - HIP_VISIBLE_DEVICES=-1\n - ROCR_VISIBLE_DEVICES=-1\n devices:\n - /dev/kfd:/dev/kfd\n - /dev/dri:/dev/dri\n group_add:\n - \"26\"\n - \"303\"\n ports:\n - \"127.0.0.1:11436:11434\"\n volumes:\n - ollama_data:/root/.ollama\n - /var/lib/ai-models:/gguf-source:ro\n\n # BROKEN config preserved verbatim: the -rocm tag crashes on gfx1151 after\n # the first real request (catalog status BROKEN).\n glm-4.7-flash--ollama-rocm-0177-broken-v1:\n image: ollama/ollama:0.17.7-rocm\n container_name: glm-4.7-flash--ollama-rocm-0177-broken-v1\n devices:\n - /dev/kfd:/dev/kfd\n - /dev/dri:/dev/dri\n group_add:\n - \"26\"\n - \"303\"\n ports:\n - \"127.0.0.1:11437:11434\"\n volumes:\n - ollama_data:/root/.ollama\n - /var/lib/ai-models:/gguf-source:ro\n\n qwen3.6-27b--ollama-vulkan-0177-v1:\n image: ollama/ollama:0.17.7\n container_name: qwen3.6-27b--ollama-vulkan-0177-v1\n environment:\n - OLLAMA_VULKAN=1\n - HIP_VISIBLE_DEVICES=-1\n - ROCR_VISIBLE_DEVICES=-1\n devices:\n - /dev/kfd:/dev/kfd\n - /dev/dri:/dev/dri\n group_add:\n - \"26\"\n - \"303\"\n ports:\n - \"127.0.0.1:11438:11434\"\n volumes:\n - ollama_data:/root/.ollama\n - /var/lib/ai-models:/gguf-source:ro\n\n qwen3.6-35b-a3b--ollama-vulkan-0177-v1:\n image: ollama/ollama:0.17.7\n container_name: qwen3.6-35b-a3b--ollama-vulkan-0177-v1\n environment:\n - OLLAMA_VULKAN=1\n - HIP_VISIBLE_DEVICES=-1\n - ROCR_VISIBLE_DEVICES=-1\n devices:\n - /dev/kfd:/dev/kfd\n - /dev/dri:/dev/dri\n group_add:\n - \"26\"\n - \"303\"\n ports:\n - \"127.0.0.1:11439:11434\"\n volumes:\n - ollama_data:/root/.ollama\n - /var/lib/ai-models:/gguf-source:ro\n\n # ---------------------------------------------------------------------------\n # Infrastructure — role-agnostic services\n # ---------------------------------------------------------------------------\n\n # Unified API Gateway — aliases role names to concrete model ports.\n # Virtual keys (sk-chris-master, sk-drew-edge) are generated at runtime\n # via /key/generate, not declared in config.yaml.\n litellm-db:\n image: postgres:16-alpine\n container_name: litellm-db\n restart: unless-stopped\n labels:\n com.local-ai-machine.always-up: \"true\"\n ports:\n - \"127.0.0.1:5432:5432\"\n environment:\n POSTGRES_DB: litellm\n POSTGRES_USER: litellm_user\n POSTGRES_PASSWORD: ${LITELLM_DB_PASSWORD}\n volumes:\n - litellm_postgres_data:/var/lib/postgresql/data\n\n litellm:\n image: ghcr.io/berriai/litellm:main-latest\n container_name: litellm-proxy\n restart: unless-stopped\n labels:\n com.local-ai-machine.always-up: \"true\"\n network_mode: host\n volumes:\n - ./litellm/config.yaml:/app/config.yaml\n - ./litellm/jsonl_logger.py:/app/jsonl_logger.py\n - ./litellm/logs:/app/logs\n environment:\n - LITELLM_MASTER_KEY=${LITELLM_MASTER_KEY}\n - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}\n - DATABASE_URL=postgresql://litellm_user:${LITELLM_DB_PASSWORD}@127.0.0.1:5432/litellm\n command: [\"--config\", \"/app/config.yaml\", \"--port\", \"4000\"]\n depends_on:\n - litellm-db\n\n # litellm-queue-haproxy (M-094, superseding the M-094 Phase 1 custom Bun\n # proxy — that implementation's source stays at docker/litellm-queue-proxy/\n # for reference/history, its own card decision log documents a real bug it\n # had: a request could hang indefinitely with no timeout-based fallback\n # release, and it also blocked its own health endpoint while stuck, since\n # every path shared the one semaphore with no bypass).\n #\n # HAProxy has been solving exactly this class of problem — hold excess\n # concurrent requests to a backend in a queue, with a real timeout-based\n # safety valve, rather than reject or hang — for two decades. Confirmed\n # before building this that the custom proxy never had any real\n # litellm/LLM-protocol-specific logic in it (pure transparent method/\n # header/body forwarding + a connection-scoped concurrency gate), so a\n # generic, battle-tested reverse proxy is a direct, sound replacement —\n # not a \"good enough\" compromise.\n #\n # `maxconn 1` on the single backend server (docker/litellm-queue-haproxy/\n # haproxy.cfg) gives GLOBAL (not per-model) concurrency serialization —\n # every request goes through litellm as one upstream regardless of which\n # model it targets, so limiting concurrency to litellm itself achieves\n # cross-model serialization, protecting against memory-bandwidth\n # contention and OOM between concurrently-resident models (see M-094's\n # card for the full reasoning — same failure class as M-037/M-078, this\n # time triggered by concurrent requests rather than concurrent container\n # starts).\n #\n # `network_mode: host` for the identical reason the Bun version needed it:\n # litellm binds directly to the host's own port 4000 via its own\n # `network_mode: host`, and a bridge-networked container's \"127.0.0.1\"\n # refers to itself, not the host.\n #\n # Nothing points at :4001 by default — pi-web and every other caller still\n # talk to litellm directly on :4000, completely unaffected by this\n # service's presence, until a cutover is explicitly re-attempted (gated,\n # see the card — the first cutover attempt was reverted after finding the\n # custom proxy's hang bug).\n litellm-queue-haproxy:\n image: haproxy:latest\n container_name: litellm-queue-haproxy\n restart: unless-stopped\n labels:\n com.local-ai-machine.always-up: \"true\"\n network_mode: host\n volumes:\n - ./litellm-queue-haproxy/haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro\n depends_on:\n - litellm\n\n # web_search backend (per M-038, pi-web; originally deployed alongside the\n # now-decommissioned Turnstone service too). `search.formats: [html, json]`\n # in ./searxng/settings.yml is the one setting that actually matters: a\n # stock SearxNG ships html-only and 403s on format=json, the most common\n # new-deployment failure — internal-network-only, no UI exposed to anyone.\n #\n # 127.0.0.1:8091 publish (M-038, 2026-07-31): pi-web is `network_mode:\n # host`, so it has no bridge DNS at all — same class of problem the\n # litellm/prometheus host-networking fix solved earlier this session, but\n # the mirror-image direction: there, the *caller* needed host networking to\n # reach a 127.0.0.1-bound *target*; here the *caller* (pi-web) is already\n # host-networked and the *target* (searxng) needs a loopback-bound publish\n # so the host namespace can see it. Confirmed before adding this: the host\n # can already reach searxng directly via its bridge IP (`docker network\n # inspect docker_default`), but that IP isn't guaranteed stable across\n # container recreation, so publishing to a fixed 127.0.0.1 port (matching\n # every other backend service in this file) is the durable fix rather than\n # hardcoding the bridge IP. 8091 chosen as the next free loopback port\n # outside the 8000-8012 (vLLM) / 8100-8199 (llama.cpp) ranges.\n searxng:\n image: searxng/searxng:latest\n container_name: searxng\n restart: unless-stopped\n labels:\n com.local-ai-machine.always-up: \"true\"\n ports:\n - \"127.0.0.1:8091:8080\"\n volumes:\n - ./searxng:/etc/searxng:ro\n - searxng-cache:/var/cache/searxng\n healthcheck:\n test: [\"CMD\", \"wget\", \"-q\", \"--spider\", \"http://localhost:8080/healthz\"]\n interval: 10s\n timeout: 5s\n retries: 5\n start_period: 20s\n\n # ---------------------------------------------------------------------------\n # llm-inference-bench orchestrator (M-101) — port 8092 (LAN)\n # ---------------------------------------------------------------------------\n # Containerized benchmark orchestrator, deliberately a NEW stack separate\n # from catalog/ + scripts/benchmark_orchestrator.py (which stays as-is).\n # Runs the vendored julien-lebot/llm-inference-bench llm_decode_bench.py\n # against named model services, pushes raw structured JSON to main, and\n # serves a LAN web queue (port 8092).\n #\n # network_mode: host for the same reason litellm/prometheus use it: every\n # model port in this stack binds 127.0.0.1 only, and only host networking\n # lets the bench client (and health checks) reach those ports. The web UI\n # consequently binds 0.0.0.0:8092 directly — LAN-visible, no auth\n # (Chris-approved), keep it off the WAN boundary.\n #\n # user 1000:1000 + group_add 131 (docker GID) = the exact omp/pi-web\n # pattern for a container that must run `docker` against the box's daemon\n # through the mounted socket.\n #\n # volumes:\n # - /home/chris/local-ai-machine-bench:/bench — the DEDICATED bench-only\n # checkout, hard-reset to origin/main before every run; results are\n # committed + pushed straight to main from here. NEVER the human box\n # checkout at /home/chris/local-ai-machine.\n # - the box's existing push-capable deploy key + known_hosts, read-only,\n # so git fetch/reset/push need no agent or new credentials.\n #\n # must carry the com.local-ai-machine.always-up label — otherwise this\n # service would be a \"model\" by this stack's two-tier convention and the\n # orchestrator's own stop-everything-exclusive logic would stop ITSELF\n # mid-run (it can't run a benchmark against a stopped orchestrator).\n # `restart: unless-stopped` so an orchestrator crash self-heals; queue\n # state persists on a gitignored path under /bench so pending runs survive\n # the restart.\n llm-inference-bench:\n build:\n context: ./llm-inference-bench\n image: local-ai-machine/llm-inference-bench:latest\n container_name: llm-inference-bench\n restart: unless-stopped\n labels:\n com.local-ai-machine.always-up: \"true\"\n network_mode: host\n user: \"1000:1000\"\n group_add:\n - \"131\" # docker group GID (matches host's docker group)\n volumes:\n - /home/chris/local-ai-machine-bench:/bench\n - /var/run/docker.sock:/var/run/docker.sock\n - /home/chris/.ssh/github_deploy_key:/bench-key/github_deploy_key:ro\n - /home/chris/.ssh/known_hosts:/bench-key/known_hosts:ro\n environment:\n - BENCH_REPO_URL=git@github.com:chrisjohnson/local-ai-machine.git\n - BENCH_CHECKOUT=/bench\n - BENCH_DOCKER_DIR=/bench/docker\n - BENCH_PORT=8092\n # ssh/git need a resolvable HOME; the image creates uid-1000 user \"bench\"\n - HOME=/home/bench\n # Match the box's existing repo identity for the results commits.\n - BENCH_GIT_AUTHOR_NAME=local-ai-machine-bench\n - BENCH_GIT_AUTHOR_EMAIL=local-ai-machine-bench@localhost\n\n # Telemetry & Observability Stack\n # network_mode: host (matching litellm's own reason for the same thing)\n # — every model-serving port in this stack is deliberately bound to\n # 127.0.0.1 only, not exposed on the LAN (see the port-keyed scrape\n # targets in prometheus.yml). extra_hosts/host.docker.internal reaches\n # the host via its *bridge* gateway IP, which a 127.0.0.1-only listener\n # correctly refuses — confirmed directly (connection refused from\n # host.docker.internal, works fine from the host's own loopback).\n # Host networking is the only way to reach those ports without loosening\n # their actual bind address, which would be a real security regression.\n prometheus:\n image: prom/prometheus:latest\n container_name: prometheus\n restart: unless-stopped\n labels:\n com.local-ai-machine.always-up: \"true\"\n network_mode: host\n volumes:\n - ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml\n depends_on:\n - node-exporter\n\n node-exporter:\n image: prom/node-exporter:latest\n container_name: node-exporter\n restart: unless-stopped\n labels:\n com.local-ai-machine.always-up: \"true\"\n pid: host\n # Now-host-networked prometheus can't resolve bridge-network DNS names\n # (\"node-exporter:9100\") anymore — needs an actual published port,\n # loopback-only like every other monitoring/model port in this stack.\n ports:\n - \"127.0.0.1:9100:9100\"\n volumes:\n - /proc:/host/proc:ro\n - /sys:/host/sys:ro\n - /:/rootfs:ro\n - /var/lib/node-exporter-textfile:/textfile:ro\n command:\n - \"--path.procfs=/host/proc\"\n - \"--path.sysfs=/host/sys\"\n - \"--path.rootfs=/rootfs\"\n - \"--collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/)\"\n - \"--collector.textfile.directory=/textfile\"\n\n grafana:\n image: grafana/grafana:latest\n container_name: grafana\n restart: unless-stopped\n labels:\n com.local-ai-machine.always-up: \"true\"\n ports:\n - \"3000:3000\"\n environment:\n - GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_ADMIN_PASSWORD}\n volumes:\n - grafana_data:/var/lib/grafana\n - ./grafana/dashboards:/var/lib/grafana/dashboards\n - ./grafana/provisioning:/etc/grafana/provisioning\n # prometheus moved to network_mode: host (see its own comment) so it\n # dropped off the bridge network entirely — Grafana (still bridge-\n # networked) can no longer resolve \"prometheus\" via Docker's embedded\n # DNS. Reach it via host.docker.internal instead; unlike the model-\n # serving ports, prometheus itself isn't restricted to 127.0.0.1, so\n # this actually works (real difference from the host.docker.internal\n # attempt that failed for the model ports in the prior PR).\n extra_hosts:\n - \"host.docker.internal:host-gateway\"\n depends_on:\n - prometheus\n\n # Browser chat UI — points at LiteLLM, not any model server directly.\n open-webui:\n image: ghcr.io/open-webui/open-webui:main\n container_name: open-webui\n restart: unless-stopped\n #labels:\n #com.local-ai-machine.always-up: \"true\"\n ports:\n - \"3001:8080\"\n environment:\n - OPENAI_API_BASE_URL=http://litellm:4000/v1\n - OPENAI_API_KEY=${LITELLM_MASTER_KEY}\n - WEBUI_AUTH=true\n volumes:\n - open_webui_data:/app/backend/data\n depends_on:\n - litellm\n\n omp:\n build:\n context: ../oh-my-pi/\n image: ghcr.io/can1357/oh-my-pi:latest\n container_name: oh-my-pi\n user: \"1000:1000\"\n tty: true\n stdin_open: true\n volumes:\n - /home/chris/turnstone-workspace/printer-dashboard:/work\n - ~/.omp:/home/node/.omp\n - /home/chris/.ssh:/home/node/.ssh\n - /var/run/docker.sock:/var/run/docker.sock\n #labels:\n #com.local-ai-machine.always-up: \"true\"\n network_mode: host\n group_add:\n - \"131\" # docker group GID (matches host's docker group)\n environment:\n # Relative name, not absolute: omp resolves the config root as\n # path.join(os.homedir(), PI_CONFIG_DIR), so \".omp\" lands on the\n # ~/.omp:/home/omp/.omp mount above.\n - PI_CONFIG_DIR=.omp\n # Seed-time substitution target for models.seed.yml.tmpl's\n # __LITELLM_MASTER_KEY__ placeholder (oh-my-pi/docker-entrypoint.sh).\n # Same env value every litellm-facing service already gets.\n - LITELLM_MASTER_KEY=${LITELLM_MASTER_KEY}\n working_dir: /work\n # `cli` is a subcommand of the omp binary, not a PATH executable; our\n # Dockerfile entrypoint is the seed script (not upstream's `omp`), so the\n # subcommand must be dispatched through the binary explicitly.\n command: [\"omp\"]\n extra_hosts:\n # Reach the host-networked litellm proxy (port 4000) from this\n # bridge-networked container — same pattern as turnstone.\n - \"host.docker.internal:host-gateway\"\n depends_on:\n - litellm\n\n jmfederico-pi-web:\n build:\n context: ../jmfederico-pi-web/\n image: ghcr.io/jmfederico/pi-web:latest\n container_name: pi-web\n user: \"1000:1000\"\n tty: true\n stdin_open: true\n ports:\n - \"8080:3000\" # Expose the Next.js UI port\n volumes:\n - /home/chris/turnstone-workspace/printer-dashboard:/work\n - ~/.pi-web:/home/piweb/.pi-web\n - /home/chris/.ssh:/home/piweb/.ssh\n - /var/run/docker.sock:/var/run/docker.sock\n labels:\n com.local-ai-machine.always-up: \"true\"\n group_add:\n - \"131\" # docker group GID\n environment:\n # Absolute path for pi-web config directory\n - PI_CODING_AGENT_DIR=/home/piweb/.pi-web\n - LITELLM_MASTER_KEY=${LITELLM_MASTER_KEY}\n # M-094: pi-web-factory's own Workflow-Step wait-loop timeout\n # (modules/piwebClient.ts's waitForCompletion). 120s default was sized\n # for \"pi-web is just slow sometimes\" — now that a Step's completion\n # request can genuinely queue behind another model's full generation\n # (the litellm-queue-proxy, port 4001, models.json now points here\n # instead of litellm's own :4000), a queued Step needs headroom for\n # BOTH the wait AND its own generation time on top. 10 minutes is a\n # deliberately generous starting point pending Phase 2's real\n # concurrent-Workflow-Run test — tune down once real data exists.\n - PI_WEB_FACTORY_STEP_TIMEOUT_MS=600000\n # @juicesharp/rpiv-web-tools (see settings.seed.json's packages\n # array): reuses the SearxNG instance already running for the other\n # pi-web variant (M-038) rather than a second search stack. That\n # variant is network_mode: host and reaches searxng via its\n # loopback-only host publish (127.0.0.1:8091) - this service is\n # bridge-networked instead, and host.docker.internal can't see a\n # 127.0.0.1-bound host port at all (confirmed live: ECONNREFUSED).\n # Since both services already sit on the same default bridge network\n # (br-localai), container-to-container DNS + searxng's actual\n # internal port (8080, independent of how it's published to the\n # host) works directly - confirmed live (200 from /healthz).\n - WEB_SEARCH_PROVIDER=searxng\n - SEARXNG_URL=http://searxng:8080\n working_dir: /work\n command: [\"bash\", \"-c\", \"pi-web-sessiond & PI_WEB_HOST=0.0.0.0 PI_WEB_PORT=3000 pi-web-server\"]\n extra_hosts:\n - \"host.docker.internal:host-gateway\"\n depends_on:\n - litellm\n\n # pi-web-factory-visualizer (M-077/M-081): read-only web UI over\n # pi-web-factory's trace db. Reuses the SAME already-built image as\n # jmfederico-pi-web (no separate `build:` context — pi-web-factory is\n # baked into that one image, M-068) rather than a standalone `docker run`,\n # so it's git-driven and comes up/down with the rest of this stack like\n # every other service here. Deliberately a SEPARATE service, not a second\n # process bolted onto the jmfederico-pi-web container itself: independent\n # restart/redeploy lifecycle (a visualizer bug or update shouldn't need to\n # restart the container hosting every live coding session), matching this\n # file's existing one-service-per-concern convention. Only mounts\n # `~/.pi-web` (read-only would be nicer, but the entrypoint's shared\n # seed/sync logic wants to write there once too) — no printer-dashboard,\n # no docker.sock, no SSH key, nothing this read-only server needs. No auth\n # (matching pi-web's own explicit LAN-only, no-auth choice on 8080) —\n # don't expose this port past the LAN boundary either.\n pi-web-factory-visualizer:\n image: ghcr.io/jmfederico/pi-web:latest\n container_name: pi-web-factory-visualizer\n entrypoint: /usr/local/bin/docker-entrypoint.sh\n # Hardcoded absolute path rather than $HOME: docker compose interpolates\n # bare $VAR/${VAR} in this YAML file using the HOST's own environment at\n # `docker compose up` time (caught live — first attempt resolved to the\n # box's own /home/chris, not the container's /home/piweb, before this\n # was ever reachable at runtime), not the container's shell at runtime.\n # $$HOME (compose's own escape syntax) would also work; this is simpler\n # and matches this image's one fixed runtime user (piweb) always.\n command: [\"sh\", \"-c\", \"bun /home/piweb/pi-web-factory/visualizer/server.ts\"]\n restart: unless-stopped\n labels:\n com.local-ai-machine.always-up: \"true\"\n ports:\n - \"8090:8090\"\n volumes:\n - ~/.pi-web:/home/piweb/.pi-web\n environment:\n - PI_CODING_AGENT_DIR=/home/piweb/.pi-web\n - LITELLM_MASTER_KEY=${LITELLM_MASTER_KEY}\n # cli.ts (M-068) writes the real trace db here, under the same\n # bind-mounted, persistent config dir every pi-web-factory run inside\n # jmfederico-pi-web uses — NOT the visualizer's own co-located\n # default, which would point at the baked-in code copy instead.\n - PI_WEB_FACTORY_VISUALIZER_DB_PATH=/home/piweb/.pi-web/pi-web-factory-data/factory.db\n depends_on:\n - jmfederico-pi-web\n\nvolumes:\n searxng-cache:\n litellm_postgres_data:\n vllm-qwen3.6-35b-a3b-cache:\n vllm-qwen3.5-4b-cache:\n vllm-qwen3.6-27b-cache:\n vllm-glm-4.7-flash-awq-cache:\n vllm-qwen3-coder-next-gptq4bit-cache:\n vllm-gpt-oss-20b-cache:\n vllm-gpt-oss-120b-cache:\n vllm-qwen3.5-122b-a10b-awq4bit-cache:\n vllm-gemma-4-26b-a4b-it-cache:\n vllm-gemma-4-31b-it-cache:\n vllm-qwen2.5-vl-7b-instruct-cache:\n vllm-north-mini-code-1.0-w4a16-cache:\n open_webui_data:\n grafana_data:\n ollama_data:\n\nnetworks:\n default:\n driver: bridge\n driver_opts:\n com.docker.network.bridge.name: br-localai\n"}}}}