From 302b08e051514f8d75576e7e66c3074310a8676e Mon Sep 17 00:00:00 2001 From: Mikei386 <44135113+Mikei386@users.noreply.github.com> Date: Mon, 21 Sep 2026 08:31:45 +0200 Subject: [PATCH] Add CPU embeddings for OpenClaw memory --- .env.example | 2 + ATHENA.md | 2 + README.md | 7 ++ compose.yaml | 59 ++++++++++++++++ config/install.env.example | 4 ++ docs/CONTAINER_INVENTORY.md | 5 +- docs/LIVE_STATE.md | 13 +++- docs/OPENCLAW_MEMORY.md | 90 ++++++++++++++++++++++++ docs/RECOVERY.md | 3 + install.sh | 10 ++- manage.sh | 2 +- platform/docker/llama-cpp-cpu/Dockerfile | 32 +++++++++ smoke-test.sh | 9 +++ 13 files changed, 231 insertions(+), 7 deletions(-) create mode 100644 docs/OPENCLAW_MEMORY.md create mode 100644 platform/docker/llama-cpp-cpu/Dockerfile diff --git a/.env.example b/.env.example index d9e8183..5baaa61 100644 --- a/.env.example +++ b/.env.example @@ -20,6 +20,8 @@ ULTRA_MODEL_FILE=qwen-pure/qwen3.8-27b-IQ4_XS-pure.gguf UNCENSORED_MODEL_FILE=qwen3.8-27b-abliterated/Qwen3.8-27B-ABLITERATED-Q4_K_M.gguf UNCENSORED_PROJECTOR_FILE=qwen3.8-27b-abliterated/mmproj-Qwen3.8-27B-ABLITERATED-F16.gguf VISION_PROJECTOR_FILE=qwen/mmproj-BF16.gguf +EMBEDDING_MODEL_FILE=embeddinggemma/embeddinggemma-300m-qat-Q8_0.gguf +EMBEDDING_THREADS=8 FAST_CONTEXT=76800 FAST_BATCH_SIZE=2048 diff --git a/ATHENA.md b/ATHENA.md index f0fa53d..93fbdf3 100644 --- a/ATHENA.md +++ b/ATHENA.md @@ -23,6 +23,7 @@ Sie betreibt: - FLUX.2 Klein 9B FP8 Beta für Textbilder und Referenzbild-Bearbeitung, - Qwen3-TTS und TTS-Gateway für Sprache, - Whisper.cpp und die WebRTC-Brücke für OpenClaw Talk, +- EmbeddingGemma auf der CPU für OpenClaws semantische Memory-Suche, - die GPU-lose Mikes-Applio-UI als gesonderten Checkout, - das Athena-Dashboard, - Portainer CE als optionale Ansicht auf die laufenden Docker-Container, @@ -92,6 +93,7 @@ nicht direkt. Kein automatischer Host-Neustart ist vorgesehen. - FLUX.2 Klein 9B FP8 Beta: Bildgenerierung und Editing; Qwen wird dafür kurz entladen und danach automatisch wiederhergestellt - Qwen3-TTS 1.7B: RTX 3060; kein Piper-Fallback +- EmbeddingGemma 300M Q8: CPU, OpenAI-kompatibel auf Port 8082; kein GPU-Zugriff Die geprüften Live-Werte stehen in [docs/LIVE_STATE.md](docs/LIVE_STATE.md). `config/profile-matrix.json` und `docs/STANDARD_PROFILE_MATRIX.md` im Repository diff --git a/README.md b/README.md index 50ce3d2..91713af 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,7 @@ dokumentieren den ausgerollten Stand, reduzierte Statuslatenzen und Tests. - FLUX.2 Klein 9B FP8 Beta: Transformer/VAE auf RTX 5080, Textencoder auf RTX 3060 - Qwen3-TTS 1.7B auf der RTX 3060 hinter dem TTS-Gateway; kein Piper-Fallback - Whisper.cpp `small` auf der CPU für lokale deutsche Spracherkennung +- EmbeddingGemma 300M Q8 auf der CPU für OpenClaws hybride Memory-Suche - Live-Dashboard mit 21 Tagen Detailhistorie für GPUs und Slot-Kontextbelegung auf Port 8099 - Portainer CE als optionale Container-Ansicht auf Port 9443 - WireGuard-Gateway, Datenbackup und Athena-Operator @@ -129,6 +130,7 @@ Prefills konkurrieren weiterhin um Rechenleistung und den gemeinsamen KV-Pool. ## Endpunkte - Router: `http://192.168.1.212:8081/v1` +- Embeddings: `http://192.168.1.212:8082/v1` - Athena-Dashboard: `http://192.168.1.212:8099` Der Router stellt Sprache OpenAI-kompatibel bereit: Sprachausgabe über @@ -148,6 +150,11 @@ OpenClaw wird über den Provider **llama.cpp → Existing llama-server** mit erkennt OpenClaw die vollständige Auswahl automatisch, während Laden, Entladen und Umschalten weiterhin ausschließlich der Athena Profile Router übernimmt. + +OpenClaws Memory-Suche verwendet den separaten CPU-Dienst +`mike-ai-embedding`. Dadurch bleiben die GPUs vollständig für Qwen, Vision +und TTS verfügbar. Die verbindliche Konfiguration, Prüfung und +Wiederherstellung stehen in [OpenClaw Memory](docs/OPENCLAW_MEMORY.md). - Portainer: `https://192.168.1.212:9443` - Hermes-Dashboard auf Unraid: `http://192.168.1.2:9119` diff --git a/compose.yaml b/compose.yaml index 27a021b..c4ff547 100644 --- a/compose.yaml +++ b/compose.yaml @@ -88,6 +88,65 @@ services: cap_drop: [ALL] security_opt: ["no-new-privileges:true"] + # Dedicated CPU-only embedding endpoint for OpenClaw memory search. It + # shares the WireGuard namespace so the API is reachable only through + # Athena's private VPN address, without consuming scarce GPU memory. + embedding: + build: + context: . + dockerfile: platform/docker/llama-cpp-cpu/Dockerfile + args: + LLAMA_CPP_COMMIT: ${LLAMA_CPP_COMMIT:-b29c606} + image: ${LLAMA_CPU_IMAGE:-mike-ai/llama.cpp-cpu:local} + container_name: mike-ai-embedding + restart: unless-stopped + network_mode: "service:wireguard-gateway" + read_only: true + tmpfs: + - /tmp:size=256m,mode=1777 + volumes: + - "${MODEL_DIR:-/srv/mike-ai/models}:/models:ro" + command: + - --model + - "/models/${EMBEDDING_MODEL_FILE:?EMBEDDING_MODEL_FILE is required}" + - --alias + - embeddinggemma + - --embedding + - --pooling + - mean + - --ctx-size + - "4096" + - --batch-size + # OpenClaw's chunks plus embedding control tokens must fit in both the + # logical and physical batch. 256 rejected valid index chunks. + - "4096" + - --ubatch-size + - "1024" + - --parallel + - "4" + - --threads + - "${EMBEDDING_THREADS:-8}" + - --threads-batch + - "${EMBEDDING_THREADS:-8}" + - --n-gpu-layers + - "0" + - --host + - 0.0.0.0 + - --port + - "8082" + - --no-ui + depends_on: + wireguard-gateway: + condition: service_healthy + cap_drop: [ALL] + security_opt: ["no-new-privileges:true"] + healthcheck: + test: [CMD, curl, -fsS, "http://127.0.0.1:8082/health"] + interval: 10s + timeout: 5s + retries: 30 + start_period: 10s + llama-fast: <<: *llama-common container_name: mike-ai-llama-fast diff --git a/config/install.env.example b/config/install.env.example index 2f0adaf..0ebbf2f 100644 --- a/config/install.env.example +++ b/config/install.env.example @@ -75,6 +75,10 @@ UNCENSORED_PROJECTOR_SHA256=2284099ce864f1023d721e6ef5eaef32bb56abdbc1dc561c6d91 VISION_PROJECTOR_FILE=qwen/mmproj-BF16.gguf VISION_PROJECTOR_URL=https://huggingface.co/unsloth/Qwen3.8-27B-GGUF/resolve/main/mmproj-BF16.gguf VISION_PROJECTOR_SHA256=83ee4f4f205fa514161778c41df1ea14144faa0f713510893b63c2395f5c2d53 +EMBEDDING_MODEL_FILE=embeddinggemma/embeddinggemma-300m-qat-Q8_0.gguf +EMBEDDING_MODEL_URL=https://huggingface.co/ggml-org/embeddinggemma-300m-qat-q8_0-GGUF/resolve/main/embeddinggemma-300m-qat-Q8_0.gguf +EMBEDDING_MODEL_SHA256=6fa0c02a9c302be6f977521d399b4de3a46310a4f2621ee0063747881b673f67 +EMBEDDING_THREADS=8 DEFAULT_REASONING_EFFORT=off # All standard profiles use the MTP tensor embedded in their GGUF. A separate # draft-model artifact is neither downloaded nor passed to llama-server. diff --git a/docs/CONTAINER_INVENTORY.md b/docs/CONTAINER_INVENTORY.md index 77fc885..fdebdd2 100644 --- a/docs/CONTAINER_INVENTORY.md +++ b/docs/CONTAINER_INVENTORY.md @@ -1,8 +1,8 @@ # Container-Inventar auf Athena -Stand: 20. September 2026 (lesender Docker-Abgleich) +Stand: 21. September 2026 -Athena hatte beim lesenden Abgleich 30 Docker-Container. Nicht jeder Container enthält +Athena hat 31 reguläre Docker-Container. Nicht jeder Container enthält ein KI-Modell: Router, Oberflächen, Netzwerk, Steuerung und Sicherung sind gewöhnliche Dienste. Die rechenintensiven GPU-Worker werden absichtlich nur bei Bedarf gestartet. Ein Container im Zustand `Created` oder `Exited (0)` ist daher @@ -11,6 +11,7 @@ nicht automatisch ein ungenutzter Rest. | Container | Modell oder wesentliche Komponente | Aufgabe | |---|---|---| | `mike-ai-backup` | kein Modell; Offen Docker Volume Backup `v2` (Digest vom 15. September 2026) | Sichert `/data`, `/etc/mike-ai`, den Stack und die persistenten Docker-Volumes im Fünf-Stunden-Takt. | +| `mike-ai-embedding` | EmbeddingGemma 300M QAT Q8 | Dauerhafter CPU-only-Endpunkt für OpenClaws semantische und hybride Memory-Suche; teilt ausschließlich den privaten Netzwerk-Namespace des WireGuard-Gateways. | | `mike-ai-bonsai2-ab` | Bonsai-2-Vergleichsmodell | Gestoppter, reproduzierbar dokumentierter A/B-Testcontainer; kein Produktivprofil. | | `mike-ai-applio-studio` | Applio/RVC; Stimmenmodelle werden nutzerseitig ergänzt | Vollständige RVC-Oberfläche für Inferenz, Modellverwaltung und Training auf der RTX 5080. Für eine Konvertierung ist ein importiertes oder trainiertes `.pth`-Modell nötig; eine Referenzaufnahme allein reicht nicht. | | `mike-ai-image-worker` | FLUX.2 Klein 9B FP8, Qwen3-8B NF4 Textencoder und VAE | Erzeugt und bearbeitet Bilder transaktional; nutzt während eines Auftrags RTX 5080 und RTX 3060. | diff --git a/docs/LIVE_STATE.md b/docs/LIVE_STATE.md index afec34f..07ec5c6 100644 --- a/docs/LIVE_STATE.md +++ b/docs/LIVE_STATE.md @@ -1,6 +1,6 @@ # Geprüfter Live-Stand auf Athena -Stand: **20. September 2026**. Quelle: lesender SSH-Abgleich von Docker, +Stand: **21. September 2026**. Quelle: lesender SSH-Abgleich von Docker, Compose-Dateien, Git-Inhalten, Images, Health-Endpunkten und Backup-Timern. Containerzustände sind Momentaufnahmen; der Controller darf Profile danach umschalten. Es wurde für diesen Abgleich kein Dienst neu gestartet. @@ -45,6 +45,15 @@ nicht mehr den aktuellen Containerzustand. 1280 × 1280 lief im Test in CUDA-OOM. - Qwen3-TTS 1.7B auf 3060 hinter dem TTS-Gateway; kein Piper-Fallback. - Whisper.cpp `ggml-small` auf CPU über `/v1/audio/transcriptions`. +- `mike-ai-embedding` stellt EmbeddingGemma 300M Q8 CPU-only über den privaten + WireGuard-Endpunkt `http://192.168.1.212:8082/v1/embeddings` bereit. Eine + Probe mit zwei deutschen Texten lieferte zwei Vektoren mit je 768 Dimensionen. + Gemessen wurden rund 86 MiB RAM im Leerlauf, rund 1,1 GiB beim vollständigen + Indexaufbau und 0,044 Sekunden für die abschließende Netzwerkprobe. OpenClaw + indexierte 107 Dateien mit 1.139 Chunks vollständig; die semantische Suche + und der 768-dimensionale Vektorindex sind aktiv. Der + GPU-Versuch war zwar schneller (0,077 Sekunden), ließ im Ultra-Profil aber + nur 372 MB VRAM auf der RTX 3060 frei und wurde deshalb verworfen. - Der gesunde Dienst `mike-ai-realtime-voice` vermittelt OpenClaw Talk über WebRTC zwischen Whisper, dem normalen OpenClaw-Agenten und Qwen3-TTS. Er braucht keine GPU und keinen Profilwechsel. Das Plugin liegt in @@ -59,7 +68,7 @@ nicht mehr den aktuellen Containerzustand. trotzdem Status, Modelle und vorbereitende Aufgaben anzeigen; eine echte Konvertierung verlangt den Applio-Modus. -Der vollständige Bestand der **30** angelegten Docker-Container steht im +Der vollständige Bestand der **31** angelegten Docker-Container steht im [Container-Inventar](CONTAINER_INVENTORY.md). `Created` oder `Exited` bei Spezialworkern bedeutet nicht automatisch einen zu entfernenden Testrest. diff --git a/docs/OPENCLAW_MEMORY.md b/docs/OPENCLAW_MEMORY.md new file mode 100644 index 0000000..378b1d3 --- /dev/null +++ b/docs/OPENCLAW_MEMORY.md @@ -0,0 +1,90 @@ +# OpenClaw Memory über Athena + +Stand: **21. September 2026** + +OpenClaw verwendet für seine Memory-Suche einen eigenen, dauerhaft laufenden +Embedding-Dienst auf Athena. Der Chat-Router auf Port 8081 bleibt unverändert; +Embeddings laufen getrennt auf Port 8082. + +## Aufbau + +- Container: `mike-ai-embedding` +- Modell: `ggml-org/embeddinggemma-300m-qat-q8_0-GGUF`, Q8_0 +- Datei: `/data/models/embeddinggemma/embeddinggemma-300m-qat-Q8_0.gguf` +- SHA-256: `6fa0c02a9c302be6f977521d399b4de3a46310a4f2621ee0063747881b673f67` +- Laufzeit: eigener, auf Athenas CPU nativ optimierter CPU-only-Build von + llama.cpp ohne CUDA-Abhängigkeit, zusätzlich erzwungen durch + `--n-gpu-layers 0` +- API: `http://192.168.1.212:8082/v1/embeddings` +- Zugriff: privater WireGuard-Netzwerk-Namespace; kein öffentlicher Host-Port +- Ergebnisdimension: 768 +- vier CPU-Slots mit insgesamt 4096 Kontexttoken +- logische Batchgröße 4096 und physische Micro-Batchgröße 1024; damit passen + auch größere OpenClaw-Memory-Chunks in einen Slot und mehrere Chunks werden + beim Indexaufbau gemeinsam verarbeitet + +Der Dienst benötigt im Leerlauf rund 86 MiB und während eines vollständigen +Indexaufbaus rund 1,1 GiB RAM. Die abschließende Netzwerkprobe von Unraid aus +lag bei 0,044 Sekunden. Ein Betrieb auf der RTX 3060 +wurde verworfen: Im Ultra-Profil blieben nur 372 MB VRAM Reserve, während der +Zeitgewinn lediglich rund 0,06 Sekunden betrug. + +## OpenClaw-Konfiguration + +OpenClaw nutzt den offiziellen generischen Anbieter `openai-compatible`. Die +bestehenden Quellen-, Scope- und Chunking-Einstellungen unter `memory.search` +bleiben dabei erhalten. + +```json5 +{ + memory: { + search: { + provider: "openai-compatible", + model: "embeddinggemma", + fallback: "none", + remote: { + baseUrl: "http://192.168.1.212:8082/v1", + apiKey: "local" + } + } + } +} +``` + +`local` ist hier nur ein nicht geheimes Kompatibilitätskennwort. Der Dienst +liegt im privaten VPN und llama.cpp verlangt selbst keinen Schlüssel. + +## Prüfung + +Auf Athena: + +```bash +docker inspect -f '{{.State.Health.Status}}' mike-ai-embedding +docker exec mike-ai-embedding curl -fsS \ + -H 'Content-Type: application/json' \ + -d '{"model":"embeddinggemma","input":["Athena Speicherprobe"]}' \ + http://127.0.0.1:8082/v1/embeddings +``` + +Auf Unraid: + +```bash +docker exec OpenClaw openclaw memory status --deep --agent main +docker exec OpenClaw openclaw memory index --force --agent main +docker exec OpenClaw openclaw memory search --agent main "GPU-Aufteilung" +``` + +Nach einem Wechsel von Anbieter oder Modell muss der Index ausdrücklich neu +aufgebaut werden. OpenClaw erledigt das absichtlich nicht still im Hintergrund. +Der produktive Neuaufbau am 21. September umfasste 107 Dateien und 1.139 +Chunks. Danach meldete OpenClaw `dirty: false`, einen vollständigen +768-dimensionalen Vektorindex, aktive semantische Suche und eine erfolgreiche +Anbieterprobe. Eine Suchprobe lieferte drei Treffer. + +## Wiederherstellung + +Der Athena-Installer lädt das Modell anhand der fest hinterlegten URL und +Prüfsumme. Danach startet `./manage.sh deploy core` den Embedding-Dienst mit. +Auf Unraid werden die vier oben dokumentierten `memory.search`-Werte gesetzt +und anschließend der Index erzwungen neu aufgebaut. Weder API-Schlüssel noch +Memory-Inhalte liegen in diesem Git-Repository. diff --git a/docs/RECOVERY.md b/docs/RECOVERY.md index a98b8a0..d53bc73 100644 --- a/docs/RECOVERY.md +++ b/docs/RECOVERY.md @@ -20,6 +20,9 @@ Quelle ist im Git-Repository unter `integrations/openclaw-athena-talk` erfasst. Piper-Daten sind kein aktueller Sicherungsbestand. Modellgewichte unter `/data/models` und das reproduzierbare Whisper-Volume gehören nicht zu diesen Backup-Mounts. Ein Backup ausschließlich auf `/data` schützt nicht vor deren Ausfall. +Das gilt auch für das reproduzierbare EmbeddingGemma-Gewicht unter +`/data/models/embeddinggemma`; URL und SHA-256 stehen in +`config/install.env.example`, sodass der Installer es erneut laden und prüfen kann. Die verschlüsselten Notfallpakete über `athena-export-backup.timer` laufen ebenfalls im Fünf-Stunden-Takt; beim Abgleich war der Timer aktiv und der diff --git a/install.sh b/install.sh index 255da08..ba6aaa8 100755 --- a/install.sh +++ b/install.sh @@ -49,7 +49,8 @@ required=(AI_HOSTNAME ADMIN_USER MODEL_DIR FAST_MODEL_FILE UNCENSORED_MODEL_FILE UNCENSORED_MODEL_URL UNCENSORED_MODEL_SHA256 UNCENSORED_PROJECTOR_FILE UNCENSORED_PROJECTOR_URL UNCENSORED_PROJECTOR_SHA256 - VISION_PROJECTOR_FILE VISION_PROJECTOR_URL VISION_PROJECTOR_SHA256) + VISION_PROJECTOR_FILE VISION_PROJECTOR_URL VISION_PROJECTOR_SHA256 + EMBEDDING_MODEL_FILE EMBEDDING_MODEL_URL EMBEDDING_MODEL_SHA256) for name in "${required[@]}"; do [[ -n "${!name:-}" ]] || die "Pflichtwert $name fehlt." done @@ -343,6 +344,8 @@ ULTRA_MODEL_FILE=$ULTRA_MODEL_FILE UNCENSORED_MODEL_FILE=$UNCENSORED_MODEL_FILE UNCENSORED_PROJECTOR_FILE=$UNCENSORED_PROJECTOR_FILE VISION_PROJECTOR_FILE=$VISION_PROJECTOR_FILE +EMBEDDING_MODEL_FILE=$EMBEDDING_MODEL_FILE +EMBEDDING_THREADS=${EMBEDDING_THREADS:-8} FAST_CONTEXT=${FAST_CONTEXT:-76800} FAST_BATCH_SIZE=${FAST_BATCH_SIZE:-64} FAST_UBATCH_SIZE=${FAST_UBATCH_SIZE:-32} @@ -442,6 +445,7 @@ $ULTRA_MODEL_FILE|$ULTRA_MODEL_URL|$ULTRA_MODEL_SHA256 $UNCENSORED_MODEL_FILE|$UNCENSORED_MODEL_URL|$UNCENSORED_MODEL_SHA256 $UNCENSORED_PROJECTOR_FILE|$UNCENSORED_PROJECTOR_URL|$UNCENSORED_PROJECTOR_SHA256 $VISION_PROJECTOR_FILE|$VISION_PROJECTOR_URL|$VISION_PROJECTOR_SHA256 +$EMBEDDING_MODEL_FILE|$EMBEDDING_MODEL_URL|$EMBEDDING_MODEL_SHA256 EOF } @@ -517,6 +521,8 @@ build_and_start() { cd "$STACK_DIR" docker build --progress=plain --build-arg LLAMA_CPP_COMMIT="$commit" \ -f platform/docker/llama-cpp/Dockerfile -t mike-ai/llama.cpp:local . + docker build --progress=plain --build-arg LLAMA_CPP_COMMIT="$commit" \ + -f platform/docker/llama-cpp-cpu/Dockerfile -t mike-ai/llama.cpp-cpu:local . docker compose --env-file "$SECRETS_DIR/stack.env" --profile image build image-worker local flux_components=${FLUX_COMPONENT_DIR:-/data/models/FLUX.2-klein-9B-components} local flux_transformer=${FLUX_TRANSFORMER_DIR:-/data/models/FLUX.2-klein-9B-fp8} @@ -549,7 +555,7 @@ build_and_start() { docker compose --env-file "$SECRETS_DIR/stack.env" --profile image create image-worker docker volume inspect portainer_data >/dev/null 2>&1 || docker volume create portainer_data >/dev/null docker compose --env-file "$SECRETS_DIR/stack.env" up -d --build \ - wireguard-gateway qwen3-tts tts-gateway profile-controller router llama-dashboard portainer backup + wireguard-gateway embedding qwen3-tts tts-gateway profile-controller router llama-dashboard portainer backup if [[ ${WIREGUARD_MODE:-container} == container ]]; then systemctl restart mike-ai-container-vpn-guard.service fi diff --git a/manage.sh b/manage.sh index ea66b50..07c8aa7 100755 --- a/manage.sh +++ b/manage.sh @@ -52,7 +52,7 @@ case "$command" in [[ $# -eq 0 ]] || { echo "core akzeptiert keine weiteren Services" >&2; exit 2; } run "$ROOT_DIR/platform/mcp/install-tools.sh" run "${compose[@]}" up -d --build \ - wireguard-gateway qwen3-tts tts-gateway profile-controller router llama-dashboard portainer backup + wireguard-gateway embedding qwen3-tts tts-gateway profile-controller router llama-dashboard portainer backup else run "${compose[@]}" up -d --build --no-deps "$@" fi diff --git a/platform/docker/llama-cpp-cpu/Dockerfile b/platform/docker/llama-cpp-cpu/Dockerfile new file mode 100644 index 0000000..806aced --- /dev/null +++ b/platform/docker/llama-cpp-cpu/Dockerfile @@ -0,0 +1,32 @@ +FROM debian:13-slim AS build + +ARG DEBIAN_FRONTEND=noninteractive +ARG LLAMA_CPP_COMMIT +RUN apt-get update && apt-get install -y --no-install-recommends \ + build-essential ca-certificates cmake git libcurl4-openssl-dev libopenblas-dev \ + ninja-build pkg-config && \ + rm -rf /var/lib/apt/lists/* +RUN test -n "$LLAMA_CPP_COMMIT" +RUN git clone --filter=blob:none https://github.com/ggml-org/llama.cpp /src/llama.cpp && \ + git -C /src/llama.cpp checkout "$LLAMA_CPP_COMMIT" +RUN cmake -S /src/llama.cpp -B /src/llama.cpp/build -G Ninja \ + -DCMAKE_BUILD_TYPE=Release \ + -DGGML_NATIVE=ON \ + -DGGML_BLAS=ON \ + -DGGML_BLAS_VENDOR=OpenBLAS && \ + cmake --build /src/llama.cpp/build --target llama-server -j "$(nproc)" + +FROM debian:13-slim +ARG DEBIAN_FRONTEND=noninteractive +ARG LLAMA_CPP_COMMIT +RUN apt-get update && apt-get install -y --no-install-recommends \ + ca-certificates curl libcurl4 libgomp1 libopenblas0-pthread python3 && \ + rm -rf /var/lib/apt/lists/* && \ + useradd --system --uid 10003 --home /nonexistent --shell /usr/sbin/nologin llama +COPY --from=build /src/llama.cpp/build/bin/ /opt/llama/bin/ +LABEL org.opencontainers.image.source="https://github.com/ggml-org/llama.cpp" \ + com.mike-ai.llama-cpp-commit="$LLAMA_CPP_COMMIT" \ + com.mike-ai.llama-cpp-backend="cpu-openblas" +ENV LD_LIBRARY_PATH=/opt/llama/bin +USER 10003:10003 +ENTRYPOINT ["/opt/llama/bin/llama-server"] diff --git a/smoke-test.sh b/smoke-test.sh index 5569661..7946ec8 100755 --- a/smoke-test.sh +++ b/smoke-test.sh @@ -26,6 +26,7 @@ healthy() { for name in \ mike-ai-wireguard-gateway \ mike-ai-profile-controller \ + mike-ai-embedding \ mike-ai-router \ mike-ai-qwen3-tts \ mike-ai-tts-gateway \ @@ -36,6 +37,14 @@ for name in \ done pass "Athena-Kerndienste sind gesund" +docker exec mike-ai-embedding curl -fsS \ + -H 'Content-Type: application/json' \ + -d '{"model":"embeddinggemma","input":["Athena Speicherprobe"]}' \ + http://127.0.0.1:8082/v1/embeddings | \ + python3 -c 'import json,sys; data=json.load(sys.stdin); assert len(data["data"][0]["embedding"]) == 768' || \ + fail "CPU-Embedding-Endpunkt liefert keinen 768-dimensionalen Vektor" +pass "CPU-Embedding-Endpunkt funktioniert" + for name in mike-ai-llama-dashboard mike-ai-portainer; do network_mode=$(docker inspect -f '{{.HostConfig.NetworkMode}}' "$name" 2>/dev/null || true) [[ $network_mode != container:* ]] || \