1. llama-cpp compose entrypoint regression (introduced in 8f103f3):
`exec llama-server` assumed PATH includes the binary, but the
ggml-org/llama.cpp:server-cuda image stores it at /app/llama-server.
Container hit a restart loop with `exec: llama-server: not found`
when we tried to recreate after Gemma 4 validation window. Fixed
to `exec /app/llama-server`. Affects ALL composes that mount this
pattern; verified production Qwen3.6-27B llama-cpp container now
restarts cleanly.
2. Gemma 4 fp8 PTH was the wrong default for Ampere — should be INT8 PTH:
Phase 2 boot crashed with:
ValueError: type fp8e4nv not supported in this architecture.
The supported fp8 dtypes are ('fp8e4b15', 'fp8e5')
This is a hardware capability gap on sm_86. Per-token-head fp8 KV
uses Triton kernels with `fp8e4nv` storage; Ampere doesn't implement
that fp8 variant (only fp8e4b15 / fp8e5). Ada/Blackwell users have
fp8e4nv support; Ampere users don't.
The right Ampere unblock for Gemma 4 long-context is `int8_per_token_head`,
which dispatches to standard PyTorch torch.int8 ops (not Triton fp8).
PR #40391's whole purpose was unblocking the per-token-head KV family
regardless of underlying dtype — the page-size mismatch fix applies to
INT8 PTH exactly the same way it applies to FP8 PTH.
Same memory savings (1 byte/element vs bf16's 2 bytes) → same ~120K
ctx target. Different precision profile (INT8 has better near-zero
precision, narrower dynamic range than e5m2).
Updated:
- Default `--kv-cache-dtype` from `fp8_per_token_head` to `int8_per_token_head`
- Header comment with full hardware compatibility table (sm_86/89/90/120)
- Filename retained as `gemma-mtp-fp8.yml` for git history; could rename
to `gemma-mtp-pth.yml` (per-token-head, hardware-agnostic) in a
follow-up if the misnomer becomes confusing
Live validation pending — needs a fresh Phase 2 boot test with INT8 PTH
to confirm it actually runs cleanly on this rig + benchmark at extended
context. Will batch with another Qwen-down validation window.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
126 lines
5.7 KiB
YAML
126 lines
5.7 KiB
YAML
# Qwen3.6-27B on llama.cpp — single 3090, full 262K context, vision on.
|
|
#
|
|
# The "easy mode" path. No Genesis patches, no AutoRound, no patched vLLM
|
|
# fork — just a stock llama.cpp Docker image and a single GGUF file.
|
|
#
|
|
# Showcase: vLLM single-card caps at 192K opt-in (with prefill caveats);
|
|
# this hits 262K (the model's architectural max) on a stock 3090 thanks to
|
|
# GGUF + q4_0 KV. The KV-cache type is the unlock — fp16 doesn't fit at
|
|
# all, q8 fits but runs much slower on Ampere; q4_0 fits at full speed.
|
|
#
|
|
# Config:
|
|
# - GGUF: Qwen3.6-27B-UD-Q3_K_XL (Unsloth dynamic, ~14.5 GB)
|
|
# - mmproj: F16 (vision projector, ~0.8 GB)
|
|
# - Context: 262144 (model max)
|
|
# - KV: q4_0 K + q4_0 V
|
|
# - Slots: 1 (single concurrent request; switch to docker-compose.concurrent.yml for 4)
|
|
# - FA: on
|
|
#
|
|
# VRAM budget on 24 GB:
|
|
# weights (Q3_K_XL): ~14.5 GB
|
|
# KV at 262K (q4_0 K+V): ~4.5 GB
|
|
# mmproj F16: ~0.8 GB
|
|
# total: ~19.8 GB
|
|
# headroom: ~4 GB for prompt + activation peaks
|
|
#
|
|
# Bench (canonical narrative + code prompt) numbers — see BENCHMARKS.md.
|
|
#
|
|
# Quick start:
|
|
# 1. Get the GGUF + mmproj:
|
|
# hf download unsloth/Qwen3.6-27B-GGUF \
|
|
# --include "Qwen3.6-27B-UD-Q3_K_XL.gguf" "mmproj-F16.gguf" \
|
|
# --local-dir /mnt/models/gguf/qwen3.6-27b/unsloth-q3kxl
|
|
# (mmproj sometimes ships at the repo root rather than under unsloth-q3kxl;
|
|
# the MMPROJ env var below points at the canonical sidecar location.)
|
|
# 2. From this directory:
|
|
# MODEL_DIR=/mnt/models/gguf docker compose up -d
|
|
# 3. curl http://localhost:8020/v1/models → should list the model.
|
|
#
|
|
# Override defaults via .env or shell:
|
|
# MODEL_DIR host dir to mount as /models (default: ../../../../models-cache for repo, /mnt/models/gguf on this stack)
|
|
# GGUF_FILE path under /models (default: qwen3.6-27b/unsloth-q3kxl/Qwen3.6-27B-UD-Q3_K_XL.gguf)
|
|
# MMPROJ_FILE path under /models (default: qwen3.6-27b/mmproj-F16.gguf)
|
|
# CTX_SIZE total KV pool (default: 262144)
|
|
# KV_TYPE K and V quant type (default: q4_0)
|
|
# REASONING_FORMAT reasoning channel routing (default: none — for opencode/IDE-agent compat)
|
|
# Override to `auto` to get separate `reasoning_content` field
|
|
# (Qwen3.6 thinking trace) — useful for clients that render
|
|
# reasoning_content (most don't). Issue: club-3090#97.
|
|
# DISABLE_THINKING set to 1 in .env to add `--chat-template-kwargs '{"enable_thinking":false}'`
|
|
# which forces empty <think></think> blocks in responses. Useful for
|
|
# clients (e.g. opencode) that display <think> content as the response.
|
|
# Tradeoff: applies to ALL clients on this server — Hermes/agents that
|
|
# use thinking lose reasoning capability. Issue: club-3090#97.
|
|
# PORT host port (default: 8020)
|
|
# CUDA_VISIBLE_DEVICES which GPU to use (default: 0)
|
|
#
|
|
# ─── opencode / IDE-agent compatibility (issue #97) ───
|
|
# `--reasoning-format none` is the default because Qwen3.6's thinking mode
|
|
# emits `<think>...</think>` blocks that llama.cpp's peg-native parser routes
|
|
# to the OpenAI `reasoning_content` field by default. opencode (and most simple
|
|
# OpenAI-compat clients) ignore `reasoning_content` and wait for `content`,
|
|
# causing indefinite client hangs even though the server returns 200 cleanly.
|
|
# `--reasoning-format none` collapses thinking into the content stream so all
|
|
# clients work. Power users wanting reasoning_content separation: set
|
|
# `REASONING_FORMAT=auto` in .env or shell.
|
|
|
|
services:
|
|
llama-cpp-qwen36-27b:
|
|
image: ghcr.io/ggml-org/llama.cpp:server-cuda
|
|
container_name: llama-cpp-qwen36-27b
|
|
restart: unless-stopped
|
|
ports:
|
|
- "${PORT:-8020}:8080"
|
|
volumes:
|
|
- "${MODEL_DIR:-../../../../models-cache}:/models:ro"
|
|
entrypoint:
|
|
- bash
|
|
- -c
|
|
- |
|
|
set -e
|
|
# DISABLE_THINKING=1 in compose/.env appends --chat-template-kwargs to disable
|
|
# Qwen3 thinking server-side. Forces the chat template to insert empty
|
|
# <think></think> blocks → output goes straight to the response. Useful for
|
|
# clients (e.g. opencode) that display <think> content as the response.
|
|
# Tradeoff: applies to ALL clients on this server instance — Hermes/agents that
|
|
# use thinking lose reasoning capability. See docs/HARDWARE.md and disc club-3090#97.
|
|
# Note: $$VAR is YAML-escape for $VAR (compose passes literal $ to bash).
|
|
EXTRA_ARGS=()
|
|
if [ "$${DISABLE_THINKING:-0}" = "1" ]; then
|
|
EXTRA_ARGS+=("--chat-template-kwargs" '{"enable_thinking":false}')
|
|
echo "[entrypoint] DISABLE_THINKING=1 — chat template will produce empty <think></think>"
|
|
fi
|
|
exec /app/llama-server "$$@" "$${EXTRA_ARGS[@]}"
|
|
- --
|
|
command:
|
|
- --host
|
|
- 0.0.0.0
|
|
- --port
|
|
- "8080"
|
|
- -m
|
|
- /models/${GGUF_FILE:-qwen3.6-27b/unsloth-q3kxl/Qwen3.6-27B-UD-Q3_K_XL.gguf}
|
|
- --mmproj
|
|
- /models/${MMPROJ_FILE:-qwen3.6-27b/mmproj-F16.gguf}
|
|
- -c
|
|
- ${CTX_SIZE:-262144}
|
|
- -ngl
|
|
- "99"
|
|
- -fa
|
|
- "on"
|
|
- --cache-type-k
|
|
- ${KV_TYPE:-q4_0}
|
|
- --cache-type-v
|
|
- ${KV_TYPE:-q4_0}
|
|
- -np
|
|
- "1"
|
|
- --jinja
|
|
- --reasoning-format
|
|
- ${REASONING_FORMAT:-none}
|
|
deploy:
|
|
resources:
|
|
reservations:
|
|
devices:
|
|
- driver: nvidia
|
|
device_ids: ["${CUDA_VISIBLE_DEVICES:-0}"]
|
|
capabilities: [compute, utility]
|