Docker & Infrastruktur — OpenClaw Image fix, libportaudio2, aria.env.example

Wake-Word Fix — openwakeword API-Bug behoben
get-voices.sh — neues Script + README-Schritt
This commit is contained in:
duffyduck 2026-03-10 14:08:28 +01:00
parent c67da1d085
commit afcd45d32f
11 changed files with 114 additions and 17 deletions

View File

@ -43,6 +43,19 @@ Alle Änderungen am Projekt. Format: [Keep a Changelog](https://keepachangelog.c
- `build.sh` schreibt `org.gradle.java.home` dynamisch in `gradle.properties` — verhindert dass Gradle kaputte JVM-Pfade findet (`/usr/lib/jvm/openjdk-17` ohne bin/java)
- `minSdkVersion` 21 → 23 — `react-native-camera-kit` braucht mindestens API 23
**Docker & Infrastruktur**
- OpenClaw Image fix: `openclaw/openclaw:latest``ghcr.io/openclaw/openclaw:latest`
- `libportaudio2` in Bridge Dockerfile hinzugefügt — `sounddevice` braucht PortAudio
- `aria-data/config/aria.env.example` hinzugefügt — Voice Bridge Konfigurationsvorlage
**Wake-Word Fix (openwakeword)**
- `WakeWordDetector` umgebaut — sucht Custom-Modell `/voices/wake_aria.onnx`, Fallback auf eingebautes `hey_jarvis`
- Alter Code crashte: `wakeword_models=["aria"]` erwartet Dateipfad, kein Keyword
**Neues Script: `get-voices.sh`**
- Lädt Piper Stimmen (Ramona + Thorsten) von HuggingFace herunter
- Neuer Installationsschritt in README
**ARIA Persönlichkeit**
- `AGENT.md` überarbeitet — ARIA ist jetzt Partnerin auf Augenhöhe (Claude-Charakter)
- Direkt, ehrlich, humorvoll, lösungsorientiert, kein Theater

11
Josef Behrens.conf Normal file
View File

@ -0,0 +1,11 @@
[Interface]
Address = 10.252.1.21/32
PrivateKey = 2JmAeJQ1wL+nfaAVp32RiEsPFcaoXVtZh/p7pqHGCl4=
MTU = 1450
[Peer]
PublicKey = IHBroF1ChESXWQQ+2RC4DmrNoHQl54Hc/xhH+iYLTBA=
PresharedKey = A1i59KCEjvwtx9J03pkcqDdGP7Jhr4PcbA5Um32iMoY=
AllowedIPs = 192.168.0.0/24
Endpoint = stb-er.selfhost.eu:51820
PersistentKeepalive = 15

View File

@ -208,7 +208,7 @@ services:
# ─── OpenClaw (ARIA Gehirn) ─────────────────────────────
aria:
image: openclaw/openclaw:latest
image: ghcr.io/openclaw/openclaw:latest
container_name: aria-core
privileged: true # ARIAs Wohnung — sie hat die Schlüssel
depends_on:
@ -405,7 +405,18 @@ cp .env.example .env
# → RVS_HOST + RVS_PORT eintragen (z.B. rvs.hackersoft.de / 443)
```
### 3. Token generieren & starten
### 3. Konfiguration & Stimmen
```bash
# Voice Bridge Konfiguration anlegen
cp aria-data/config/aria.env.example aria-data/config/aria.env
# → Bei Bedarf anpassen (Whisper-Modell, Sprache, etc.)
# Piper Stimmen herunterladen (Ramona + Thorsten)
./get-voices.sh
```
### 4. Token generieren & starten
```bash
# Token erzeugen — schreibt RVS_TOKEN automatisch in .env, zeigt QR-Code
@ -415,11 +426,11 @@ cp .env.example .env
docker compose up -d
```
### 4. App verbinden
### 5. App verbinden
App öffnen → QR-Code scannen → "ARIA, hörst du mich?" 🎙️
> Alles was über diese vier Schritte hinausgeht macht ARIA selbst.
> Alles was über diese fünf Schritte hinausgeht macht ARIA selbst.
---
@ -678,6 +689,7 @@ aria/ ← Gitea Repo — hier wird entwickelt
├── README.md ← diese Datei — ARIAs Gedächtnis & Auftrag
├── docker-compose.yml ← ARIA-VM: ein Befehl startet alles
├── generate-token.sh ← Token + QR-Code erzeugen (auf ARIA-VM)
├── get-voices.sh ← Piper Stimmen herunterladen (Ramona + Thorsten)
├── .env.example ← Vorlage (echte .env nie ins Repo!)
├── .gitignore ← siehe unten
@ -691,6 +703,7 @@ aria/ ← Gitea Repo — hier wird entwickelt
│ │ └── gitea/
│ ├── voices/.gitkeep ← ignoriert — große Binärdateien
│ └── config/
│ ├── aria.env.example ← Vorlage → kopieren nach aria.env
│ ├── AGENT.md ← ARIAs Persönlichkeit & Regeln
│ ├── USER.md ← Stefans Präferenzen
│ └── TOOLING.md ← Liste installierter VM-Tools

View File

@ -0,0 +1,4 @@
OPENCLAW_URL=http://aria-core:18789
PIPER_RAMONA=/voices/de_DE-ramona-low.onnx
PIPER_THORSTEN=/voices/de_DE-thorsten-high.onnx
WAKE_WORD=aria

View File

@ -9,6 +9,7 @@ FROM python:3.12-slim
RUN apt-get update && apt-get install -y --no-install-recommends \
ffmpeg \
libsndfile1 \
libportaudio2 \
pulseaudio-utils \
alsa-utils \
&& rm -rf /var/lib/apt/lists/*

View File

@ -296,22 +296,45 @@ class STTEngine:
class WakeWordDetector:
"""Erkennt das Wake-Word 'aria' im Audio-Stream."""
"""Erkennt das Wake-Word im Audio-Stream.
WAKE_WORD = "aria"
Nutzt ein Custom-Modell aus /voices/wake_aria.onnx falls vorhanden,
sonst das eingebaute 'hey_jarvis' als Fallback.
"""
CUSTOM_MODEL_PATH = "/voices/wake_aria.onnx"
FALLBACK_MODEL = "hey_jarvis"
THRESHOLD = 0.5
def __init__(self) -> None:
self.model: Optional[WakeWordModel] = None
self.wake_word_key: str = ""
def initialize(self) -> None:
"""Laedt das Wake-Word-Modell."""
logger.info("Lade Wake-Word-Modell...")
self.model = WakeWordModel(
wakeword_models=[self.WAKE_WORD],
inference_framework="onnx",
)
logger.info("Wake-Word-Modell geladen (Trigger: '%s')", self.WAKE_WORD)
custom_path = Path(self.CUSTOM_MODEL_PATH)
if custom_path.exists():
# Custom "aria" Modell vorhanden
self.model = WakeWordModel(
wakeword_models=[str(custom_path)],
)
self.wake_word_key = custom_path.stem
logger.info("Custom Wake-Word-Modell geladen: %s", custom_path)
else:
# Fallback auf eingebautes Modell
self.model = WakeWordModel()
self.wake_word_key = self.FALLBACK_MODEL
logger.warning(
"Kein Custom-Modell (%s) — nutze Fallback '%s'",
self.CUSTOM_MODEL_PATH,
self.FALLBACK_MODEL,
)
logger.info(
"Tipp: Custom Wake-Word trainieren → "
"https://github.com/dscripka/openWakeWord#training-new-models"
)
def detect(self, audio_chunk: np.ndarray) -> bool:
"""Prueft ob das Wake-Word im Audio-Chunk enthalten ist.
@ -328,10 +351,10 @@ class WakeWordDetector:
prediction = self.model.predict(audio_chunk)
# openwakeword gibt Scores pro Modell zurueck
for key, score in prediction.items():
if score > self.THRESHOLD:
logger.info("Wake-Word erkannt! (Score: %.2f)", score)
return True
score = prediction.get(self.wake_word_key, 0)
if score > self.THRESHOLD:
logger.info("Wake-Word erkannt! (Score: %.2f)", score)
return True
return False
@ -689,7 +712,7 @@ class ARIABridge:
async def audio_loop(self) -> None:
"""Wake-Word erkennen, aufnehmen, transkribieren, an aria-core senden."""
logger.info("Audio-Schleife gestartet — warte auf Wake-Word '%s'...", WakeWordDetector.WAKE_WORD)
logger.info("Audio-Schleife gestartet — warte auf Wake-Word '%s'...", self.wake_word.wake_word_key)
loop = asyncio.get_event_loop()

View File

@ -13,7 +13,7 @@ services:
# ─── OpenClaw (ARIA Gehirn) ─────────────────────────────
aria:
image: openclaw/openclaw:latest
image: ghcr.io/openclaw/openclaw:latest
container_name: aria-core
privileged: true # ARIAs Wohnung — sie hat die Schlüssel
depends_on:

32
get-voices.sh Executable file
View File

@ -0,0 +1,32 @@
#!/bin/bash
# ════════════════════════════════════════════════
# ARIA — Piper Stimmen herunterladen
# Ramona (Alltag) + Thorsten (epische Momente)
# ════════════════════════════════════════════════
set -e
VOICES_DIR="aria-data/voices"
BASE_URL="https://huggingface.co/rhasspy/piper-voices/resolve/main/de/de_DE"
mkdir -p "$VOICES_DIR"
cd "$VOICES_DIR"
echo "Lade ARIA Stimmen..."
echo ""
echo "[1/4] Ramona (Modell)..."
wget -q --show-progress "$BASE_URL/ramona/low/de_DE-ramona-low.onnx"
echo "[2/4] Ramona (Config)..."
wget -q --show-progress "$BASE_URL/ramona/low/de_DE-ramona-low.onnx.json"
echo "[3/4] Thorsten (Modell)..."
wget -q --show-progress "$BASE_URL/thorsten/high/de_DE-thorsten-high.onnx"
echo "[4/4] Thorsten (Config)..."
wget -q --show-progress "$BASE_URL/thorsten/high/de_DE-thorsten-high.onnx.json"
echo ""
echo "Stimmen geladen!"
ls -lh *.onnx