Compare commits

...
Author SHA1 Message Date
claude-botandClaude Opus 5 5023a5971b fix(llama-server): back to 16k — 32k per slot corrupts inference
ci/woodpecker/pr/ci-scripts-test Pipeline was successful
ci/woodpecker/pr/ci-manifests-test Pipeline was successful
ci/woodpecker/pr/pr-review Pipeline was successful
#767 broke the model. Asked to classify a document and answer with JSON
only, the server returned 1025 tokens of "!" against a 650-token prompt.
That is the KV cache emitting numbers rather than language, and it takes
everything downstream with it: Paperless ran past its 300 s timeout on
every document, Home Assistant got an empty completion once and a
hallucinated list of devices that do not exist another time.

None of that looked like a backend fault from the outside. The server
starts, /props reports the requested size, /health passes, and the
OpenAI response is well-formed with finish_reason "stop". Only the
generated text gives it away, which is why three separate symptoms got
blamed on prompt size, on tokenizer behaviour and on the RAG context
before anyone read the output.

The boundary is between the two changes rather than at either: #765 left
16384 per slot and produced coherent answers; #767 raised it to 32768 and
produced this. The same Vulkan/Mesa stack already fails at --ubatch-size
2048 with ErrorDeviceLost, so a ceiling here is not a surprise -- only
that it degrades quietly instead of erroring.

--parallel goes with it. Unset, slots return to auto, which is what keeps
--kv-unified enabled ("if number of slots is auto"), so the auto slots
share one buffer and each may draw the full 16384.

Home Assistant needed 18910 tokens for one Assist request and cannot have
them at this ceiling. That path is closed, and Paperless -- which fits in
16k and never needed more -- is what this restores.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 21:05:03 +02:00
k.renner 1951497175 Merge pull request 'feat(crowdsec): arm the bouncer everywhere, drop the per-host override' (#769) from arm-crowdsec-proxy-1 into master
ci/woodpecker/push/deploy-netbird-proxy Pipeline was successful
ci/woodpecker/cron/alert-monitor Pipeline was successful
Reviewed-on: k.renner/homelab#769
2026-09-02 18:57:09 +00:00
security-bot ad32ab566b chore(image-automation): bump ghcr.io/home-assistant/home-assistant:2026.9.0
ci/woodpecker/cron/issue-fix Pipeline was successful
ci/woodpecker/cron/alert-monitor Pipeline was successful
Updates managed by image-automation-controller.
2026-09-02 16:23:17 +00:00
claude-botandClaude Opus 5 b96260cef2 feat(crowdsec): arm the bouncer everywhere, drop the per-host override
ci/woodpecker/pr/ci-scripts-test Pipeline was successful
ci/woodpecker/pr/pr-review Pipeline was successful
proxy-2 has been enforcing since 14:21:54 without incident, so the role
default moves to nftables and the host_vars override goes away rather than
being duplicated for proxy-1. proxy-2's rendered config is unchanged by this,
so it will not restart; only proxy-1 does.

The staged rollout did its job and can be read off the deploy log:

  proxy-1: deployed in mode "dry-run"
  proxy-2: deployed in mode "nftables"

and only proxy-2 restarted, which showed up as a 1,887,882-byte startup pull
from its address alone.

The delivery path was tested in enforcing mode with a decision on 192.0.2.1
(TEST-NET-1): both hosts had the ban 3s later as a 205-byte delta, and the
revocation as a 203-byte delta 1-2s after deletion.

What is still not proven is that a packet is actually dropped, and the
measurement for that argues for finishing the rollout rather than waiting.
Requests from banned addresses reaching Traefik stopped after proxy-2 armed —
1 at 14:02, 3 at 14:07, 22 at 14:08, 1 at 14:18, none after — but 25 of those
27 arrived in a single burst beforehand, so at this volume that is consistent
with enforcement rather than evidence of it. With both hosts armed the claim
becomes falsifiable instead: no request from an address in the decision set
should reach Traefik at all, and a single one would prove otherwise.

dry-run remains available per host through inventory/host_vars, which is what
a newly added proxy should get before it enforces.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 16:28:25 +02:00
k.renner 67b0fece93 Merge pull request 'fix(ci): deploy the proxies when their inventory changes, not just their roles' (#768) from fix-proxy-deploy-triggers into master
ci/woodpecker/push/deploy-netbird-proxy Pipeline was successful
ci/woodpecker/cron/issue-fix Pipeline was successful
ci/woodpecker/cron/alert-monitor Pipeline was successful
Reviewed-on: k.renner/homelab#768
2026-09-02 14:19:21 +00:00
k.renner 91849e31ff Merge pull request 'fix(llama-server): --ctx-size is the total, so ask for 65536' (#767) from llama-ctx-fix into master
Reviewed-on: k.renner/homelab#767
2026-09-02 14:17:24 +00:00
claude-botandClaude Opus 5 021fd6a4ed docs(llama-server): cite the flag semantics instead of inferring them
ci/woodpecker/pr/ci-manifests-test Pipeline was successful
ci/woodpecker/pr/ci-scripts-test Pipeline was successful
ci/woodpecker/pr/pr-review Pipeline was successful
`llama-server --help` documents both halves of what #765 got wrong, and
it ships in the image:

    -np, --parallel N   number of server slots (default: -1, -1 = auto)
    -kvu, --kv-unified  use single unified KV buffer shared across all
                        sequences (default: enabled if number of slots
                        is auto)

So leaving --parallel unset did not merely pick a slot count, it enabled
a shared KV buffer that every slot could draw the full --ctx-size from.
That is why this server reported n_ctx_slot 16384 across four slots, and
the startup log said so outright: kv_unified = 'true'. Naming a slot
count switches that off and splits the context.

The value stays 65536. Only the reasoning in the comment changes, from an
inference drawn off two /props readings to the documented behaviour.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 16:16:16 +02:00
claude-botandClaude Opus 5 7e74b93fb2 fix(llama-server): --ctx-size is the total, so ask for 65536
ci/woodpecker/pr/ci-manifests-test Pipeline was successful
ci/woodpecker/pr/ci-scripts-test Pipeline was successful
ci/woodpecker/pr/pr-review Pipeline was successful
#765 claimed --ctx-size was per slot and halved the slot count to pay for
doubling it. Both halves of that were wrong. With --parallel set,
llama.cpp divides --ctx-size across the slots, so 32768 over two slots
gave 16384 each -- exactly what was there before -- and the Assist
request fails with the same message it did then:

    request (18910 tokens) exceeds the available context size (16384)

The flag does behave differently without --parallel, which is where the
wrong reading came from: the four-slot server reported n_ctx_slot equal
to --ctx-size rather than a quarter of it. Adding --parallel changed
that, and the memory reading confirms it -- the pod fell from 24979 MiB
to 21601 MiB, so total KV halved rather than holding steady.

65536 across two slots is 32768 each, and puts total KV back where it was
before #765 along with the memory that came with it.

Verify rather than assume this time:

    kubectl -n ai exec deploy/llama-server -- curl -sS \
      http://127.0.0.1:8080/props | jq .default_generation_settings.n_ctx

must report 32768.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 16:13:35 +02:00
4 changed files with 34 additions and 45 deletions
@@ -1,19 +0,0 @@
---
# Arms the CrowdSec firewall bouncer on THIS host only.
#
# The role defaults to dry-run for the whole netbird_proxy group, so the two
# members can be armed one at a time. proxy-2 goes first deliberately: it is
# the dual-stack member, while proxy-1 is IPv6-only and the one where every
# network-shaped problem has surfaced first — the dhcpcd/resolv.conf breakage,
# and the GitHub download that could not work at all.
#
# What flipping this does: the bouncer stops logging what it would block and
# creates two nftables tables (ip crowdsec, ip6 crowdsec6) with the decisions
# it holds. It touches no existing chain and sets no chain policy.
#
# Rollback is `systemctl stop crowdsec-firewall-bouncer` — a clean stop deletes
# both tables itself. Setting this back to dry-run and re-running the play does
# the same through the handler.
#
# Remove this file once proxy-1 follows and the role default moves to nftables.
crowdsec_bouncer_mode: nftables
@@ -28,14 +28,23 @@ crowdsec_bouncer_sha256: f86e4b72693549d99f40a9402abefb894108f047a3fbe6e72fada25
crowdsec_bouncer_api_url: https://crowdsec.renner.uno/
crowdsec_bouncer_api_key: "{{ vault_crowdsec_edge_bouncer_key }}"
# STARTS INERT, and that is the point.
# Enforcing. The bouncer creates two nftables tables (ip crowdsec, ip6
# crowdsec6) holding the decisions it pulled, touches no existing chain and
# sets no chain policy — SSH and :443 stay open unless the source is banned.
#
# dry-run pulls decisions and logs what it would block without creating a
# single nftables rule. The first bouncer to arm anywhere enforces ~24k CAPI
# decisions at once on the only external entry path, so the false-positive
# window has to come before the enforcement, not after it. Flip to `nftables`
# one host at a time once the log looks right.
crowdsec_bouncer_mode: dry-run
# `dry-run` is the other value: it pulls and logs what it would block without
# creating a single rule. That is what a new host should be given before it
# enforces, and what an existing one can be dropped back to, per host through
# inventory/host_vars, without disarming the rest.
#
# Worth knowing before arming anything new: the first bouncer on a path
# enforces the whole CAPI set at once — around 15k addresses today — so the
# false-positive window belongs before the switch, not after it. And because
# this chain sits on the INPUT hook while these hosts run no other firewall,
# there is no conntrack rule accepting established flows ahead of it: a banned
# address is dropped even when it is answering our own outbound connection.
# That is why capi_whitelists.yaml carries the proxies and netbird.renner.uno.
crowdsec_bouncer_mode: nftables
crowdsec_bouncer_update_frequency: 10s
crowdsec_bouncer_log_level: info
@@ -95,7 +95,7 @@ spec:
memory: 32Mi
containers:
- name: home-assistant
image: ghcr.io/home-assistant/home-assistant:2026.8.3 # {"$imagepolicy": "flux-system:home-assistant"}
image: ghcr.io/home-assistant/home-assistant:2026.9.0 # {"$imagepolicy": "flux-system:home-assistant"}
env:
- name: TZ
value: Europe/Berlin
+17 -18
View File
@@ -75,27 +75,26 @@ spec:
# many small per-expert matmuls that sets the pace, not bandwidth.
- --threads
- "4"
# 32k because Home Assistant's Assist agent measured 18910 tokens
# for a single request: ~12k of exposed-entity overview plus ~7k of
# base instructions and tool schemas, whose parameters enumerate
# every exposed name, area and floor. At 16384 the request was
# refused outright.
# 16k, and going above it corrupts inference rather than failing.
# At 32768 per slot this server answered a 650-token classification
# prompt with 1025 tokens of "!" — the KV cache producing numbers
# rather than language. It does not crash, it does not warn, and
# the OpenAI response looks well-formed, so the damage only shows
# in what callers get back: Paperless ran past its 300 s timeout,
# and Home Assistant got an empty completion and a hallucinated
# device list.
#
# --ctx-size is per slot, not the total: /props reports
# n_ctx_slot equal to this value while running four slots, where a
# total would have divided down to 4096. Doubling it alone would
# therefore double the KV cache, so the slot count halves in step
# and the allocation stays where it is today — 24979 MiB of the
# 32Gi limit, most of it the 19.5 GiB of weights.
# The same Vulkan/Mesa stack already fails hard at --ubatch-size
# 2048; this is the quiet version of that limit. Raise this only
# against a generation that is read, not against a server that
# merely starts.
#
# Two slots is what is actually in use: one holds Home Assistant's
# prompt, one holds Paperless's. A third caller would queue rather
# than evict either, which is the better failure mode given a cold
# prefill of this prompt costs around two minutes.
# Slots stay unset, which is also what keeps --kv-unified on
# (`llama-server --help`: enabled "if number of slots is auto"), so
# the four auto slots share one buffer and each may draw the full
# 16384 rather than a quarter of it.
- --ctx-size
- "32768"
- --parallel
- "2"
- "16384"
# A micro-batch of 2048 or more makes this Vulkan/Mesa stack fail
# with ErrorDeviceLost.
- --ubatch-size