Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
68865e15c5 | ||
|
|
be2f8b9a18 | ||
|
|
7dacb6320c | ||
|
|
8a00b7735d |
@@ -44,12 +44,37 @@ spec:
|
||||
- Qwen/Qwen3-Embedding-0.6B-GGUF:Q8_0
|
||||
# Serves POST /v1/embeddings.
|
||||
- --embeddings
|
||||
# A short, stable id for consumers. Without it the model is
|
||||
# advertised as the whole --hf-repo string, which would end up
|
||||
# verbatim in Paperless's config and break on the next model swap.
|
||||
# One name only: llama-server keeps aliases in a sorted set and
|
||||
# reports the first as the id, so a second name can steal it.
|
||||
- --alias
|
||||
- qwen3-embedding-0.6b
|
||||
# Qwen3-Embedding is trained for last-token pooling. Left
|
||||
# unspecified llama-server takes the GGUF's default, which is not
|
||||
# visible from outside and would silently produce vectors the model
|
||||
# was not trained to emit. Set before anything is indexed: changing
|
||||
# pooling later invalidates every stored embedding.
|
||||
- --pooling
|
||||
- last
|
||||
- --n-gpu-layers
|
||||
- "99"
|
||||
- --threads
|
||||
- "2"
|
||||
# Paperless chunks documents at 1024 tokens. The defaults are
|
||||
# batch 2048 / ubatch 512, and with --embeddings llama-server
|
||||
# clamps both down to 512 rather than run them mismatched — below
|
||||
# the chunk size. Matching both to 1024 fits a whole chunk in one
|
||||
# pass; 2048 is deliberately avoided, since that is where this
|
||||
# Vulkan stack fails with ErrorDeviceLost.
|
||||
- --batch-size
|
||||
- "1024"
|
||||
- --ubatch-size
|
||||
- "1024"
|
||||
# Room for a full chunk plus overhead rather than exactly one.
|
||||
- --ctx-size
|
||||
- "2048"
|
||||
- "4096"
|
||||
# No browser ever talks to this Service, so the WebUI is dead weight
|
||||
# and one more unauthenticated surface.
|
||||
- --no-webui
|
||||
|
||||
@@ -57,6 +57,20 @@ spec:
|
||||
protocol: UDP
|
||||
- port: "53"
|
||||
protocol: TCP
|
||||
# The cluster's own LLM, for AI suggestions and RAG embeddings. Scoped to
|
||||
# the two pods and the one port rather than the ai namespace as a whole, so
|
||||
# this grant cannot widen if something else is ever placed there.
|
||||
- toEndpoints:
|
||||
- matchLabels:
|
||||
"k8s:io.kubernetes.pod.namespace": ai
|
||||
app.kubernetes.io/name: llama-server
|
||||
- matchLabels:
|
||||
"k8s:io.kubernetes.pod.namespace": ai
|
||||
app.kubernetes.io/name: llama-embed
|
||||
toPorts:
|
||||
- ports:
|
||||
- port: "8080"
|
||||
protocol: TCP
|
||||
# External HTTPS only — Tika model downloads, runtime image
|
||||
# fetches, etc. RFC1918 + ULA are excluded from `world` via the
|
||||
# CIDR-deny pattern. `world` covers public IPs / DNS-resolvable
|
||||
|
||||
@@ -31,12 +31,14 @@ data:
|
||||
PAPERLESS_CONSUMER_DELETE_DUPLICATES: "true"
|
||||
PAPERLESS_CONSUMER_ENABLE_BARCODES: "true"
|
||||
PAPERLESS_CONSUMER_ENABLE_ASN_BARCODE: "true"
|
||||
PAPERLESS_CONSUMER_BARCODE_SCANNER: "ZXING"
|
||||
# No CONSUMER_BARCODE_SCANNER: zxing-cpp is the only backend, so the
|
||||
# setting no longer exists. pyzbar and libzbar are gone from the image.
|
||||
#
|
||||
# CephFS does not propagate inotify across nodes — files written
|
||||
# via the RWX consume PVC from a pod on a different node than
|
||||
# paperless never fire IN_CREATE in paperless's kernel. Polling
|
||||
# is the reliable backstop. See wiki: cephfs-inotify-no-cross-node-propagation.
|
||||
PAPERLESS_CONSUMER_POLLING: "10"
|
||||
PAPERLESS_CONSUMER_POLLING_INTERVAL: "10"
|
||||
PAPERLESS_REDIS: "redis://redis:6379"
|
||||
PAPERLESS_DBENGINE: "postgresql"
|
||||
PAPERLESS_DBHOST: "pprl-pg-rw"
|
||||
@@ -44,6 +46,44 @@ data:
|
||||
PAPERLESS_TIKA_ENABLED: "1"
|
||||
PAPERLESS_TIKA_ENDPOINT: "http://tika:9998"
|
||||
PAPERLESS_TIKA_GOTENBERG_ENDPOINT: "http://gotenberg:3000"
|
||||
# AI suggestions against the cluster's own LLM (kubernetes/apps/llama-server).
|
||||
# Nothing leaves the cluster: both endpoints are ClusterIP Services, so
|
||||
# document text is never sent to a third party and there is no API key to
|
||||
# rotate — llama-server has no authentication, and the ai namespace's network
|
||||
# policy is what restricts who may reach it.
|
||||
#
|
||||
# CAVEAT, and it is a real one: paperless reads these from an
|
||||
# ApplicationConfiguration row in the database and falls back to the
|
||||
# environment only when that row is empty. A value set in the admin UI
|
||||
# therefore overrides this ConfigMap silently. Configure AI in ONE place —
|
||||
# here — and leave those fields untouched in the UI.
|
||||
PAPERLESS_AI_ENABLED: "true"
|
||||
PAPERLESS_AI_LLM_BACKEND: "openai-like"
|
||||
PAPERLESS_AI_LLM_ENDPOINT: "http://llama-server.ai.svc.cluster.local:8080/v1"
|
||||
# Must match --alias in kubernetes/apps/llama-server/deployment.yaml. The
|
||||
# server ignores the field on the wire, but paperless sends it and the value
|
||||
# is what appears in its logs.
|
||||
PAPERLESS_AI_LLM_MODEL: "qwen3.6-35b-a3b"
|
||||
# ClusterIP addresses are private, and paperless blocks non-public endpoints
|
||||
# unless this is set. Default is already true; explicit because the whole
|
||||
# integration silently stops working if it ever flips.
|
||||
PAPERLESS_AI_LLM_ALLOW_INTERNAL_ENDPOINTS: "true"
|
||||
# Under llama-server's own 16384, leaving room for the response.
|
||||
PAPERLESS_AI_LLM_CONTEXT_SIZE: "12288"
|
||||
# The 120s default is too tight at ~11 tok/s: a classification is roughly
|
||||
# 2-4k prompt tokens plus a second round trip for localisation.
|
||||
PAPERLESS_AI_LLM_REQUEST_TIMEOUT: "300"
|
||||
# Matched against Django's settings.LANGUAGES, so the region is required —
|
||||
# "de" alone does not resolve. Without it the model returns German tags but
|
||||
# English document types, which was reproducible against this endpoint.
|
||||
PAPERLESS_AI_LLM_OUTPUT_LANGUAGE: "de-de"
|
||||
# RAG embeddings from llama-embed. Its Qwen3-Embedding covers 100+ languages,
|
||||
# where paperless's default all-MiniLM-L6-v2 is documented as suited to
|
||||
# primarily English archives — the better fit for a German corpus.
|
||||
PAPERLESS_AI_LLM_EMBEDDING_BACKEND: "openai-like"
|
||||
PAPERLESS_AI_LLM_EMBEDDING_ENDPOINT: "http://llama-embed.ai.svc.cluster.local:8080/v1"
|
||||
PAPERLESS_AI_LLM_EMBEDDING_MODEL: "qwen3-embedding-0.6b"
|
||||
PAPERLESS_AI_LLM_EMBEDDING_CHUNK_SIZE: "1024"
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
@@ -120,7 +160,7 @@ spec:
|
||||
drop: ["ALL"]
|
||||
containers:
|
||||
- name: paperless
|
||||
image: ghcr.io/paperless-ngx/paperless-ngx:2.20.15 # {"$imagepolicy": "flux-system:paperless-ngx"}
|
||||
image: ghcr.io/paperless-ngx/paperless-ngx:3.1.2 # {"$imagepolicy": "flux-system:paperless-ngx"}
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 8000
|
||||
@@ -172,6 +212,25 @@ spec:
|
||||
# Safe against ALLOWED_HOSTS: the kubelet sends the pod IP as Host,
|
||||
# and PAPERLESS_ALLOWED_HOSTS is "*". Narrowing that value later
|
||||
# means giving these probes an explicit Host header.
|
||||
|
||||
# The container does not serve until its init sequence finishes:
|
||||
# s6 makes svc-webserver depend on init-migrations and
|
||||
# init-search-index. On the first 3.x start those are the SHA256
|
||||
# checksum backfill, which opens and reads every original and every
|
||||
# archive file, and a full Tantivy index build, since the Whoosh
|
||||
# index cannot be converted. Both scale with the corpus.
|
||||
#
|
||||
# startupProbe holds liveness and readiness off until the webserver
|
||||
# answers, giving that boot 30 * 20s = 10 minutes. Without it the
|
||||
# 120s liveness delay plus three 30s periods kills the pod 210s in,
|
||||
# part-way through a migration, and the next boot starts over.
|
||||
startupProbe:
|
||||
httpGet:
|
||||
path: /accounts/login/
|
||||
port: http
|
||||
periodSeconds: 20
|
||||
timeoutSeconds: 5
|
||||
failureThreshold: 30
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /accounts/login/
|
||||
|
||||
@@ -18,7 +18,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: whisper
|
||||
image: rhasspy/wyoming-whisper:3.6.0 # {"$imagepolicy": "flux-system:wyoming-whisper"}
|
||||
image: rhasspy/wyoming-whisper:3.7.0 # {"$imagepolicy": "flux-system:wyoming-whisper"}
|
||||
args:
|
||||
- --model=base-int8
|
||||
- --language=de
|
||||
|
||||
@@ -7,7 +7,7 @@ spec:
|
||||
image: ghcr.io/paperless-ngx/paperless-ngx
|
||||
interval: 1h
|
||||
---
|
||||
# Allow only patch + minor bumps within the 2.x line. A 3.0 release
|
||||
# Allow only patch + minor bumps within the 3.x line. A 4.0 release
|
||||
# requires manual policy widening — major upgrades typically need a
|
||||
# migration plan and shouldn't land via auto-merge.
|
||||
apiVersion: image.toolkit.fluxcd.io/v1
|
||||
@@ -20,4 +20,4 @@ spec:
|
||||
name: paperless-ngx
|
||||
policy:
|
||||
semver:
|
||||
range: "2.x"
|
||||
range: "3.x"
|
||||
|
||||
@@ -24,6 +24,16 @@ spec:
|
||||
- ports:
|
||||
- port: "8080"
|
||||
protocol: TCP
|
||||
# Paperless calls both models for AI suggestions and RAG embeddings. The
|
||||
# matching egress grant on its side is scoped to these two pods on :8080.
|
||||
- fromEndpoints:
|
||||
- matchLabels:
|
||||
"k8s:io.kubernetes.pod.namespace": paperless
|
||||
app.kubernetes.io/name: paperless
|
||||
toPorts:
|
||||
- ports:
|
||||
- port: "8080"
|
||||
protocol: TCP
|
||||
- fromEndpoints:
|
||||
- matchLabels:
|
||||
"k8s:io.kubernetes.pod.namespace": monitoring
|
||||
|
||||
Reference in New Issue
Block a user