first commit
This commit is contained in:
parent
01ca5d7a00
commit
670c6082a6
5 changed files with 886 additions and 2 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -38,3 +38,6 @@ env/
|
||||||
|
|
||||||
# Logs
|
# Logs
|
||||||
*.log
|
*.log
|
||||||
|
|
||||||
|
# Ideen und Notizen
|
||||||
|
Ideen/
|
||||||
|
|
|
||||||
|
|
@ -176,6 +176,8 @@ python3 assistant.py [OPTIONEN]
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `--backend NAME` | `llama` | Backend: `llama` \| `ollama` \| `openai` |
|
| `--backend NAME` | `llama` | Backend: `llama` \| `ollama` \| `openai` |
|
||||||
| `--model NAME` | auto | Modellname (bei llama.cpp: automatisch vom Server) |
|
| `--model NAME` | auto | Modellname (bei llama.cpp: automatisch vom Server) |
|
||||||
|
| `--api-url URL` | — | Überschreibt die Backend-URL (beliebiger OpenAI-kompatibler Endpunkt) |
|
||||||
|
| `--api-key KEY` | — | API-Key (überschreibt `OPENAI_API_KEY` / Backend-Default) |
|
||||||
| `--system TEXT` | (eingebaut) | System-Prompt für den Assistenten |
|
| `--system TEXT` | (eingebaut) | System-Prompt für den Assistenten |
|
||||||
| `--history N` | `10` | Maximale Anzahl gespeicherter Gesprächsrunden |
|
| `--history N` | `10` | Maximale Anzahl gespeicherter Gesprächsrunden |
|
||||||
|
|
||||||
|
|
@ -265,6 +267,10 @@ Vollständige Sink-Namen aus `pactl list sinks short` können direkt verwendet w
|
||||||
|
|
||||||
## 9. LLM-Backend konfigurieren
|
## 9. LLM-Backend konfigurieren
|
||||||
|
|
||||||
|
Der Assistent spricht mit jedem LLM, das die **OpenAI Chat-Completions API**
|
||||||
|
implementiert. Mit `--api-url` und `--api-key` kann der Endpunkt frei
|
||||||
|
überschrieben werden — das `--backend`-Flag wählt nur den Verbindungs-Default.
|
||||||
|
|
||||||
### llama.cpp (Standard)
|
### llama.cpp (Standard)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|
@ -273,8 +279,13 @@ python3 assistant.py --backend llama
|
||||||
```
|
```
|
||||||
|
|
||||||
Der Modellname wird automatisch vom Server abgefragt.
|
Der Modellname wird automatisch vom Server abgefragt.
|
||||||
|
Anderen Port oder Host angeben:
|
||||||
|
|
||||||
### Ollama
|
```bash
|
||||||
|
python3 assistant.py --backend llama --api-url http://192.168.1.10:8001/v1
|
||||||
|
```
|
||||||
|
|
||||||
|
### Ollama (lokal)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Ollama muss laufen: ollama serve
|
# Ollama muss laufen: ollama serve
|
||||||
|
|
@ -282,6 +293,14 @@ python3 assistant.py --backend ollama --model llama3.2
|
||||||
python3 assistant.py --backend ollama --model mistral
|
python3 assistant.py --backend ollama --model mistral
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Ollama (remote / anderer Rechner)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 assistant.py --backend ollama \
|
||||||
|
--api-url http://mein-server:11434/v1 \
|
||||||
|
--model llama3.2
|
||||||
|
```
|
||||||
|
|
||||||
### OpenAI
|
### OpenAI
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|
@ -290,6 +309,66 @@ python3 assistant.py --backend openai --model gpt-4o-mini
|
||||||
python3 assistant.py --backend openai --model gpt-4o
|
python3 assistant.py --backend openai --model gpt-4o
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### OpenRouter (viele Modelle über eine API)
|
||||||
|
|
||||||
|
[OpenRouter](https://openrouter.ai) bietet Zugang zu hunderten Modellen
|
||||||
|
(Mistral, Llama, Gemma, Claude, GPT-4 u. v. m.) über eine einheitliche API.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export OPENROUTER_API_KEY=sk-or-...
|
||||||
|
python3 assistant.py \
|
||||||
|
--backend openai \
|
||||||
|
--api-url https://openrouter.ai/api/v1 \
|
||||||
|
--model mistralai/mistral-7b-instruct
|
||||||
|
```
|
||||||
|
|
||||||
|
Andere beliebte OpenRouter-Modelle:
|
||||||
|
- `meta-llama/llama-3.1-8b-instruct:free` (kostenlos)
|
||||||
|
- `google/gemma-3-27b-it`
|
||||||
|
- `anthropic/claude-3.5-haiku`
|
||||||
|
|
||||||
|
Den API-Key unter `https://openrouter.ai/keys` erstellen.
|
||||||
|
|
||||||
|
### Groq (sehr schnelle Inferenz)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export GROQ_API_KEY=gsk_...
|
||||||
|
python3 assistant.py \
|
||||||
|
--backend openai \
|
||||||
|
--api-url https://api.groq.com/openai/v1 \
|
||||||
|
--model llama-3.1-70b-versatile
|
||||||
|
```
|
||||||
|
|
||||||
|
### LM Studio
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# LM Studio muss mit aktiviertem lokalen Server laufen (Port 1234)
|
||||||
|
python3 assistant.py --backend llama --api-url http://localhost:1234/v1
|
||||||
|
```
|
||||||
|
|
||||||
|
### Together AI
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export TOGETHER_API_KEY=...
|
||||||
|
python3 assistant.py \
|
||||||
|
--backend openai \
|
||||||
|
--api-url https://api.together.xyz/v1 \
|
||||||
|
--model meta-llama/Llama-3-8b-chat-hf
|
||||||
|
```
|
||||||
|
|
||||||
|
### Allgemeines Prinzip
|
||||||
|
|
||||||
|
Jeder Provider, der `/v1/chat/completions` mit `stream=true` unterstützt,
|
||||||
|
funktioniert mit:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 assistant.py \
|
||||||
|
--backend openai \
|
||||||
|
--api-url https://<provider>/v1 \
|
||||||
|
--model <modellname> \
|
||||||
|
--api-key <api-key>
|
||||||
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 10. Sprache und Stimme
|
## 10. Sprache und Stimme
|
||||||
|
|
|
||||||
772
HOWTO_SETUP_AND_INSTALL_RESPEAKER_XVF3800_XIAO_ESP32S3.md
Normal file
772
HOWTO_SETUP_AND_INSTALL_RESPEAKER_XVF3800_XIAO_ESP32S3.md
Normal file
|
|
@ -0,0 +1,772 @@
|
||||||
|
# HOWTO: ReSpeaker XVF3800 mit XIAO ESP32S3 unter Ubuntu 24.04
|
||||||
|
|
||||||
|
Vollständige Schritt-für-Schritt-Anleitung für den Einsatz des
|
||||||
|
**Seeed Studio ReSpeaker XVF3800 4-Mic Array with XIAO ESP32S3**
|
||||||
|
als Mikrofon und Lautsprecher unter Ubuntu 24.04 mit PipeWire —
|
||||||
|
optimiert für Sprachassistenten auf Basis von Whisper und Piper.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Inhaltsverzeichnis
|
||||||
|
|
||||||
|
1. [Hardware-Überblick](#1-hardware-überblick)
|
||||||
|
2. [Voraussetzungen](#2-voraussetzungen)
|
||||||
|
3. [Bekannter Bug: USB-Codec-Reset nach Neustart](#3-bekannter-bug-usb-codec-reset-nach-neustart)
|
||||||
|
4. [Systemabhängigkeiten installieren](#4-systemabhängigkeiten-installieren)
|
||||||
|
5. [USB-Codec-Reset-Dienst einrichten](#5-usb-codec-reset-dienst-einrichten)
|
||||||
|
6. [WirePlumber konfigurieren](#6-wireplumber-konfigurieren)
|
||||||
|
7. [ALSA konfigurieren](#7-alsa-konfigurieren)
|
||||||
|
8. [Autostart-Skript für die Desktop-Sitzung](#8-autostart-skript-für-die-desktop-sitzung)
|
||||||
|
9. [Python-Abhängigkeiten installieren](#9-python-abhängigkeiten-installieren)
|
||||||
|
10. [Verifikation: Alles testen](#10-verifikation-alles-testen)
|
||||||
|
11. [Verwendung mit dem Voice-Assistenten](#11-verwendung-mit-dem-voice-assistenten)
|
||||||
|
12. [Fehlerbehebung](#12-fehlerbehebung)
|
||||||
|
13. [Zusammenfassung aller erstellten Dateien](#13-zusammenfassung-aller-erstellten-dateien)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Hardware-Überblick
|
||||||
|
|
||||||
|
Das **ReSpeaker XVF3800 4-Mic Array** ist ein USB-Mikrofon-Array von Seeed Studio
|
||||||
|
mit integriertem **XIAO ESP32S3** Mikrocontroller. Es bietet:
|
||||||
|
|
||||||
|
| Eigenschaft | Wert |
|
||||||
|
|---|---|
|
||||||
|
| Mikrofone | 4× MEMS, Kreisanordnung |
|
||||||
|
| Nativrate | 48 000 Hz, Stereo (nach Beamforming) |
|
||||||
|
| USB-Klasse | USB Audio Class (kein Treiber nötig) |
|
||||||
|
| Hardware-AEC | Ja — Acoustic Echo Cancellation im DSP |
|
||||||
|
| Beamforming | Ja — richtet das Richtmikrofon auf den Sprecher aus |
|
||||||
|
| Ausgabe | 3,5-mm-Klinke (Stereo) |
|
||||||
|
| Vendor-ID | `2886` (Seeed Studio) |
|
||||||
|
|
||||||
|
**Wichtig für Whisper:** Der eingebaute DSP gibt nach Beamforming und
|
||||||
|
Rauschunterdrückung nur zwei Kanäle aus. sounddevice meldet für dieses
|
||||||
|
Gerät `max_input_channels = 0` beim Hardware-Device, obwohl es funktioniert.
|
||||||
|
Das ist ein Anzeigebug des Treibers, kein Fehler.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Voraussetzungen
|
||||||
|
|
||||||
|
- Ubuntu 24.04 LTS (frische Installation oder bestehendes System)
|
||||||
|
- PipeWire als Audio-Server (ab Ubuntu 22.04 Standard)
|
||||||
|
- Python 3.12+
|
||||||
|
- Internetzugang (für Downloads beim ersten Start)
|
||||||
|
- NVIDIA GPU mit CUDA (empfohlen für Whisper; CPU funktioniert ebenfalls, aber langsam)
|
||||||
|
|
||||||
|
PipeWire-Status prüfen:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
systemctl --user status pipewire pipewire-pulse
|
||||||
|
# Beide müssen "active (running)" zeigen
|
||||||
|
```
|
||||||
|
|
||||||
|
Falls noch PulseAudio läuft:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
systemctl --user disable --now pulseaudio.service pulseaudio.socket
|
||||||
|
systemctl --user enable --now pipewire pipewire-pulse
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Bekannter Bug: USB-Codec-Reset nach Neustart
|
||||||
|
|
||||||
|
**Das Problem (XVF3800 Issue #20):**
|
||||||
|
Nach einem Software-Neustart (nicht nach Kaltstart) befindet sich der
|
||||||
|
TLV320-Audio-Codec auf dem XVF3800 in einem korrumpierten Zustand.
|
||||||
|
Ubuntu erkennt das Gerät zwar als USB-Gerät, aber PipeWire registriert
|
||||||
|
es nicht als Audio-Quelle. `arecord -l` zeigt die Karte, aber
|
||||||
|
`pactl list sources` kennt das Gerät nicht.
|
||||||
|
|
||||||
|
**Die Ursache:**
|
||||||
|
Der XIAO ESP32S3 initialisiert den Codec beim USB-Attach. Nach einem
|
||||||
|
`reboot`-Befehl bleibt die USB-Verbindung physisch bestehen — das
|
||||||
|
Attach-Event findet nicht statt, der Codec bleibt im alten Zustand.
|
||||||
|
|
||||||
|
**Der Fix:**
|
||||||
|
Das USB-Gerät kurz de-autorisieren und re-autorisieren — **ohne das
|
||||||
|
USB-Kabel abzuziehen**. Linux stellt über das Sysfs-Interface
|
||||||
|
(`/sys/bus/usb/devices/.../authorized`) einen Software-Disconnect/-Connect
|
||||||
|
bereit: Schreibe `0` → Gerät wird vom Kernel getrennt; schreibe `1` →
|
||||||
|
Gerät wird neu erkannt. Dadurch löst Linux ein neues USB-Attach-Event aus,
|
||||||
|
der ESP32S3 reinitialisiert den Codec, und PipeWire erkennt das Gerät
|
||||||
|
korrekt — genau so wie nach einem physischen Einstecken des Kabels.
|
||||||
|
|
||||||
|
Dieser Reset wird automatisch durch den in Abschnitt 5 beschriebenen
|
||||||
|
systemd-Dienst durchgeführt.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Systemabhängigkeiten installieren
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# PipeWire-Werkzeuge (meist schon vorhanden)
|
||||||
|
sudo apt install -y pipewire pipewire-pulse wireplumber \
|
||||||
|
libspa-0.2-bluetooth \
|
||||||
|
pavucontrol pulseaudio-utils
|
||||||
|
|
||||||
|
# Python-Entwicklungsumgebung
|
||||||
|
sudo apt install -y python3-pip python3-venv python3-dev
|
||||||
|
|
||||||
|
# ALSA-Werkzeuge (Diagnose)
|
||||||
|
sudo apt install -y alsa-utils
|
||||||
|
|
||||||
|
# Desktop-Benachrichtigungen (für das Autostart-Skript)
|
||||||
|
sudo apt install -y libnotify-bin
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. USB-Codec-Reset-Dienst einrichten
|
||||||
|
|
||||||
|
### 5.1 Reset-Skript erstellen
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo nano /usr/local/bin/respeaker-reset
|
||||||
|
```
|
||||||
|
|
||||||
|
Inhalt:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/bin/bash
|
||||||
|
# Resets ReSpeaker XVF3800 TLV320 audio codec via USB authorized toggle.
|
||||||
|
# Fixes XVF3800 Issue #20: after soft reboot the codec state is corrupted.
|
||||||
|
USB_PATH=$(grep -rl "2886" /sys/bus/usb/devices/*/idVendor 2>/dev/null \
|
||||||
|
| head -1 | sed 's|/idVendor||; s|.*/||')
|
||||||
|
if [ -z "$USB_PATH" ]; then
|
||||||
|
echo "ReSpeaker not found at boot, skipping." >&2
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
echo "Resetting ReSpeaker at /sys/bus/usb/devices/$USB_PATH ..."
|
||||||
|
echo 0 > /sys/bus/usb/devices/"$USB_PATH"/authorized
|
||||||
|
sleep 2
|
||||||
|
echo 1 > /sys/bus/usb/devices/"$USB_PATH"/authorized
|
||||||
|
echo "Reset complete."
|
||||||
|
```
|
||||||
|
|
||||||
|
Ausführbar machen:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo chmod +x /usr/local/bin/respeaker-reset
|
||||||
|
```
|
||||||
|
|
||||||
|
**Wie das Skript funktioniert:**
|
||||||
|
1. Es sucht in `/sys/bus/usb/devices/` nach einem Gerät mit Vendor-ID `2886` (Seeed Studio).
|
||||||
|
2. Es schreibt `0` in die `authorized`-Datei des USB-Geräts → USB-Disconnect.
|
||||||
|
3. Nach 2 Sekunden schreibt es `1` → USB-Connect.
|
||||||
|
4. Der ESP32S3 läuft neu an und initialisiert den Codec korrekt.
|
||||||
|
|
||||||
|
### 5.2 systemd-Dienst erstellen
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo nano /etc/systemd/system/respeaker-reset.service
|
||||||
|
```
|
||||||
|
|
||||||
|
Inhalt:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[Unit]
|
||||||
|
Description=ReSpeaker XVF3800 USB codec reset after boot
|
||||||
|
After=sound.target systemd-udevd.service
|
||||||
|
DefaultDependencies=no
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
ExecStart=/usr/local/bin/respeaker-reset
|
||||||
|
RemainAfterExit=yes
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=sound.target
|
||||||
|
```
|
||||||
|
|
||||||
|
Dienst aktivieren und starten:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo systemctl daemon-reload
|
||||||
|
sudo systemctl enable respeaker-reset.service
|
||||||
|
sudo systemctl start respeaker-reset.service
|
||||||
|
|
||||||
|
# Status prüfen
|
||||||
|
sudo systemctl status respeaker-reset.service
|
||||||
|
```
|
||||||
|
|
||||||
|
**Hinweis:** Der Dienst läuft mit Root-Rechten, weil das Schreiben in
|
||||||
|
`/sys/bus/usb/devices/*/authorized` Root-Rechte erfordert.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. WirePlumber konfigurieren
|
||||||
|
|
||||||
|
WirePlumber ist der Session-Manager von PipeWire und steuert, wie
|
||||||
|
Geräte konfiguriert werden. Die Konfiguration liegt in
|
||||||
|
`~/.config/wireplumber/main.lua.d/`.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkdir -p ~/.config/wireplumber/main.lua.d/
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6.1 ALSA-Monitor-Eigenschaften (Bit-Tiefe und Sample-Rate)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nano ~/.config/wireplumber/main.lua.d/51-alsa-custom.lua
|
||||||
|
```
|
||||||
|
|
||||||
|
Inhalt:
|
||||||
|
|
||||||
|
```lua
|
||||||
|
alsa_monitor.properties = {
|
||||||
|
["audio.format"] = "S32LE",
|
||||||
|
["audio.rate"] = 48000,
|
||||||
|
["audio.channels"] = 2,
|
||||||
|
["audio.position"] = "FL,FR"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Warum S32LE?** Das XVF3800 überträgt intern mit 32-Bit-Präzision.
|
||||||
|
S32LE verhindert unnötige Konvertierungen und damit leichte
|
||||||
|
Qualitätsverluste.
|
||||||
|
|
||||||
|
**Warum 48 000 Hz?** Das ist die native Samplerate des XVF3800.
|
||||||
|
Whisper benötigt 16 000 Hz; die Konvertierung übernimmt das Python-Skript
|
||||||
|
per `scipy.signal.resample_poly`.
|
||||||
|
|
||||||
|
### 6.2 Aufnahme-Lautstärke auf 150 % setzen
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nano ~/.config/wireplumber/main.lua.d/52-respeaker-volume.lua
|
||||||
|
```
|
||||||
|
|
||||||
|
Inhalt:
|
||||||
|
|
||||||
|
```lua
|
||||||
|
-- ReSpeaker XVF3800: set capture volume to 150% on device connect.
|
||||||
|
-- The XVF3800 DSP (noise suppression + beamforming) outputs at a low level;
|
||||||
|
-- this software boost brings speech to ~-23 dBFS, which is optimal for Whisper.
|
||||||
|
table.insert(alsa_monitor.rules, {
|
||||||
|
matches = {
|
||||||
|
{
|
||||||
|
{ "node.name", "matches", "alsa_input.usb-Seeed_Studio_reSpeaker_XVF3800*" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
apply_properties = {
|
||||||
|
["node.volume"] = 1.5,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
**Warum 150 %?** Der DSP des XVF3800 gibt nach Rauschunterdrückung und
|
||||||
|
Beamforming ein Signal mit relativ niedrigem Pegel aus. Bei 100 % landet
|
||||||
|
Sprache bei etwa −35 dBFS, was für Whisper zu leise ist (schlechtere
|
||||||
|
Transkription). 150 % bringt den Pegel auf ~−23 dBFS, den Whisper als
|
||||||
|
optimal beschreibt.
|
||||||
|
|
||||||
|
WirePlumber neu starten, damit die Regeln aktiv werden:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
systemctl --user restart wireplumber
|
||||||
|
```
|
||||||
|
|
||||||
|
Lautstärke sofort (ohne Neustart) setzen:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pactl set-source-volume \
|
||||||
|
alsa_input.usb-Seeed_Studio_reSpeaker_XVF3800_4-Mic_Array_114993700261100055-00.analog-stereo \
|
||||||
|
150%
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. ALSA konfigurieren
|
||||||
|
|
||||||
|
ALSA wird durch PipeWire unterstützt. Die `.asoundrc` stellt sicher,
|
||||||
|
dass ALSA-Anwendungen durch PipeWire geleitet werden und keine
|
||||||
|
„device busy"-Konflikte entstehen.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nano ~/.asoundrc
|
||||||
|
```
|
||||||
|
|
||||||
|
Inhalt:
|
||||||
|
|
||||||
|
```
|
||||||
|
# ALSA routes through PipeWire — avoids "device busy" conflicts.
|
||||||
|
# PipeWire default sink/source are set via:
|
||||||
|
# pactl set-default-sink <sink-name>
|
||||||
|
# pactl set-default-source <source-name>
|
||||||
|
|
||||||
|
pcm.!default {
|
||||||
|
type pipewire
|
||||||
|
playback_node "-1"
|
||||||
|
capture_node "-1"
|
||||||
|
}
|
||||||
|
|
||||||
|
ctl.!default {
|
||||||
|
type pipewire
|
||||||
|
}
|
||||||
|
|
||||||
|
# Named convenience aliases (use with: arecord -D respeaker / aplay -D motu_m2)
|
||||||
|
pcm.respeaker {
|
||||||
|
type pulse
|
||||||
|
device "alsa_input.usb-Seeed_Studio_reSpeaker_XVF3800_4-Mic_Array_114993700261100055-00.analog-stereo"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Hinweis:** Den langen Gerätenamen immer aus der Ausgabe von
|
||||||
|
`pactl list sources short` kopieren — er enthält eine Seriennummer
|
||||||
|
(`114993700261100055`) die sich von Gerät zu Gerät unterscheiden kann.
|
||||||
|
|
||||||
|
Eigene Seriennummer ermitteln:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pactl list sources short | grep -i respeaker
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Autostart-Skript für die Desktop-Sitzung
|
||||||
|
|
||||||
|
Nach dem Login prüft dieses Skript, ob das Gerät korrekt erkannt wurde,
|
||||||
|
und sendet eine Desktop-Benachrichtigung.
|
||||||
|
|
||||||
|
### 8.1 Skript erstellen
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkdir -p ~/.local/bin
|
||||||
|
nano ~/.local/bin/respeaker-init.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Inhalt:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/bin/bash
|
||||||
|
# ReSpeaker XVF3800 login health check.
|
||||||
|
# The systemd service respeaker-reset.service handles the USB codec reset at boot.
|
||||||
|
# This script only verifies the device is ready and notifies the user.
|
||||||
|
|
||||||
|
DEVICE="alsa_input.usb-Seeed_Studio_reSpeaker_XVF3800_4-Mic_Array_114993700261100055-00.analog-stereo"
|
||||||
|
|
||||||
|
# Wait for PipeWire + respeaker-reset.service to complete (max 30s)
|
||||||
|
sleep 5
|
||||||
|
for i in $(seq 25); do
|
||||||
|
pactl list sources short 2>/dev/null | grep -q "reSpeaker" && break
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
|
||||||
|
if ! pactl list sources short 2>/dev/null | grep -q "reSpeaker"; then
|
||||||
|
notify-send "ReSpeaker XVF3800" "Gerät nicht gefunden. USB-Kabel prüfen." \
|
||||||
|
--icon=audio-input-microphone --urgency=critical
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Fallback: ensure volume is 150% (WirePlumber rule should handle this)
|
||||||
|
pactl set-source-volume "$DEVICE" 150% 2>/dev/null
|
||||||
|
|
||||||
|
notify-send "ReSpeaker XVF3800" "Mikrofon bereit (150% Lautstärke, 48 kHz)." \
|
||||||
|
--icon=audio-input-microphone
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
chmod +x ~/.local/bin/respeaker-init.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
**Wichtig:** Die Seriennummer im `DEVICE`-Wert anpassen (→ Abschnitt 7).
|
||||||
|
|
||||||
|
### 8.2 Autostart-Eintrag erstellen
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkdir -p ~/.config/autostart
|
||||||
|
nano ~/.config/autostart/respeaker-init.desktop
|
||||||
|
```
|
||||||
|
|
||||||
|
Inhalt:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[Desktop Entry]
|
||||||
|
Type=Application
|
||||||
|
Name=ReSpeaker XVF3800 Init
|
||||||
|
Comment=Reset ReSpeaker audio codec state after boot (XVF3800 USB Issue #20)
|
||||||
|
Exec=/home/BENUTZERNAME/.local/bin/respeaker-init.sh
|
||||||
|
Hidden=false
|
||||||
|
NoDisplay=true
|
||||||
|
X-GNOME-Autostart-enabled=true
|
||||||
|
```
|
||||||
|
|
||||||
|
`BENUTZERNAME` durch den eigenen Benutzernamen ersetzen, oder absoluten Pfad
|
||||||
|
nutzen:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Pfad automatisch setzen
|
||||||
|
sed -i "s|BENUTZERNAME|$USER|g" ~/.config/autostart/respeaker-init.desktop
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Python-Abhängigkeiten installieren
|
||||||
|
|
||||||
|
### 9.1 Conda-Umgebung (empfohlen für GPU-Projekte)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Conda/Miniforge installieren falls nicht vorhanden
|
||||||
|
# https://github.com/conda-forge/miniforge
|
||||||
|
|
||||||
|
conda create -n voice python=3.12
|
||||||
|
conda activate voice
|
||||||
|
```
|
||||||
|
|
||||||
|
### 9.2 Python-Pakete
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install \
|
||||||
|
faster-whisper \
|
||||||
|
openwakeword \
|
||||||
|
piper-tts \
|
||||||
|
sounddevice \
|
||||||
|
scipy \
|
||||||
|
numpy \
|
||||||
|
langdetect \
|
||||||
|
openai
|
||||||
|
```
|
||||||
|
|
||||||
|
**Paketbeschreibungen:**
|
||||||
|
|
||||||
|
| Paket | Funktion |
|
||||||
|
|---|---|
|
||||||
|
| `faster-whisper` | GPU-beschleunigte Spracherkennung (OpenAI Whisper) |
|
||||||
|
| `openwakeword` | Wake-Word-Erkennung offline (u. a. „Hey Jarvis") |
|
||||||
|
| `piper-tts` | Offline Text-to-Speech mit Piper-Stimmen |
|
||||||
|
| `sounddevice` | Python-Binding für PortAudio (Mikrofon-Zugriff) |
|
||||||
|
| `scipy` | Resampling: 48 kHz → 16 kHz für Whisper |
|
||||||
|
| `numpy` | Audiodaten-Verarbeitung |
|
||||||
|
| `langdetect` | Automatische Spracherkennung des TTS-Ausgabetexts |
|
||||||
|
| `openai` | HTTP-Client für llama.cpp / Ollama / OpenAI-APIs |
|
||||||
|
|
||||||
|
### 9.3 CUDA-Unterstützung prüfen
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 -c "import torch; print(torch.cuda.is_available(), torch.cuda.device_count())"
|
||||||
|
# Sollte: True <Anzahl GPUs> ausgeben
|
||||||
|
|
||||||
|
# Whisper-Modell laden (lädt ~3 GB beim ersten Mal)
|
||||||
|
python3 -c "
|
||||||
|
from faster_whisper import WhisperModel
|
||||||
|
m = WhisperModel('large-v3', device='cuda', compute_type='float16')
|
||||||
|
print('Whisper OK')
|
||||||
|
"
|
||||||
|
```
|
||||||
|
|
||||||
|
Falls keine GPU vorhanden ist: `device='cpu'` und `compute_type='int8'` verwenden.
|
||||||
|
Die Transkription dauert dann mehrere Sekunden statt ~300 ms.
|
||||||
|
|
||||||
|
### 9.4 openwakeword initialisieren
|
||||||
|
|
||||||
|
Beim ersten Start lädt openwakeword die vortrainierten Modelle herunter:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 -c "
|
||||||
|
from openwakeword.model import Model
|
||||||
|
m = Model()
|
||||||
|
print('Wake-Word-Modelle:', list(m.models.keys()))
|
||||||
|
"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Verifikation: Alles testen
|
||||||
|
|
||||||
|
### Schritt 1: Gerät in PipeWire prüfen
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pactl list sources short | grep -i respeaker
|
||||||
|
# Erwartete Ausgabe (eine Zeile):
|
||||||
|
# 123 alsa_input.usb-Seeed_Studio_reSpeaker_XVF3800_4-Mic_Array_... PipeWire s16le 2ch 48000Hz SUSPENDED
|
||||||
|
```
|
||||||
|
|
||||||
|
Falls leer: USB-Kabel abziehen, wieder einstecken, 5 Sekunden warten,
|
||||||
|
dann erneut prüfen.
|
||||||
|
|
||||||
|
### Schritt 2: Lautstärke prüfen
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pactl get-source-volume \
|
||||||
|
alsa_input.usb-Seeed_Studio_reSpeaker_XVF3800_4-Mic_Array_114993700261100055-00.analog-stereo
|
||||||
|
# Sollte 150% / 1,50 zeigen
|
||||||
|
```
|
||||||
|
|
||||||
|
### Schritt 3: Aufnahme testen
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 5 Sekunden aufnehmen und abspielen
|
||||||
|
arecord -D respeaker -f S16_LE -r 48000 -c 2 -d 5 /tmp/test.wav && aplay /tmp/test.wav
|
||||||
|
```
|
||||||
|
|
||||||
|
Sprache sollte klar und ohne Echos zu hören sein.
|
||||||
|
|
||||||
|
### Schritt 4: sounddevice-Index ermitteln
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 -c "
|
||||||
|
import sounddevice as sd
|
||||||
|
for i, d in enumerate(sd.query_devices()):
|
||||||
|
if 'respeaker' in d['name'].lower():
|
||||||
|
print(f'Index {i}: {d[\"name\"]}')
|
||||||
|
print(f' Kanäle: in={d[\"max_input_channels\"]} out={d[\"max_output_channels\"]}')
|
||||||
|
print(f' Rate: {int(d[\"default_samplerate\"])} Hz')
|
||||||
|
"
|
||||||
|
# Hinweis: max_input_channels kann 0 melden — das ist ein Anzeigebug,
|
||||||
|
# das Gerät funktioniert trotzdem als Eingabe.
|
||||||
|
```
|
||||||
|
|
||||||
|
### Schritt 5: Whisper-Transkription testen
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 5 Sekunden sprechen, dann automatische Transkription
|
||||||
|
python3 -c "
|
||||||
|
import sounddevice as sd
|
||||||
|
import numpy as np
|
||||||
|
from scipy.signal import resample_poly
|
||||||
|
from faster_whisper import WhisperModel
|
||||||
|
|
||||||
|
DEVICE = 16 # ← Index aus Schritt 4 einsetzen
|
||||||
|
RATE = 48000
|
||||||
|
SEC = 5
|
||||||
|
|
||||||
|
print('Sprechen ...')
|
||||||
|
audio = sd.rec(int(SEC * RATE), samplerate=RATE, channels=1,
|
||||||
|
dtype='float32', device=DEVICE)
|
||||||
|
sd.wait()
|
||||||
|
audio16k = resample_poly(audio[:,0], 16000, RATE).astype('float32')
|
||||||
|
|
||||||
|
model = WhisperModel('large-v3', device='cuda', compute_type='float16')
|
||||||
|
segs, _ = model.transcribe(audio16k, language='de')
|
||||||
|
print('Transkription:', ' '.join(s.text for s in segs))
|
||||||
|
"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Schritt 6: TTS-Ausgabe testen
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 -c "
|
||||||
|
import subprocess
|
||||||
|
from piper.voice import PiperVoice
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
VOICES = Path.home() / '.local/share/piper/voices'
|
||||||
|
VOICES.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
voice_name = 'de_DE-thorsten-high'
|
||||||
|
onnx = VOICES / f'{voice_name}.onnx'
|
||||||
|
if not onnx.exists():
|
||||||
|
from piper.download_voices import download_voice
|
||||||
|
print('Lade Stimme herunter ...')
|
||||||
|
download_voice(voice_name, VOICES)
|
||||||
|
|
||||||
|
voice = PiperVoice.load(str(onnx))
|
||||||
|
audio = b''.join(c.audio_int16_bytes for c in voice.synthesize('Hallo, ich bin dein Sprachassistent.'))
|
||||||
|
|
||||||
|
proc = subprocess.Popen(
|
||||||
|
['paplay', '--raw', f'--rate={voice.config.sample_rate}',
|
||||||
|
'--channels=1', '--format=s16le'],
|
||||||
|
stdin=subprocess.PIPE
|
||||||
|
)
|
||||||
|
proc.stdin.write(audio)
|
||||||
|
proc.stdin.close()
|
||||||
|
proc.wait()
|
||||||
|
print('TTS OK')
|
||||||
|
"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 11. Verwendung mit dem Voice-Assistenten
|
||||||
|
|
||||||
|
Der Voice-Assistent in diesem Repository (`assistant.py`) nutzt alle
|
||||||
|
oben konfigurierten Komponenten.
|
||||||
|
|
||||||
|
### Starten
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Standard: Wake-Word "Hey Jarvis", llama.cpp auf localhost:8001
|
||||||
|
python3 assistant.py
|
||||||
|
|
||||||
|
# Entwicklung ohne Wake-Word
|
||||||
|
python3 assistant.py --no-wakeword
|
||||||
|
|
||||||
|
# MOTU M2 statt ReSpeaker als Mikrofon
|
||||||
|
python3 assistant.py --mic motu
|
||||||
|
|
||||||
|
# Alle verfügbaren Mikrofone anzeigen
|
||||||
|
python3 assistant.py --list-mics
|
||||||
|
```
|
||||||
|
|
||||||
|
### Mikrofon-Index im Code
|
||||||
|
|
||||||
|
Der Assistent verwendet den Kurznamen `respeaker` (→ `mic.py`), der
|
||||||
|
über Substring-Suche nach `reSpeaker` im Gerätenamen auflöst.
|
||||||
|
Der Hardware-Index (z. B. 16) kann auf anderen Systemen abweichen —
|
||||||
|
der Kurzname funktioniert unabhängig davon.
|
||||||
|
|
||||||
|
### GPU-Konfiguration
|
||||||
|
|
||||||
|
Das System wurde mit folgender GPU-Belegung entwickelt:
|
||||||
|
|
||||||
|
```
|
||||||
|
GPU 0 NVIDIA T600 (4 GB) — reserviert / Display
|
||||||
|
GPU 1 RTX 3090 (24 GB) — llama.cpp, Port 8001
|
||||||
|
GPU 2 RTX 3090 (24 GB) — Whisper
|
||||||
|
```
|
||||||
|
|
||||||
|
Whisper wird mit `CUDA_VISIBLE_DEVICES=1,2` gestartet und nutzt
|
||||||
|
physischen GPU-Index 2 (`--gpu 1` im Python-Code = Index 1
|
||||||
|
innerhalb von `CUDA_VISIBLE_DEVICES`).
|
||||||
|
|
||||||
|
Für Systeme mit einer einzelnen GPU:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 assistant.py --gpu 0
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 12. Fehlerbehebung
|
||||||
|
|
||||||
|
### Gerät nicht in `pactl list sources`
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. USB-Kabel abziehen und wieder einstecken
|
||||||
|
# 2. Manuellen Reset auslösen
|
||||||
|
sudo /usr/local/bin/respeaker-reset
|
||||||
|
|
||||||
|
# 3. Auf PipeWire-Erkennung warten
|
||||||
|
sleep 3 && pactl list sources short | grep -i respeaker
|
||||||
|
```
|
||||||
|
|
||||||
|
### Kein Ton bei der Aufnahme / Stille
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Lautstärke prüfen
|
||||||
|
pactl get-source-volume alsa_input.usb-Seeed_Studio_reSpeaker_XVF3800_4-Mic_Array_114993700261100055-00.analog-stereo
|
||||||
|
|
||||||
|
# Lautstärke manuell auf 150% setzen
|
||||||
|
pactl set-source-volume alsa_input.usb-Seeed_Studio_reSpeaker_XVF3800_4-Mic_Array_114993700261100055-00.analog-stereo 150%
|
||||||
|
|
||||||
|
# Mit alsamixer kontrollieren
|
||||||
|
alsamixer -c 5 # ← Kartennummer aus 'arecord -l'
|
||||||
|
```
|
||||||
|
|
||||||
|
### `max_input_channels = 0` in sounddevice
|
||||||
|
|
||||||
|
Das ist ein bekanntes Anzeigeproblem des ALSA-Treibers für dieses Gerät.
|
||||||
|
Das Gerät funktioniert trotzdem als Eingabe. Im Code muss die
|
||||||
|
Kanal-Filterung beim Auflösen des Gerätenamens deaktiviert sein
|
||||||
|
(→ `mic.py`, Funktion `resolve_mic`: kein Filter auf `max_input_channels`).
|
||||||
|
|
||||||
|
### Whisper versteht nichts / Transkription leer
|
||||||
|
|
||||||
|
1. Lautstärke zu niedrig → auf 150 % erhöhen (s. o.)
|
||||||
|
2. Falscher `--lang`-Parameter (z. B. `--lang en` bei deutschen Sätzen)
|
||||||
|
3. Zu kurze Stille-Erkennung → `--silence-sec 3.0` statt 5.0 testen
|
||||||
|
|
||||||
|
### `[audio] input overflow` im Terminal
|
||||||
|
|
||||||
|
Tritt auf, wenn Whisper die Verarbeitung verzögert und der PortAudio-Puffer
|
||||||
|
überläuft. Wird im Assistenten bewusst unterdrückt (ist kein Fehler).
|
||||||
|
Bei häufigem Auftreten GPU-Auslastung prüfen:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nvidia-smi
|
||||||
|
```
|
||||||
|
|
||||||
|
### systemd-Dienst schlägt fehl
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo journalctl -u respeaker-reset.service -n 20
|
||||||
|
```
|
||||||
|
|
||||||
|
Häufige Ursache: Gerät ist beim Systemstart noch nicht angeschlossen.
|
||||||
|
In diesem Fall ist kein Fehler — das Skript prüft zuerst, ob die
|
||||||
|
Vendor-ID `2886` vorhanden ist, und beendet sich sauber wenn nicht.
|
||||||
|
|
||||||
|
### WirePlumber-Regeln greifen nicht
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# WirePlumber neu starten
|
||||||
|
systemctl --user restart wireplumber
|
||||||
|
|
||||||
|
# Logs ansehen
|
||||||
|
journalctl --user -u wireplumber -n 30
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 13. Zusammenfassung aller erstellten Dateien
|
||||||
|
|
||||||
|
| Datei | Zweck |
|
||||||
|
|---|---|
|
||||||
|
| `/usr/local/bin/respeaker-reset` | USB-Codec-Reset (Root, ausführbar) |
|
||||||
|
| `/etc/systemd/system/respeaker-reset.service` | Automatischer Reset bei jedem Neustart |
|
||||||
|
| `~/.config/wireplumber/main.lua.d/51-alsa-custom.lua` | ALSA-Format und Sample-Rate für PipeWire |
|
||||||
|
| `~/.config/wireplumber/main.lua.d/52-respeaker-volume.lua` | Aufnahme-Lautstärke automatisch auf 150 % |
|
||||||
|
| `~/.asoundrc` | ALSA → PipeWire-Routing, benannte Gerät-Aliases |
|
||||||
|
| `~/.local/bin/respeaker-init.sh` | Login-Healthcheck mit Desktop-Benachrichtigung |
|
||||||
|
| `~/.config/autostart/respeaker-init.desktop` | Autostart des Healthchecks bei Desktop-Login |
|
||||||
|
|
||||||
|
### Kurzform: Komplette Installation auf einem neuen Rechner
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Reset-Skript
|
||||||
|
sudo tee /usr/local/bin/respeaker-reset > /dev/null << 'EOF'
|
||||||
|
#!/bin/bash
|
||||||
|
USB_PATH=$(grep -rl "2886" /sys/bus/usb/devices/*/idVendor 2>/dev/null \
|
||||||
|
| head -1 | sed 's|/idVendor||; s|.*/||')
|
||||||
|
[ -z "$USB_PATH" ] && exit 0
|
||||||
|
echo 0 > /sys/bus/usb/devices/"$USB_PATH"/authorized
|
||||||
|
sleep 2
|
||||||
|
echo 1 > /sys/bus/usb/devices/"$USB_PATH"/authorized
|
||||||
|
EOF
|
||||||
|
sudo chmod +x /usr/local/bin/respeaker-reset
|
||||||
|
|
||||||
|
# 2. systemd-Dienst
|
||||||
|
sudo tee /etc/systemd/system/respeaker-reset.service > /dev/null << 'EOF'
|
||||||
|
[Unit]
|
||||||
|
Description=ReSpeaker XVF3800 USB codec reset after boot
|
||||||
|
After=sound.target systemd-udevd.service
|
||||||
|
DefaultDependencies=no
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
ExecStart=/usr/local/bin/respeaker-reset
|
||||||
|
RemainAfterExit=yes
|
||||||
|
[Install]
|
||||||
|
WantedBy=sound.target
|
||||||
|
EOF
|
||||||
|
sudo systemctl daemon-reload
|
||||||
|
sudo systemctl enable --now respeaker-reset.service
|
||||||
|
|
||||||
|
# 3. WirePlumber
|
||||||
|
mkdir -p ~/.config/wireplumber/main.lua.d
|
||||||
|
cat > ~/.config/wireplumber/main.lua.d/51-alsa-custom.lua << 'EOF'
|
||||||
|
alsa_monitor.properties = {
|
||||||
|
["audio.format"] = "S32LE",
|
||||||
|
["audio.rate"] = 48000,
|
||||||
|
["audio.channels"] = 2,
|
||||||
|
["audio.position"] = "FL,FR"
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
cat > ~/.config/wireplumber/main.lua.d/52-respeaker-volume.lua << 'EOF'
|
||||||
|
table.insert(alsa_monitor.rules, {
|
||||||
|
matches = {{ { "node.name", "matches", "alsa_input.usb-Seeed_Studio_reSpeaker_XVF3800*" } }},
|
||||||
|
apply_properties = { ["node.volume"] = 1.5 },
|
||||||
|
})
|
||||||
|
EOF
|
||||||
|
systemctl --user restart wireplumber
|
||||||
|
|
||||||
|
# 4. Python-Pakete
|
||||||
|
pip install faster-whisper openwakeword piper-tts sounddevice scipy langdetect openai
|
||||||
|
|
||||||
|
# 5. Prüfen
|
||||||
|
pactl list sources short | grep -i respeaker
|
||||||
|
```
|
||||||
|
|
||||||
|
Nach Abschluss aller Schritte und einem Neustart ist das ReSpeaker XVF3800
|
||||||
|
vollständig einsatzbereit für Whisper-Transkription und Piper-TTS.
|
||||||
18
README.md
18
README.md
|
|
@ -55,6 +55,24 @@ python3 assistant.py --backend ollama --model llama3.2
|
||||||
python3 assistant.py --en-variant gb
|
python3 assistant.py --en-variant gb
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## LLM-Backends
|
||||||
|
|
||||||
|
Der Assistent unterstützt jeden OpenAI-kompatiblen Endpunkt über `--api-url` und `--api-key`.
|
||||||
|
|
||||||
|
| Ziel | Befehl |
|
||||||
|
|---|---|
|
||||||
|
| llama.cpp lokal (Standard, Port 8001) | `python3 assistant.py` |
|
||||||
|
| llama.cpp auf anderem Port/Host | `--backend llama --api-url http://host:8080/v1` |
|
||||||
|
| Ollama lokal | `--backend ollama --model llama3.2` |
|
||||||
|
| Ollama remote | `--backend ollama --api-url http://server:11434/v1 --model llama3.2` |
|
||||||
|
| OpenAI | `--backend openai --model gpt-4o-mini` + `OPENAI_API_KEY` setzen |
|
||||||
|
| OpenRouter | `--backend openai --api-url https://openrouter.ai/api/v1 --model mistralai/mistral-7b-instruct --api-key $OPENROUTER_API_KEY` |
|
||||||
|
| Groq | `--backend openai --api-url https://api.groq.com/openai/v1 --model llama-3.1-70b-versatile --api-key $GROQ_API_KEY` |
|
||||||
|
| LM Studio | `--backend llama --api-url http://localhost:1234/v1` |
|
||||||
|
| Together AI | `--backend openai --api-url https://api.together.xyz/v1 --model meta-llama/Llama-3-8b-chat-hf --api-key $TOGETHER_API_KEY` |
|
||||||
|
|
||||||
|
Alle Provider, die die OpenAI Chat-Completions API implementieren, funktionieren mit `--backend openai --api-url <endpoint>`.
|
||||||
|
|
||||||
## Dateien
|
## Dateien
|
||||||
|
|
||||||
| Datei | Beschreibung |
|
| Datei | Beschreibung |
|
||||||
|
|
|
||||||
14
assistant.py
14
assistant.py
|
|
@ -278,7 +278,15 @@ def run(args: argparse.Namespace) -> None:
|
||||||
else:
|
else:
|
||||||
ww_model = None
|
ww_model = None
|
||||||
|
|
||||||
backend_cfg = BACKENDS[args.backend]
|
backend_cfg = dict(BACKENDS[args.backend]) # Kopie — kein Mutation globaler State
|
||||||
|
if args.api_url:
|
||||||
|
backend_cfg["base_url"] = args.api_url
|
||||||
|
if args.api_key:
|
||||||
|
backend_cfg["api_key"] = args.api_key
|
||||||
|
elif args.backend == "openai" and not backend_cfg["api_key"]:
|
||||||
|
backend_cfg["api_key"] = os.getenv("OPENAI_API_KEY", "")
|
||||||
|
if not backend_cfg["api_key"] and "openrouter" in (args.api_url or ""):
|
||||||
|
backend_cfg["api_key"] = os.getenv("OPENROUTER_API_KEY", "")
|
||||||
client = openai.OpenAI(
|
client = openai.OpenAI(
|
||||||
base_url=backend_cfg["base_url"],
|
base_url=backend_cfg["base_url"],
|
||||||
api_key =backend_cfg["api_key"] or "none",
|
api_key =backend_cfg["api_key"] or "none",
|
||||||
|
|
@ -834,6 +842,10 @@ def parse_args() -> argparse.Namespace:
|
||||||
choices=["llama", "ollama", "openai"], help="LLM-Backend")
|
choices=["llama", "ollama", "openai"], help="LLM-Backend")
|
||||||
p.add_argument("--model", default=None,
|
p.add_argument("--model", default=None,
|
||||||
help="LLM-Modellname (default: auto-detect)")
|
help="LLM-Modellname (default: auto-detect)")
|
||||||
|
p.add_argument("--api-url", default=None,
|
||||||
|
help="Überschreibt die Backend-URL (z. B. https://openrouter.ai/api/v1)")
|
||||||
|
p.add_argument("--api-key", default=None,
|
||||||
|
help="API-Key (überschreibt OPENAI_API_KEY / Backend-Default)")
|
||||||
p.add_argument("--whisper-model", default="large-v3")
|
p.add_argument("--whisper-model", default="large-v3")
|
||||||
p.add_argument("--gpu", type=int, default=1,
|
p.add_argument("--gpu", type=int, default=1,
|
||||||
help="CUDA-Index für Whisper (in CUDA_VISIBLE_DEVICES)")
|
help="CUDA-Index für Whisper (in CUDA_VISIBLE_DEVICES)")
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue