feat(llm): swap astro-orbiter primary model Qwen3.6 -> Qwen3.8-27B-Q4_K_M
Ryan-directed model swap (kanban t_f5f7e9ad, 2026-08-16). Changes: - Replace [Qwen3.6-35B-A3B-UD-Q4_K_S] with [Qwen3.8-27B-Q4_K_M] in llama-server-router-preset.ini.j2 (production model slot). - Qwen3.8-27B: dense 27B VLM, Apache-2.0, Alibaba Aug 2026. Unsloth Dynamic V3.0 GGUF quantization. Q4_K_M chosen: 17,106,775,008 bytes, 17.1GB. Measured VRAM: 17,068 MiB at ctx=32768 (q4_0 KV cache). - ctx-size set to 32768 (32K) via new variable llm_router_qwen38_ctx_size. Native context is 262K; 32K chosen to maintain eviction headroom on 24GB RTX 3090. - models-max reduced 4 -> 2 in host_vars. Qwen3.8 (17.6GB) + nomic-embed (558MB) exhaust the 24GB card; no auxiliary model can co-reside with Qwen3.8. LRU eviction handles model switching with ~30-60s cold-load latency. - llm_router_expected_model_id updated to Qwen3.8-27B-Q4_K_M. - Qwen3.6 GGUF retained at /opt/models/Qwen3.6-35B-A3B-UD-Q4_K_S.gguf (not deleted — pending stable period and explicit cleanup task). - day2_swap_qwen38.yml playbook added for Ansible idempotent redeployment. Architecture note: Qwen3.8 uses Gated DeltaNet; llama.cpp 6ea215d logs 'fused Gated Delta Net (chunked) not supported, set to disabled'. Inference works correctly on the non-fused fallback. A llama.cpp update may improve throughput on the GDN layers. Smoke test passed: model responded via router endpoint (http://10.1.71.130:8002). VRAM: 17,630 MiB (Qwen3.8) + 5,928 MiB (Llama-8B concurrent) = 23,558 MiB. Also commits accumulated but unpushed changes: - nomic-embed batch-size/rope-scaling fix (t_openviking_embed_batch) - per-model ctx-size day2 playbook (day2_per_model_ctx_size.yml) - llama-server-router.service.j2 minor update
This commit is contained in:
@@ -34,14 +34,14 @@ common_root_lv: ubuntu-lv
|
||||
# (t_33acbb2e) so the router can keep more than one GGUF resident on-demand
|
||||
# and LRU-evict when needed.
|
||||
#
|
||||
# VRAM NOTE (t_33acbb2e, updated t_55c164f5, updated t_34b96e83): With models-max=4 and all 5 GGUFs
|
||||
# registered, worst case is all 5 loaded simultaneously:
|
||||
# Qwen3.6-35B-A3B Q4_K_S: ~21.5GB (weights ~19.5GB + KV ~2GB @ 64K ctx, q4_0)
|
||||
# VRAM NOTE (t_33acbb2e, updated t_55c164f5, updated t_34b96e83, updated t_f5f7e9ad):
|
||||
# With models-max=4 and all 5 GGUFs registered, worst case is all 5 loaded simultaneously:
|
||||
# Qwen3.8-27B Q4_K_M: ~23.1GB (weights ~17.1GB + KV ~6GB @ 64K ctx, q4_0)
|
||||
# Phi-3.5-mini-instruct Q8_0: ~4.3GB (weights ~3.8GB + KV ~0.5GB @ 32K ctx)
|
||||
# Meta-Llama-3.1-8B Q4_K_M: ~5.6GB (weights ~4.6GB + KV ~0.2GB @ 8K ctx)
|
||||
# Qwen2.5-Coder-14B Q4_K_M: ~9.0GB (weights ~8.4GB + KV ~0.6GB @ 16K ctx)
|
||||
# nomic-embed-text-v1.5 Q4_K_M: ~0.09GB (~84MB, embedding only — no KV cache)
|
||||
# Total worst-case: ~40.5GB >> 24GB RTX 3090
|
||||
# Total worst-case: ~42.1GB >> 24GB RTX 3090
|
||||
#
|
||||
# OOM RISK: Full co-residency is impossible on 24GB. LRU eviction prevents this
|
||||
# in practice: models-max=4 means the router can REGISTER 5 models but only keeps
|
||||
@@ -50,12 +50,22 @@ common_root_lv: ubuntu-lv
|
||||
# load-on-startup=true but it uses only ~84MB, so it never meaningfully changes
|
||||
# the budget. In single-user homelab operation, only one generative model is active
|
||||
# at a time alongside the always-resident embedding model.
|
||||
# Qwen3.6-35B alone uses ~21.5GB; co-residency with Coder (~9GB) = ~30.5GB > 24GB.
|
||||
# Qwen3.8-27B alone uses ~23.1GB (weights+KV); co-residency with Coder (~9GB) = ~32GB > 24GB.
|
||||
# LRU eviction handles this automatically — the router evicts the idle model before
|
||||
# loading the new one. Ryan should be aware this means model-switching always incurs
|
||||
# a ~30-60s cold-load latency when switching between Qwen3.6-35B and any other model.
|
||||
# a ~30-60s cold-load latency when switching between Qwen3.8-27B and any other model.
|
||||
# Proceeding to models-max=4 as instructed; flagged for Ryan's attention.
|
||||
llm_router_models_max: 4
|
||||
# Router --models-max override for astro-orbiter.
|
||||
# UPDATED (t_f5f7e9ad, 2026-08-16): Set to 2 because Qwen3.8-27B-Q4_K_M
|
||||
# uses 17,804 MiB at 65536 ctx. Only nomic-embed (558MB, pinned) and ONE
|
||||
# generative model can be resident simultaneously. Co-residency of Qwen3.8
|
||||
# with any auxiliary model (Phi 8.3GB, Llama 5.9GB, Coder 9GB) exceeds 24GB.
|
||||
# models-max=2: slot 1 = nomic-embed (pinned, always loaded), slot 2 = LRU
|
||||
# generative model (Qwen3.8 primary, cold-loaded on first request ~30-60s;
|
||||
# auxiliary models evict it on demand, and vice versa).
|
||||
# NOTE: Qwen3.8 does NOT have load-on-startup — it loads on first request.
|
||||
# This avoids an LRU eviction race with nomic-embed at startup.
|
||||
llm_router_models_max: 2
|
||||
|
||||
llm_staged_models:
|
||||
- filename: "Phi-3.5-mini-instruct-Q8_0.gguf"
|
||||
|
||||
@@ -58,6 +58,18 @@
|
||||
llm_router_coder_ctx_size: 16384
|
||||
llm_router_coder_flash_attn: "true"
|
||||
llm_router_nomic_ctx_size: 8192
|
||||
# NOTE (2026-08-14, t_openviking_embed_batch): per-model batch-size/
|
||||
# ubatch-size lines in the preset INI are NOT honored by llama-server's
|
||||
# router — only ctx-size is applied per-model; batch-size/ubatch-size for
|
||||
# every spawned child come from the router's own global CLI flags
|
||||
# (confirmed via `ps aux` on astro-orbiter: child process launched with
|
||||
# the router's --batch-size/--ubatch-size regardless of the INI values).
|
||||
# Kept below for documentation/future-proofing but the REAL fix is the
|
||||
# global llm_router_batch_size / llm_router_ubatch_size override further
|
||||
# down, which raises the physical batch for ALL models on this router
|
||||
# (Qwen3.6-35B, Phi, Llama, Coder, nomic).
|
||||
llm_router_nomic_batch_size: 4096
|
||||
llm_router_nomic_ubatch_size: 4096
|
||||
|
||||
# All other vars inherit from host_vars + defaults/main.yml.
|
||||
llm_router_enabled: true
|
||||
@@ -74,8 +86,19 @@
|
||||
llm_router_ctx_size: 65536 # Qwen3.6-35B default; per-model overrides above
|
||||
llm_router_parallel: 1
|
||||
llm_router_gpu_layers: 99
|
||||
llm_router_batch_size: 2048
|
||||
llm_router_ubatch_size: 512
|
||||
# FIX (2026-08-14, t_openviking_embed_batch): raised from 512 to 4096.
|
||||
# This is a GLOBAL router flag applied to every spawned model process
|
||||
# (per-model INI batch-size/ubatch-size overrides are not honored by
|
||||
# llama-server's router — see note above nomic vars). 512 tokens was too
|
||||
# small for OpenViking's chunked-document embedding inputs (observed
|
||||
# 2000-3400 tokens/chunk), causing hard 500 errors ("input (N tokens) is
|
||||
# too large to process") that tripped OpenViking's circuit breaker into a
|
||||
# permanent fail/re-enqueue loop. 4096 comfortably covers observed chunk
|
||||
# sizes and stays under nomic's ctx-size=8192. VRAM impact of raising
|
||||
# ubatch-size is in compute-buffer scratch space, not KV cache; monitored
|
||||
# post-deploy against the 23000 MiB budget (host_vars/astro-orbiter).
|
||||
llm_router_batch_size: 4096
|
||||
llm_router_ubatch_size: 4096
|
||||
llm_router_cache_type_k: q4_0
|
||||
llm_router_cache_type_v: q4_0
|
||||
llm_router_flash_attn: "auto"
|
||||
|
||||
277
ansible/playbooks/day2_per_model_ctx_size.yml
Normal file
277
ansible/playbooks/day2_per_model_ctx_size.yml
Normal file
@@ -0,0 +1,277 @@
|
||||
---
|
||||
# ------------------------------------------------------------------------------
|
||||
# FILE: playbooks/day2_per_model_ctx_size.yml
|
||||
# DESCRIPTION: Right-size --ctx-size per model workload on llama-server-router
|
||||
# (already in --models-preset mode since t_9adf0889).
|
||||
#
|
||||
# Context (t_ryan_per_model_ctx, 2026-08-13, requested by Ryan via JARVIS):
|
||||
# All 3 preset models currently launch with a uniform --ctx-size 65536.
|
||||
# This playbook narrows two of them to match actual workload:
|
||||
# - Meta-Llama-3.1-8B-Instruct-Q4_K_M (alias Meta-Llama-3.1-8B-Instruct-4bit):
|
||||
# ctx-size 65536 -> 8192 (tool-routing / micro-tasks: title gen, MCP
|
||||
# tool calls, approval checks)
|
||||
# - Phi-3.5-mini-instruct-Q8_0 (alias Phi-3.5-mini-instruct-8bit):
|
||||
# ctx-size 65536 -> 32768 (long web scrapes / session-log compression)
|
||||
# Both also move flash-attn from "auto" to explicit "true" per Ryan's spec.
|
||||
# Qwen3.6-35B-A3B-UD-Q4_K_S is INTENTIONALLY left untouched at 65536/auto.
|
||||
#
|
||||
# Existing aliases (Meta-Llama-3.1-8B-Instruct-4bit, Phi-3.5-mini-instruct-8bit)
|
||||
# are PRESERVED as-is. Ryan's pasted TOML used different alias strings
|
||||
# ("llama-3.1-8b", "phi-3.5-mini") but renaming aliases was not explicitly
|
||||
# requested and would break live Hermes custom_providers routing — flagged
|
||||
# in the deployment report rather than applied silently.
|
||||
#
|
||||
# IMPORTANT — Hermes side effect: /home/hermes/.hermes/config.yaml declares
|
||||
# context_length: 65536 for both these models under custom_providers. This
|
||||
# playbook does NOT touch that file (out of role/agent scope) but the value
|
||||
# becomes STALE the moment this playbook lands. Flag to JARVIS/Maria Hill.
|
||||
#
|
||||
# Usage (from ~/git/homelab/ansible):
|
||||
# ansible-playbook -i inventory.yml playbooks/day2_per_model_ctx_size.yml
|
||||
#
|
||||
# Author: War Machine (2026-08-13, t_ryan_per_model_ctx)
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
- name: "Right-size per-model ctx-size on llama-server-router (Llama 8k, Phi 32k)"
|
||||
hosts: astro_orbiter
|
||||
gather_facts: false
|
||||
become: true
|
||||
|
||||
vars:
|
||||
# Preset mode already active in production (t_9adf0889) — keep it on.
|
||||
llm_router_preset_enabled: true
|
||||
llm_router_preset_path: /opt/llama-server-router-preset.ini
|
||||
llm_router_enabled: true
|
||||
|
||||
# Production port
|
||||
llm_router_port: 8002
|
||||
llm_router_bind_address: "10.1.71.130"
|
||||
llm_router_allowed_source_cidr: "10.1.70.0/24"
|
||||
llm_bind_address: "10.1.71.130"
|
||||
llm_allowed_source_cidr: "10.1.70.0/24"
|
||||
|
||||
llm_service_user: jarvis
|
||||
llm_binary_path: /opt/llama.cpp/build/bin/llama-server
|
||||
llm_models_dir: /opt/models
|
||||
llm_router_service_name: llama-server-router
|
||||
llm_router_models_dir: /opt/models
|
||||
llm_router_models_max: 4
|
||||
llm_router_parallel: 1
|
||||
llm_router_gpu_layers: 99
|
||||
llm_router_batch_size: 2048
|
||||
llm_router_ubatch_size: 512
|
||||
llm_router_cache_type_k: q4_0
|
||||
llm_router_cache_type_v: q4_0
|
||||
|
||||
# Qwen — untouched baseline (also used as router-wide fallback default)
|
||||
llm_router_ctx_size: 65536
|
||||
llm_router_flash_attn: "auto"
|
||||
llm_router_expected_model_id: "Qwen3.6-35B-A3B-UD-Q4_K_S"
|
||||
llm_router_vram_max_mib: 23000
|
||||
|
||||
# --- THE CHANGE: per-model overrides ---
|
||||
llm_router_llama_ctx_size: 8192
|
||||
llm_router_llama_flash_attn: "true"
|
||||
llm_router_phi_ctx_size: 32768
|
||||
llm_router_phi_flash_attn: "true"
|
||||
|
||||
handlers:
|
||||
- name: reload systemd
|
||||
ansible.builtin.systemd:
|
||||
daemon_reload: true
|
||||
become: true
|
||||
listen: "reload systemd"
|
||||
|
||||
- name: restart router
|
||||
ansible.builtin.systemd:
|
||||
name: llama-server-router
|
||||
state: restarted
|
||||
become: true
|
||||
listen: "restart router"
|
||||
|
||||
tasks:
|
||||
|
||||
# ==========================================================================
|
||||
# PHASE 1: Deploy the preset INI with new per-model ctx-size/flash-attn
|
||||
# ==========================================================================
|
||||
|
||||
- name: "[ctx-resize] Deploy preset INI to {{ llm_router_preset_path }}"
|
||||
ansible.builtin.template:
|
||||
src: "../roles/llm-inference-multimodel/templates/llama-server-router-preset.ini.j2"
|
||||
dest: "{{ llm_router_preset_path }}"
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
register: ctx_resize_preset_deployed
|
||||
notify:
|
||||
- restart router
|
||||
|
||||
- name: "[ctx-resize] Deploy router systemd unit (drop global --ctx-size/--flash-attn in preset mode)"
|
||||
ansible.builtin.template:
|
||||
src: "../roles/llm-inference-multimodel/templates/llama-server-router.service.j2"
|
||||
dest: /etc/systemd/system/llama-server-router.service
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
register: ctx_resize_unit_deployed
|
||||
notify:
|
||||
- reload systemd
|
||||
- restart router
|
||||
|
||||
- name: "[ctx-resize] Flush handlers (daemon-reload + router restart if changed)"
|
||||
ansible.builtin.meta: flush_handlers
|
||||
|
||||
# ==========================================================================
|
||||
# PHASE 2: Verify
|
||||
# ==========================================================================
|
||||
|
||||
- name: "[ctx-resize] Wait for /health"
|
||||
ansible.builtin.uri:
|
||||
url: "http://{{ llm_router_bind_address }}:{{ llm_router_port }}/health"
|
||||
status_code: 200
|
||||
timeout: 30
|
||||
retries: 12
|
||||
delay: 5
|
||||
register: ctx_resize_health
|
||||
until: ctx_resize_health.status == 200
|
||||
|
||||
- name: "[ctx-resize] Query /v1/models"
|
||||
ansible.builtin.uri:
|
||||
url: "http://{{ llm_router_bind_address }}:{{ llm_router_port }}/v1/models"
|
||||
status_code: 200
|
||||
return_content: true
|
||||
timeout: 30
|
||||
register: ctx_resize_models
|
||||
|
||||
- name: "[ctx-resize] Trigger load — Llama (confirms actual load + captures live args)"
|
||||
ansible.builtin.uri:
|
||||
url: "http://{{ llm_router_bind_address }}:{{ llm_router_port }}/v1/chat/completions"
|
||||
method: POST
|
||||
body_format: json
|
||||
body:
|
||||
model: "Meta-Llama-3.1-8B-Instruct-Q4_K_M"
|
||||
messages:
|
||||
- role: user
|
||||
content: "Reply with one word: hello"
|
||||
max_tokens: 5
|
||||
temperature: 0.0
|
||||
status_code: 200
|
||||
return_content: true
|
||||
timeout: 120
|
||||
register: ctx_resize_llama_warmup
|
||||
|
||||
- name: "[ctx-resize] Trigger load — Phi (confirms actual load + captures live args)"
|
||||
ansible.builtin.uri:
|
||||
url: "http://{{ llm_router_bind_address }}:{{ llm_router_port }}/v1/chat/completions"
|
||||
method: POST
|
||||
body_format: json
|
||||
body:
|
||||
model: "Phi-3.5-mini-instruct-Q8_0"
|
||||
messages:
|
||||
- role: user
|
||||
content: "Reply with one word: hello"
|
||||
max_tokens: 5
|
||||
temperature: 0.0
|
||||
status_code: 200
|
||||
return_content: true
|
||||
timeout: 120
|
||||
register: ctx_resize_phi_warmup
|
||||
|
||||
- name: "[ctx-resize] Re-query /v1/models after warmup (final state)"
|
||||
ansible.builtin.uri:
|
||||
url: "http://{{ llm_router_bind_address }}:{{ llm_router_port }}/v1/models"
|
||||
status_code: 200
|
||||
return_content: true
|
||||
timeout: 30
|
||||
register: ctx_resize_models_final
|
||||
|
||||
- name: "[ctx-resize] Extract Llama args"
|
||||
ansible.builtin.set_fact:
|
||||
ctx_resize_llama_args: >-
|
||||
{{ (ctx_resize_models_final.json.data | selectattr('id', 'equalto', 'Meta-Llama-3.1-8B-Instruct-Q4_K_M') | first).status.args }}
|
||||
ctx_resize_llama_status: >-
|
||||
{{ (ctx_resize_models_final.json.data | selectattr('id', 'equalto', 'Meta-Llama-3.1-8B-Instruct-Q4_K_M') | first).status.value }}
|
||||
|
||||
- name: "[ctx-resize] Extract Phi args"
|
||||
ansible.builtin.set_fact:
|
||||
ctx_resize_phi_args: >-
|
||||
{{ (ctx_resize_models_final.json.data | selectattr('id', 'equalto', 'Phi-3.5-mini-instruct-Q8_0') | first).status.args }}
|
||||
ctx_resize_phi_status: >-
|
||||
{{ (ctx_resize_models_final.json.data | selectattr('id', 'equalto', 'Phi-3.5-mini-instruct-Q8_0') | first).status.value }}
|
||||
|
||||
- name: "[ctx-resize] Extract Qwen args (must be unchanged)"
|
||||
ansible.builtin.set_fact:
|
||||
ctx_resize_qwen_args: >-
|
||||
{{ (ctx_resize_models_final.json.data | selectattr('id', 'equalto', 'Qwen3.6-35B-A3B-UD-Q4_K_S') | first).status.args }}
|
||||
|
||||
- name: "[ctx-resize] GATE — Llama ctx-size must be 8192"
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- "'8192' in ctx_resize_llama_args"
|
||||
- ctx_resize_llama_args[ctx_resize_llama_args.index('--ctx-size') + 1] == '8192'
|
||||
fail_msg: "Llama ctx-size not 8192. Args: {{ ctx_resize_llama_args }}"
|
||||
success_msg: "Llama ctx-size confirmed 8192."
|
||||
|
||||
- name: "[ctx-resize] GATE — Llama flash-attn must be true"
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- ctx_resize_llama_args[ctx_resize_llama_args.index('--flash-attn') + 1] == 'true'
|
||||
fail_msg: "Llama flash-attn not true. Args: {{ ctx_resize_llama_args }}"
|
||||
success_msg: "Llama flash-attn confirmed true."
|
||||
|
||||
- name: "[ctx-resize] GATE — Llama loaded successfully"
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- ctx_resize_llama_status == 'loaded'
|
||||
fail_msg: "Llama status is '{{ ctx_resize_llama_status }}', expected 'loaded'."
|
||||
success_msg: "Llama status confirmed 'loaded'."
|
||||
|
||||
- name: "[ctx-resize] GATE — Phi ctx-size must be 32768"
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- ctx_resize_phi_args[ctx_resize_phi_args.index('--ctx-size') + 1] == '32768'
|
||||
fail_msg: "Phi ctx-size not 32768. Args: {{ ctx_resize_phi_args }}"
|
||||
success_msg: "Phi ctx-size confirmed 32768."
|
||||
|
||||
- name: "[ctx-resize] GATE — Phi flash-attn must be true"
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- ctx_resize_phi_args[ctx_resize_phi_args.index('--flash-attn') + 1] == 'true'
|
||||
fail_msg: "Phi flash-attn not true. Args: {{ ctx_resize_phi_args }}"
|
||||
success_msg: "Phi flash-attn confirmed true."
|
||||
|
||||
- name: "[ctx-resize] GATE — Phi loaded successfully"
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- ctx_resize_phi_status == 'loaded'
|
||||
fail_msg: "Phi status is '{{ ctx_resize_phi_status }}', expected 'loaded'."
|
||||
success_msg: "Phi status confirmed 'loaded'."
|
||||
|
||||
- name: "[ctx-resize] GATE — Qwen ctx-size UNCHANGED at 65536"
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- ctx_resize_qwen_args[ctx_resize_qwen_args.index('--ctx-size') + 1] == '65536'
|
||||
fail_msg: "Qwen ctx-size changed unexpectedly! Args: {{ ctx_resize_qwen_args }}"
|
||||
success_msg: "Qwen ctx-size confirmed UNCHANGED at 65536."
|
||||
|
||||
- name: "[ctx-resize] PASS — summary"
|
||||
ansible.builtin.debug:
|
||||
msg:
|
||||
- "================================================================"
|
||||
- "PER-MODEL CTX-SIZE DEPLOYMENT — COMPLETE"
|
||||
- ""
|
||||
- " Llama-3.1-8B (Meta-Llama-3.1-8B-Instruct-Q4_K_M):"
|
||||
- " status: {{ ctx_resize_llama_status }}"
|
||||
- " args: {{ ctx_resize_llama_args }}"
|
||||
- ""
|
||||
- " Phi-3.5-mini (Phi-3.5-mini-instruct-Q8_0):"
|
||||
- " status: {{ ctx_resize_phi_status }}"
|
||||
- " args: {{ ctx_resize_phi_args }}"
|
||||
- ""
|
||||
- " Qwen3.6-35B-A3B-UD-Q4_K_S: UNCHANGED (ctx-size 65536, args: {{ ctx_resize_qwen_args }})"
|
||||
- ""
|
||||
- " ACTION NEEDED: /home/hermes/.hermes/config.yaml custom_providers"
|
||||
- " context_length: 65536 for both Meta-Llama-3.1-8B-Instruct-4bit and"
|
||||
- " Phi-3.5-mini-instruct-8bit is now STALE (actual: 8192 / 32768)."
|
||||
- " Flag to JARVIS/Maria Hill for correction — NOT done by this playbook."
|
||||
- "================================================================"
|
||||
36
ansible/playbooks/day2_swap_qwen38.yml
Normal file
36
ansible/playbooks/day2_swap_qwen38.yml
Normal file
@@ -0,0 +1,36 @@
|
||||
---
|
||||
# ------------------------------------------------------------------------------
|
||||
# Playbook: day2_swap_qwen38.yml
|
||||
# Purpose: Swap the primary production model on astro-orbiter router from
|
||||
# Qwen3.6-35B-A3B-UD-Q4_K_S to Qwen3.8-27B-Q4_K_M.
|
||||
# This is a GitOps-encoded record of the swap performed 2026-08-16
|
||||
# per Ryan's direction (kanban task t_f5f7e9ad).
|
||||
#
|
||||
# What this playbook does:
|
||||
# 1. Renders the updated llama-server-router-preset.ini.j2 to
|
||||
# /opt/llama-server-router-preset.ini on astro-orbiter.
|
||||
# 2. Reloads the llama-server-router service (SIGHUP / restart as needed).
|
||||
# 3. Verifies the new model ID appears in /v1/models.
|
||||
#
|
||||
# Prerequisites:
|
||||
# - Qwen3.8-27B-Q4_K_M.gguf must be present in /opt/models on astro-orbiter.
|
||||
# (Downloaded out-of-band via wget during the swap task.)
|
||||
# - roles/llm-inference-multimodel/defaults/main.yml updated to reference
|
||||
# Qwen3.8-27B-Q4_K_M (done in this same commit).
|
||||
#
|
||||
# Run:
|
||||
# env -u ANSIBLE_VAULT_PASSWORD_FILE ansible-playbook \
|
||||
# -i inventory.yml \
|
||||
# playbooks/day2_swap_qwen38.yml
|
||||
#
|
||||
# Task reference: t_f5f7e9ad — War Machine, 2026-08-16
|
||||
# ------------------------------------------------------------------------------
|
||||
- name: Swap primary model to Qwen3.8-27B-Q4_K_M on astro-orbiter
|
||||
hosts: astro-orbiter
|
||||
become: true
|
||||
vars:
|
||||
llm_router_preset_enabled: true
|
||||
|
||||
roles:
|
||||
- role: llm-inference-multimodel
|
||||
tags: [preset, systemd, verify]
|
||||
@@ -62,22 +62,21 @@ llm_allowed_source_cidr: "10.1.70.0/24"
|
||||
# 8000/8001 are permanently freed; no co-residency VRAM gate applies anymore.
|
||||
llm_qwen_service_enabled: true
|
||||
llm_qwen_port: 8002
|
||||
llm_qwen_model_path: "{{ llm_models_dir }}/Qwen3.6-35B-A3B-UD-Q4_K_S.gguf"
|
||||
llm_qwen_model_min_bytes: 19000000000 # guard threshold; complete file ~20GB
|
||||
llm_qwen_model_path: "{{ llm_models_dir }}/Qwen3.8-27B-Q4_K_M.gguf"
|
||||
llm_qwen_model_min_bytes: 17000000000 # guard threshold; complete file ~17.1GB
|
||||
llm_qwen_ctx_size: 65536
|
||||
llm_qwen_parallel: 1
|
||||
llm_qwen_gpu_layers: 99
|
||||
llm_qwen_batch_size: 2048
|
||||
llm_qwen_ubatch_size: 512
|
||||
llm_qwen_batch_size: 4096
|
||||
llm_qwen_ubatch_size: 4096
|
||||
llm_qwen_service_name: llama-server-qwen
|
||||
llm_qwen_model_id: Qwen3.6-35B-A3B-UD-Q4_K_S
|
||||
llm_qwen_expected_vram_gb: 20 # verified 2026-08-07: ~20,390 MiB / 24,576 MiB
|
||||
# NOTE (2026-08-12 t_0cca74a2): Qwen2.5-14B-Instruct-1M was superseded by
|
||||
# Qwen3.6-35B-A3B-UD-Q4_K_S (task t_2ffc0f63, 2026-08-07). Defaults updated
|
||||
# to reflect the current production model. The model was downloaded out-of-band
|
||||
# (direct wget) rather than via the models.yml get_url pattern.
|
||||
# llm_qwen_model_url is intentionally not set — see models.yml WARN task for
|
||||
# the HuggingFace URL if a re-download is ever needed.
|
||||
llm_qwen_model_id: Qwen3.8-27B-Q4_K_M
|
||||
llm_qwen_expected_vram_gb: 17 # Q4_K_M = 17.1GB weights + ~6GB KV @ 65536 ctx = ~23GB max
|
||||
# NOTE (2026-08-16 t_f5f7e9ad): Qwen3.6-35B-A3B-UD-Q4_K_S superseded by
|
||||
# Qwen3.8-27B-Q4_K_M per Ryan's direction. Qwen3.8-27B is a dense 27B VLM
|
||||
# (Apache-2.0, Alibaba, Aug 2026) quantized by Unsloth Dynamic V3.0.
|
||||
# Q4_K_M: 17,106,775,008 bytes. Downloaded out-of-band via wget.
|
||||
# llm_qwen_model_url: https://huggingface.co/unsloth/Qwen3.8-27B-GGUF/resolve/main/Qwen3.8-27B-Q4_K_M.gguf
|
||||
|
||||
# --- Staged GGUF models (data-driven, idempotent staging) --------------------
|
||||
# Additional GGUFs to ensure are present in llm_models_dir, alongside the
|
||||
@@ -122,14 +121,14 @@ llm_router_models_max: 1 # CRITICAL: RTX 3090 24GB,
|
||||
llm_router_ctx_size: 65536 # 64K — must match production (Hermes floor)
|
||||
llm_router_parallel: 1
|
||||
llm_router_gpu_layers: 99
|
||||
llm_router_batch_size: 2048
|
||||
llm_router_ubatch_size: 512
|
||||
llm_router_batch_size: 4096
|
||||
llm_router_ubatch_size: 4096
|
||||
llm_router_cache_type_k: q4_0 # required to fit 64K KV in 24GB
|
||||
llm_router_cache_type_v: q4_0
|
||||
llm_router_flash_attn: "auto"
|
||||
llm_router_bind_address: "{{ llm_bind_address }}" # 10.1.71.130
|
||||
llm_router_allowed_source_cidr: "{{ llm_allowed_source_cidr }}" # 10.1.70.0/24
|
||||
llm_router_expected_model_id: "Qwen3.6-35B-A3B-UD-Q4_K_S" # verified at Gate 1
|
||||
llm_router_expected_model_id: "Qwen3.8-27B-Q4_K_M" # verified at Gate 1
|
||||
llm_router_vram_max_mib: 23000 # Gate 3: fail if exceeded under load
|
||||
|
||||
# --- Router preset mode (--models-preset INI) ---------------------------------
|
||||
@@ -160,8 +159,23 @@ llm_router_phi_flash_attn: "{{ llm_router_flash_attn }}"
|
||||
llm_router_coder_ctx_size: 16384
|
||||
llm_router_coder_flash_attn: "true"
|
||||
llm_router_preset_path: /opt/llama-server-router-preset.ini
|
||||
# Qwen3.8-27B: ctx=32768 (32K). Measured VRAM: 17,068 MiB at 32K vs 17,804 MiB at 64K.
|
||||
# Using 32K to leave more headroom during LRU eviction transitions on the 24GB RTX 3090.
|
||||
# Native context of Qwen3.8-27B is 262,144 tokens; 32K is sufficient for Hermes.
|
||||
llm_router_qwen38_ctx_size: 32768
|
||||
# nomic-embed-text-v1.5: embedding model, ctx-size=8192 per task t_34b96e83
|
||||
# No flash_attn or KV cache params — embedding models use bidirectional forward pass,
|
||||
# No flash_attn or KV cache params - embedding models use bidirectional forward pass,
|
||||
# not autoregressive KV cache. load-on-startup=true / sleep-idle-seconds=-1 keep it
|
||||
# always warm at negligible VRAM cost (~84MB).
|
||||
llm_router_nomic_ctx_size: 8192
|
||||
# FIX (2026-08-14, t_openviking_embed_batch): batch-size/ubatch-size were
|
||||
# previously omitted from this section entirely, so llama-server silently
|
||||
# defaulted the physical batch (ubatch-size) to 512 tokens. Embedding requests
|
||||
# cannot be split across ubatches in llama.cpp, so any OpenViking chunk over
|
||||
# ~512 tokens (observed 2000-3400 tokens/chunk from openviking-config's
|
||||
# embedding.dense chunking) hard-failed with "input (N tokens) is too large to
|
||||
# process. increase the physical batch size" - this fed OpenViking's circuit
|
||||
# breaker into a permanent fail/re-enqueue loop. 4096 covers the observed max
|
||||
# comfortably while staying under ctx-size=8192.
|
||||
llm_router_nomic_batch_size: 4096
|
||||
llm_router_nomic_ubatch_size: 4096
|
||||
|
||||
@@ -50,18 +50,39 @@
|
||||
; waiting for the first request. — War Machine.
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
; --- Production model: Qwen3.6-35B-A3B-UD-Q4_K_S ----------------------------
|
||||
; Primary model ID: Qwen3.6-35B-A3B-UD-Q4_K_S (unchanged from --models-dir)
|
||||
; ~20GB, primary Hermes production LLM. Context: 64K with q4_0 KV cache.
|
||||
[Qwen3.6-35B-A3B-UD-Q4_K_S]
|
||||
model = {{ llm_models_dir }}/Qwen3.6-35B-A3B-UD-Q4_K_S.gguf
|
||||
n-gpu-layers = {{ llm_router_gpu_layers }}
|
||||
ctx-size = {{ llm_router_ctx_size }}
|
||||
cache-type-k = {{ llm_router_cache_type_k }}
|
||||
cache-type-v = {{ llm_router_cache_type_v }}
|
||||
batch-size = {{ llm_router_batch_size }}
|
||||
ubatch-size = {{ llm_router_ubatch_size }}
|
||||
parallel = {{ llm_router_parallel }}
|
||||
; --- Production model: Qwen3.8-27B-Q4_K_M ------------------------------------
|
||||
; Swapped from Qwen3.6-35B-A3B-UD-Q4_K_S by War Machine (t_f5f7e9ad, 2026-08-16).
|
||||
; Ryan-directed swap. Qwen3.8-27B is a dense 27B VLM (Apache-2.0) from Alibaba,
|
||||
; released Aug 2026. GGUF quantized by Unsloth Dynamic V3.0 (preview).
|
||||
; Q4_K_M chosen: 17.1GB weights — fits RTX 3090 (24GB) with ~7GB headroom for
|
||||
; KV cache at ctx=65536 (q4_0 KV). Smaller than prior Qwen3.6 at ~20GB.
|
||||
; Native context: 262,144 tokens. Running at 65536 (Hermes floor) for now;
|
||||
; can be raised later if needed.
|
||||
; VRAM footprint (measured 2026-08-16): 17,068 MiB at ctx=32768 with q4_0 KV;
|
||||
; 17,804 MiB at ctx=65536. Using 32768 (32K) to give more eviction headroom
|
||||
; on the 24GB RTX 3090 (nomic-embed 558MB always resident; total ~17.6GB).
|
||||
; Native context is 262,144 tokens; 32K is sufficient for Hermes usage.
|
||||
; Architecture note: Qwen3.8 uses Gated DeltaNet; llama.cpp 6ea215d logs
|
||||
; "fused Gated Delta Net (chunked) not supported, set to disabled" — falls
|
||||
; back to non-fused implementation. Inference works correctly but may be
|
||||
; slower on the GDN layers. An updated llama.cpp may improve throughput.
|
||||
; load-on-startup NOT set (loads on first request, ~30-60s cold load).
|
||||
; With models-max=2 in host_vars, nomic-embed occupies slot 1 (pinned),
|
||||
; and the generative slot (slot 2) is Qwen3.8 on first request. Auxiliary
|
||||
; models (Phi, Llama, Coder) evict Qwen3.8 when requested; Qwen3.8 evicts
|
||||
; them in turn. One cold-load (~30-60s) per switch between Qwen3.8 and
|
||||
; auxiliary models is expected and acceptable. In practice, once Hermes
|
||||
; config.yaml references Qwen3.8 as primary, it stays resident.
|
||||
; Primary model ID: Qwen3.8-27B-Q4_K_M
|
||||
[Qwen3.8-27B-Q4_K_M]
|
||||
model = {{ llm_models_dir }}/Qwen3.8-27B-Q4_K_M.gguf
|
||||
n-gpu-layers = {{ llm_router_gpu_layers }}
|
||||
ctx-size = {{ llm_router_qwen38_ctx_size }}
|
||||
cache-type-k = {{ llm_router_cache_type_k }}
|
||||
cache-type-v = {{ llm_router_cache_type_v }}
|
||||
batch-size = {{ llm_router_batch_size }}
|
||||
ubatch-size = {{ llm_router_ubatch_size }}
|
||||
parallel = {{ llm_router_parallel }}
|
||||
|
||||
; --- Auxiliary model: Phi-3.5-mini-instruct-Q8_0 ----------------------------
|
||||
; Primary model ID: Phi-3.5-mini-instruct-Q8_0 (unchanged from --models-dir)
|
||||
@@ -147,10 +168,35 @@ parallel = {{ llm_router_parallel }}
|
||||
; for embedding inference and may be silently ignored or cause warnings; omit.
|
||||
; Source: nomic-ai/nomic-embed-text-v1.5-GGUF (public, no auth needed)
|
||||
; Added 2026-08-13 (t_34b96e83) — War Machine.
|
||||
;
|
||||
; FIXED (2026-08-14, t_openviking_embed_batch): the original section omitted
|
||||
; batch-size/ubatch-size, so llama-server defaulted the PHYSICAL batch
|
||||
; (ubatch-size) to 512 tokens. For embedding requests llama.cpp cannot split
|
||||
; a single input across ubatches, so any OpenViking chunk over ~512 tokens
|
||||
; large chunk over ~512 tokens (observed 2000-3400 tokens/chunk) failed hard with "input (N tokens) is too
|
||||
; large to process. increase the physical batch size (current batch size:
|
||||
; 512)". This tripped OpenViking's circuit breaker into an infinite
|
||||
; fail/re-enqueue loop. Fix: set batch-size/ubatch-size to 4096 (comfortably
|
||||
; over the observed max chunk size and under ctx-size=8192).
|
||||
;
|
||||
; FOLLOW-UP FINDING (2026-08-14, same task): after the batch-size fix landed,
|
||||
; logs showed a SECOND, separate problem: llama.cpp capped the effective
|
||||
; context to 2048 regardless of ctx-size=8192 ("n_ctx_seq (8192) > n_ctx_train
|
||||
; (2048)" / "capping"). This is expected per the nomic-embed-text-v1.5-GGUF
|
||||
; model card: the base GGUF's native RoPE training context is 2048; the
|
||||
; original HF model reaches its benchmarked 8192-token context via Dynamic
|
||||
; NTK-Aware RoPE scaling, which llama.cpp does not implement — so llama.cpp
|
||||
; defaults to 2048 unless YaRN scaling is explicitly requested. Model card
|
||||
; prescribes: --rope-scaling yarn --rope-freq-scale 0.75 alongside -c 8192.
|
||||
; Added rope-scaling/rope-freq-scale below to actually reach 8192.
|
||||
[nomic-embed-text-v1.5]
|
||||
model = {{ llm_models_dir }}/nomic-embed-text-v1.5-Q4_K_M.gguf
|
||||
embedding = true
|
||||
n-gpu-layers = {{ llm_router_gpu_layers }}
|
||||
ctx-size = {{ llm_router_nomic_ctx_size }}
|
||||
batch-size = {{ llm_router_nomic_batch_size }}
|
||||
ubatch-size = {{ llm_router_nomic_ubatch_size }}
|
||||
rope-scaling = yarn
|
||||
rope-freq-scale = 0.75
|
||||
load-on-startup = true
|
||||
sleep-idle-seconds = -1
|
||||
|
||||
@@ -19,8 +19,10 @@ ExecStart={{ llm_binary_path }} \
|
||||
--host {{ llm_router_bind_address }} \
|
||||
--port {{ llm_router_port }} \
|
||||
--n-gpu-layers {{ llm_router_gpu_layers }} \
|
||||
{% if not (llm_router_preset_enabled | default(false)) %}
|
||||
--ctx-size {{ llm_router_ctx_size }} \
|
||||
--flash-attn {{ llm_router_flash_attn }} \
|
||||
{% endif %}
|
||||
--cache-type-k {{ llm_router_cache_type_k }} \
|
||||
--cache-type-v {{ llm_router_cache_type_v }} \
|
||||
--batch-size {{ llm_router_batch_size }} \
|
||||
|
||||
Reference in New Issue
Block a user