Commit graph

7 commits

Author SHA1 Message Date
Jeuner
76a5d4e499 feat: show the ComfyUI job itself in the transcript, not just a status line
Ein Statustext über dem Orb war zu unauffällig. Ein Tool-Aufruf legt
jetzt eine eigene Karte im Gesprächsverlauf an, mit Spinner und dem
tatsächlichen Prompt, den das LLM an ComfyUI schickt ("Anfrage an
ComfyUI: ..."). Sobald das Bild fertig ist, wird dieselbe Karte
in-place durch das Ergebnis ersetzt statt eine zweite anzuhängen;
schlägt ComfyUI fehl, zeigt sie stattdessen die Fehlermeldung.

Der Prompt-Text landet über sichere DOM-Erstellung (createTextNode),
nicht per innerHTML, weil er vom LLM erzeugt und damit indirekt
nutzerbeeinflusst ist.

Per Browser-Test mit echter synthetisierter Sprache verifiziert: Karte
mit Spinner + Prompt erscheint sofort, wird sauber (kein Leerlauf-id im
DOM) durch das Bild ersetzt, gefolgt von Astras Textantwort.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LVgSHNHdRx3UNTBodFmhRA
2026-09-07 16:48:17 +02:00
Jeuner
6854e80703 feat: show what Astra is doing while it works
Der Orb pulsierte bisher nur bei "listening"/"speaking", nicht während
"responding" — während des LLM-Aufrufs sah die UI eingefroren aus, obwohl
im Hintergrund gearbeitet wurde. Jetzt pulsiert er (und der Status-Punkt
leuchtet) auch während "responding".

Neuer "activity"-Nachrichtentyp überschreibt zusätzlich den Statustext
mit konkreten Zwischenschritten, ohne den Animationszustand zu brechen —
astra/tools.py meldet jetzt "Astra erzeugt ein Bild …" während ComfyUI
läuft, statt dass der Nutzer nur ein generisches "Einen Moment …" sieht,
das über die volle Dauer (LLM-Entscheidung, ComfyUI-Generierung,
LLM-Folgeantwort) unverändert bleibt.

Per Browser-Test mit echter synthetisierter Sprache verifiziert: die
Statuszeile durchläuft jetzt sichtbar "Einen Moment …" → "Astra erzeugt
ein Bild …" → "Einen Moment …" → "Astra spricht." statt derselben Zeile
über die ganze Anfrage hinweg.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LVgSHNHdRx3UNTBodFmhRA
2026-09-07 16:35:26 +02:00
Jeuner
9dcfa7d9f5 fix: show uploaded images inline instead of just the filename
Bilder wurden bisher nur als Textzeile "Hochgeladen: X" bestätigt, nie
tatsächlich angezeigt. Zeigt jetzt sofort beim Auswählen eine lokale
Vorschau im Transkript (data:-URL, passt bereits in die bestehende
img-src-CSP), bevor der Upload überhaupt beim Server ankommt.

Verifiziert per echtem Playwright-Browsertest inkl. fake mic/WebRTC:
Bild landet als <img> im Transkript, keine Konsolenfehler.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LVgSHNHdRx3UNTBodFmhRA
2026-09-07 16:12:23 +02:00
Jeuner
16786fd3fd feat: image generation via ComfyUI and PDF/image upload
Zwei neue, sauber getrennte Fähigkeiten, jede in ihrem eigenen Modul:

- astra/comfyui.py: async HTTP-Client für einen lokalen ComfyUI-Server
  (z-image-turbo-Workflow), kennt nichts von der Pipeline.
- astra/documents.py: PDF-Textextraktion via pypdf, keine Netzwerkzugriffe.
- astra/tools.py: verdrahtet generate_image als natives Ollama-Tool-Call —
  Qwen 3.5 unterstützt Tools und Vision bereits nativ laut `ollama show`.

Dafür wurde astra/services.py so erweitert, dass NativeOllamaService
Ollamas native tool_calls im Streaming-Response erkennt und als
ChatCompletionChunk-Deltas an Pipecats bereits vorhandene, generische
Function-Calling-Maschinerie (_process_context/run_function_calls)
weiterreicht — die musste dafür nicht angefasst werden. trim_messages in
core.py bewahrt jetzt Tool-Roundtrips und Bild-Anhänge vollständig statt
sie auf role/content zu reduzieren.

Neuer Upload-Button im UI (Bild oder PDF, während eines laufenden
Gesprächs): PDFs gehen als Text, Bilder als Base64 über Qwens Vision in
den Gesprächskontext ein. Generierte Bilder werden über /api/media/<id>
ausgeliefert und per Datenkanal im Transkript angezeigt.

Kompletter Function-Calling-Roundtrip end-to-end gegen echtes Ollama und
echtes ComfyUI verifiziert (Modell ruft generate_image korrekt auf,
Bild wird erzeugt und im media_store abgelegt).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LVgSHNHdRx3UNTBodFmhRA
2026-09-07 15:56:18 +02:00
Jeuner
991f0d1eb5 feat: add voice picker UI and optional Tailnet exposure
Stimmen werden jetzt lazy pro Name geladen und gecacht statt einer
fest verdrahteten Default-Stimme; das UI bekommt ein Dropdown mit
allen 26 deutschen Pocket-TTS-Stimmen (/api/voices), Auswahl wird im
Browser gemerkt und ist während eines laufenden Gesprächs gesperrt.

Zusätzlich ein optionaler ASTRA_TAILNET_HOST, damit die App über
`tailscale serve` auch von einem anderen Gerät im selben Tailnet
erreichbar ist, ohne die Loopback-only-Härtung für alle anderen
Hosts aufzuweichen.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LVgSHNHdRx3UNTBodFmhRA
2026-09-07 15:15:13 +02:00
Jeuner
0d75eb953c feat: add favicon, footer credits and README
Favicon aus dem Astra-Sternsymbol, GitHub- und Dillenberg.net-Link im
Footer, sowie eine README mit Architektur, Setup und Sicherheitsnotizen.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LVgSHNHdRx3UNTBodFmhRA
2026-09-07 14:00:28 +02:00
Jeuner
a42b3e8d73 feat: initial commit of astra local voice agent
Lokaler deutscher Sprachagent für Apple Silicon: Pipecat-Pipeline mit
Nemotron-ASR (MLX), Qwen über natives Ollama /api/chat, und Pocket TTS.
Loopback-only WebRTC-Server mit Origin/Host-Härtung.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LVgSHNHdRx3UNTBodFmhRA
2026-09-07 13:39:51 +02:00