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
This commit is contained in:
Jeuner 2026-09-07 16:35:26 +02:00
parent f8963ebc79
commit 6854e80703
4 changed files with 9 additions and 3 deletions

View file

@ -17,9 +17,11 @@ def build_tools(config: Settings, notify: Callable[[dict], None], media_store: d
async def handle_generate_image(params):
prompt = params.arguments.get("prompt", "")
notify({"type": "activity", "text": "Astra erzeugt ein Bild …"})
try:
image = await generate_image(config.comfyui_url, prompt)
except ComfyUIError as exc:
notify({"type": "activity", "text": "Bilderzeugung fehlgeschlagen."})
await params.result_callback({"error": str(exc)})
return
image_id = uuid.uuid4().hex

View file

@ -37,7 +37,8 @@ async def test_generate_image_tool_stores_media_and_notifies(monkeypatch):
assert len(media_store) == 1
image_id, image_bytes = next(iter(media_store.items()))
assert image_bytes == b"png-bytes"
assert notifications == [{"type": "image", "url": f"/api/media/{image_id}"}]
assert notifications[0]["type"] == "activity"
assert notifications[1] == {"type": "image", "url": f"/api/media/{image_id}"}
assert params.results[0]["status"] == "ok"
@ -48,9 +49,10 @@ async def test_generate_image_tool_reports_comfyui_errors_without_storing_media(
monkeypatch.setattr(tools_module, "generate_image", failing_generate_image)
notifications = []
media_store: dict[str, bytes] = {}
schema = tools_module.build_tools(
Settings(comfyui_url="http://fake-comfyui"), lambda _: None, media_store
Settings(comfyui_url="http://fake-comfyui"), notifications.append, media_store
).standard_tools[0]
params = FakeFunctionCallParams({"prompt": "a cat"})
@ -58,3 +60,4 @@ async def test_generate_image_tool_reports_comfyui_errors_without_storing_media(
assert media_store == {}
assert "error" in params.results[0]
assert [n["type"] for n in notifications] == ["activity", "activity"]

View file

@ -88,6 +88,7 @@ function receive(event) {
let message;
try { message = JSON.parse(event.data); } catch { return; }
if (message.type === "state") state(message.state);
if (message.type === "activity" && !muted) $("status").textContent = message.text;
if (message.type === "partial") $("partial").textContent = message.text;
if (message.type === "transcript") addMessage(message);
if (message.type === "image") addImage(message.url);

File diff suppressed because one or more lines are too long