Merge pull request 'Fix llama-server-fast context-size exhaustion breaking Auto Mode' (#49) from fix-fastmodel-context-size into main
Reviewed-on: #49
This commit was merged in pull request #49.
This commit is contained in:
+15
-4
@@ -116,11 +116,22 @@ LLAMA_FAST_MODEL_FILE=Qwen3-4B-Instruct-2507-UD-Q8_K_XL.gguf
|
|||||||
# Same reasoning as LLAMA_GPU_LAYERS above — full GPU offload, this model
|
# Same reasoning as LLAMA_GPU_LAYERS above — full GPU offload, this model
|
||||||
# is dense too.
|
# is dense too.
|
||||||
LLAMA_FAST_GPU_LAYERS=999
|
LLAMA_FAST_GPU_LAYERS=999
|
||||||
# Classifier transcripts are truncated/bounded by qwen-code itself (see its
|
# --ctx-size is the TOTAL across every LLAMA_FAST_PARALLEL slot, not per
|
||||||
# own Auto Mode docs) — no need for anywhere near the 27B's huge context.
|
# request — same halving already called out for the main model above.
|
||||||
# 8192 keeps this instance's KV cache negligible.
|
# Was PARALLEL=2, silently halving this to 4096/slot — too small: a real
|
||||||
|
# classifier call (hints + environment + recent tool-call history) hit
|
||||||
|
# "exceeds the available context size (4096 tokens)" in practice, which
|
||||||
|
# qwen-code surfaces as "Auto Mode couldn't classify this action
|
||||||
|
# (Classifier stage 1 unavailable)" — see issue #5. Fixed by dropping to
|
||||||
|
# a single slot instead of raising ctx-size (no extra VRAM, and this
|
||||||
|
# service doesn't need concurrent classifier calls the way the main
|
||||||
|
# model needs concurrent chat sessions) — the full 8192 now goes to the
|
||||||
|
# one slot. If hints.allow/softDeny/hardDeny ever approach their
|
||||||
|
# 50-entries-each ceiling, raise LLAMA_FAST_CTX_SIZE instead — qwen-code
|
||||||
|
# caps those at 200 chars x 150 entries plus 40,000 chars of
|
||||||
|
# historical-action context, which can exceed 8192 tokens worst-case.
|
||||||
LLAMA_FAST_CTX_SIZE=8192
|
LLAMA_FAST_CTX_SIZE=8192
|
||||||
LLAMA_FAST_PARALLEL=2
|
LLAMA_FAST_PARALLEL=1
|
||||||
|
|
||||||
# --- ComfyUI diffusion model (Qwen-Image, FP8 — see docs/research/
|
# --- ComfyUI diffusion model (Qwen-Image, FP8 — see docs/research/
|
||||||
# image-generation-model-choice.md and issue #42) ---
|
# image-generation-model-choice.md and issue #42) ---
|
||||||
|
|||||||
@@ -4,3 +4,4 @@
|
|||||||
# to be committed to this repo.
|
# to be committed to this repo.
|
||||||
data/
|
data/
|
||||||
.leankg/
|
.leankg/
|
||||||
|
.cache/
|
||||||
|
|||||||
+1
-1
@@ -70,7 +70,7 @@ services:
|
|||||||
--port 8080
|
--port 8080
|
||||||
--n-gpu-layers ${LLAMA_FAST_GPU_LAYERS:-999}
|
--n-gpu-layers ${LLAMA_FAST_GPU_LAYERS:-999}
|
||||||
--ctx-size ${LLAMA_FAST_CTX_SIZE:-8192}
|
--ctx-size ${LLAMA_FAST_CTX_SIZE:-8192}
|
||||||
--parallel ${LLAMA_FAST_PARALLEL:-2}
|
--parallel ${LLAMA_FAST_PARALLEL:-1}
|
||||||
--flash-attn on
|
--flash-attn on
|
||||||
--cache-type-k q8_0
|
--cache-type-k q8_0
|
||||||
--cache-type-v q8_0
|
--cache-type-v q8_0
|
||||||
|
|||||||
@@ -4,6 +4,18 @@
|
|||||||
# exist, pulls, validates, rebuilds/re-pulls images, and recreates only what
|
# exist, pulls, validates, rebuilds/re-pulls images, and recreates only what
|
||||||
# changed — safe to run any time, including with nothing to do.
|
# changed — safe to run any time, including with nothing to do.
|
||||||
#
|
#
|
||||||
|
# Tunable config values (LLAMA_*, ports, timeouts — anything with a real
|
||||||
|
# default in .env.example) are synced from .env.example every run. A value
|
||||||
|
# already matching is left alone silently. A value that DIFFERS from the
|
||||||
|
# server's current .env is a conflict: interactively, you're shown every
|
||||||
|
# conflict on one screen (via gum) and choose which to accept — unpicked
|
||||||
|
# keys keep the server's current value. Non-interactively (no TTY — cron,
|
||||||
|
# CI, piped), any conflict is a hard error unless --force is passed, which
|
||||||
|
# accepts every new value automatically. Secrets and host-resolved values
|
||||||
|
# (blank in .env.example — OMNIROUTE_*_SECRET/_KEY/_SALT/_PASSWORD,
|
||||||
|
# SEARXNG_LAN_IP, COMFYUI_PUID/PGID, HOST_VIDEO_GID/RENDER_GID) are never
|
||||||
|
# touched by this — they keep going through set_if_blank as before.
|
||||||
|
#
|
||||||
# omniroute's own routing/provider config (llama-server, search) lives in
|
# omniroute's own routing/provider config (llama-server, search) lives in
|
||||||
# its dashboard, not a checked-in file like the old litellm-config.yaml —
|
# its dashboard, not a checked-in file like the old litellm-config.yaml —
|
||||||
# see issue #31 and docs/proxy-key-onboarding.md.
|
# see issue #31 and docs/proxy-key-onboarding.md.
|
||||||
@@ -14,6 +26,14 @@
|
|||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
cd "$(dirname "$0")/.."
|
cd "$(dirname "$0")/.."
|
||||||
|
|
||||||
|
FORCE=false
|
||||||
|
for arg in "$@"; do
|
||||||
|
case "$arg" in
|
||||||
|
--force) FORCE=true ;;
|
||||||
|
*) echo "Usage: $0 [--force]" >&2; exit 1 ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
# Must run before anything else touches a file this script itself reads
|
# Must run before anything else touches a file this script itself reads
|
||||||
# (docker-compose.yml, .env.example, this script's own remaining lines) —
|
# (docker-compose.yml, .env.example, this script's own remaining lines) —
|
||||||
# a self-updating script isn't guaranteed atomic against its own file
|
# a self-updating script isn't guaranteed atomic against its own file
|
||||||
@@ -27,6 +47,106 @@ git pull --ff-only
|
|||||||
|
|
||||||
[ -f .env ] || cp .env.example .env
|
[ -f .env ] || cp .env.example .env
|
||||||
|
|
||||||
|
echo "==> syncing tracked config values from .env.example"
|
||||||
|
# ponytail: gum (charmbracelet/gum) is a single static binary, fetched as a
|
||||||
|
# release tarball — no build step, no package-manager dependency. Cached
|
||||||
|
# under .cache/gum/ (gitignored) so repeat runs don't re-download. Release
|
||||||
|
# asset naming (gum_<ver>_Linux_<arch>.tar.gz) follows charm's standard
|
||||||
|
# goreleaser convention but hasn't been exercised against a real download
|
||||||
|
# on this exact host yet — if it 404s, check
|
||||||
|
# https://github.com/charmbracelet/gum/releases for the current naming.
|
||||||
|
GUM_VERSION="0.14.5"
|
||||||
|
GUM_DIR="$(pwd)/.cache/gum"
|
||||||
|
GUM_BIN="$GUM_DIR/gum"
|
||||||
|
ensure_gum() {
|
||||||
|
command -v gum >/dev/null 2>&1 && { echo "gum"; return; }
|
||||||
|
[ -x "$GUM_BIN" ] && { echo "$GUM_BIN"; return; }
|
||||||
|
mkdir -p "$GUM_DIR"
|
||||||
|
local arch tmpdir url
|
||||||
|
case "$(uname -m)" in
|
||||||
|
x86_64) arch="x86_64" ;;
|
||||||
|
aarch64|arm64) arch="arm64" ;;
|
||||||
|
*) echo "no gum build for $(uname -m), falling back to plain prompts" >&2; echo ""; return ;;
|
||||||
|
esac
|
||||||
|
url="https://github.com/charmbracelet/gum/releases/download/v${GUM_VERSION}/gum_${GUM_VERSION}_Linux_${arch}.tar.gz"
|
||||||
|
tmpdir="$(mktemp -d)"
|
||||||
|
if curl -fsSL "$url" | tar -xz -C "$tmpdir" 2>/dev/null; then
|
||||||
|
find "$tmpdir" -name gum -type f -exec cp {} "$GUM_BIN" \;
|
||||||
|
chmod +x "$GUM_BIN" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
rm -rf "$tmpdir"
|
||||||
|
[ -x "$GUM_BIN" ] && echo "$GUM_BIN" || echo ""
|
||||||
|
}
|
||||||
|
|
||||||
|
# Collect every key where .env.example has a real (non-blank) default:
|
||||||
|
# missing from .env -> just add it (no conflict, nothing to decide);
|
||||||
|
# present and identical -> leave alone silently; present and different ->
|
||||||
|
# a conflict to resolve below.
|
||||||
|
conflict_keys=()
|
||||||
|
conflict_old=()
|
||||||
|
conflict_new=()
|
||||||
|
while IFS='=' read -r key value; do
|
||||||
|
[ -n "$value" ] || continue
|
||||||
|
current="$(grep -E "^${key}=" .env | head -1 | cut -d= -f2-)"
|
||||||
|
if ! grep -qE "^${key}=" .env; then
|
||||||
|
echo "${key}=${value}" >> .env
|
||||||
|
elif [ "$current" != "$value" ]; then
|
||||||
|
conflict_keys+=("$key")
|
||||||
|
conflict_old+=("$current")
|
||||||
|
conflict_new+=("$value")
|
||||||
|
fi
|
||||||
|
done < <(grep -E '^[A-Za-z_][A-Za-z0-9_]*=.+' .env.example)
|
||||||
|
|
||||||
|
if [ "${#conflict_keys[@]}" -gt 0 ]; then
|
||||||
|
if [ "$FORCE" = true ]; then
|
||||||
|
for i in "${!conflict_keys[@]}"; do
|
||||||
|
key="${conflict_keys[$i]}"; new="${conflict_new[$i]}"
|
||||||
|
sed -i "s|^${key}=.*|${key}=${new}|" .env
|
||||||
|
echo "${key}: ${conflict_old[$i]} -> ${new} (--force)"
|
||||||
|
done
|
||||||
|
elif [ ! -t 0 ] || [ ! -t 1 ]; then
|
||||||
|
echo "ERROR: ${#conflict_keys[@]} config value(s) in .env differ from .env.example, and this isn't an interactive terminal:" >&2
|
||||||
|
for i in "${!conflict_keys[@]}"; do
|
||||||
|
echo " ${conflict_keys[$i]}: ${conflict_old[$i]} (current) vs ${conflict_new[$i]} (.env.example)" >&2
|
||||||
|
done
|
||||||
|
echo "Re-run interactively to choose per-key, or pass --force to accept every new value." >&2
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
gum_bin="$(ensure_gum)"
|
||||||
|
labels=()
|
||||||
|
for i in "${!conflict_keys[@]}"; do
|
||||||
|
labels+=("${conflict_keys[$i]}: ${conflict_old[$i]} -> ${conflict_new[$i]}")
|
||||||
|
done
|
||||||
|
if [ -n "$gum_bin" ]; then
|
||||||
|
selected="$(printf '%s\n' "${labels[@]}" | "$gum_bin" choose --no-limit --selected "$(printf '%s\n' "${labels[@]}" | paste -sd,)" --header "Config differs from .env.example — selected keys take the new value, unselected keep the server's current value:")"
|
||||||
|
else
|
||||||
|
# ponytail: plain-bash fallback if gum couldn't be fetched (offline,
|
||||||
|
# unsupported arch) — same one-screen-of-conflicts idea, cruder UI.
|
||||||
|
echo "Config differs from .env.example. Enter space-separated numbers to KEEP the server's current value (all others take the new value), or press enter to take every new value:"
|
||||||
|
for i in "${!conflict_keys[@]}"; do
|
||||||
|
echo " $((i+1))) ${labels[$i]}"
|
||||||
|
done
|
||||||
|
read -r -p "> " keep_nums
|
||||||
|
selected=""
|
||||||
|
for i in "${!conflict_keys[@]}"; do
|
||||||
|
case " $keep_nums " in
|
||||||
|
*" $((i+1)) "*) ;;
|
||||||
|
*) selected="${selected}${labels[$i]}"$'\n' ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
for i in "${!conflict_keys[@]}"; do
|
||||||
|
key="${conflict_keys[$i]}"; new="${conflict_new[$i]}"
|
||||||
|
if printf '%s\n' "$selected" | grep -qxF "${labels[$i]}"; then
|
||||||
|
sed -i "s|^${key}=.*|${key}=${new}|" .env
|
||||||
|
echo "${key}: ${conflict_old[$i]} -> ${new}"
|
||||||
|
else
|
||||||
|
echo "${key}: kept ${conflict_old[$i]} (server value)"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
# Handles all three cases: the KEY=value line is missing entirely (.env
|
# Handles all three cases: the KEY=value line is missing entirely (.env
|
||||||
# predates that var being added to .env.example — sed can't fix what isn't
|
# predates that var being added to .env.example — sed can't fix what isn't
|
||||||
# there, so this appends it), present but blank, or already set.
|
# there, so this appends it), present but blank, or already set.
|
||||||
|
|||||||
Reference in New Issue
Block a user