mirror of
https://github.com/Jeuners/astra-vision.git
synced 2026-09-09 15:02:35 +02:00
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
This commit is contained in:
parent
6854e80703
commit
76a5d4e499
4 changed files with 53 additions and 7 deletions
|
|
@ -18,10 +18,12 @@ 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 …"})
|
||||
notify({"type": "tool_start", "text": f'Anfrage an ComfyUI: "{prompt}"'})
|
||||
try:
|
||||
image = await generate_image(config.comfyui_url, prompt)
|
||||
except ComfyUIError as exc:
|
||||
notify({"type": "activity", "text": "Bilderzeugung fehlgeschlagen."})
|
||||
notify({"type": "tool_error", "text": f"ComfyUI-Anfrage fehlgeschlagen: {exc}"})
|
||||
await params.result_callback({"error": str(exc)})
|
||||
return
|
||||
image_id = uuid.uuid4().hex
|
||||
|
|
|
|||
|
|
@ -37,8 +37,9 @@ 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[0]["type"] == "activity"
|
||||
assert notifications[1] == {"type": "image", "url": f"/api/media/{image_id}"}
|
||||
assert [n["type"] for n in notifications] == ["activity", "tool_start", "image"]
|
||||
assert "a cat" in notifications[1]["text"]
|
||||
assert notifications[2] == {"type": "image", "url": f"/api/media/{image_id}"}
|
||||
assert params.results[0]["status"] == "ok"
|
||||
|
||||
|
||||
|
|
@ -60,4 +61,9 @@ 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"]
|
||||
assert [n["type"] for n in notifications] == [
|
||||
"activity",
|
||||
"tool_start",
|
||||
"activity",
|
||||
"tool_error",
|
||||
]
|
||||
|
|
|
|||
44
web/app.js
44
web/app.js
|
|
@ -33,10 +33,46 @@ function addMessage(event) {
|
|||
while ($("messages").children.length > 80) $("messages").firstElementChild.remove();
|
||||
$("messages").scrollTop = $("messages").scrollHeight;
|
||||
}
|
||||
function addImage(url) {
|
||||
function addToolStart(text) {
|
||||
$("messages").querySelector(".empty")?.remove();
|
||||
document.getElementById("pending-tool")?.remove();
|
||||
const article = document.createElement("article");
|
||||
article.className = "message assistant";
|
||||
article.className = "message assistant pending-tool";
|
||||
article.id = "pending-tool";
|
||||
const speaker = document.createElement("span");
|
||||
speaker.className = "speaker";
|
||||
speaker.textContent = "Astra";
|
||||
const body = document.createElement("p");
|
||||
body.className = "pending-text";
|
||||
const spinner = document.createElement("i");
|
||||
spinner.className = "spinner";
|
||||
body.append(spinner, document.createTextNode(` ${text}`));
|
||||
article.append(speaker, body);
|
||||
$("messages").append(article);
|
||||
$("messages").scrollTop = $("messages").scrollHeight;
|
||||
}
|
||||
function addToolError(text) {
|
||||
const pending = document.getElementById("pending-tool");
|
||||
if (pending) {
|
||||
pending.removeAttribute("id");
|
||||
pending.classList.remove("pending-tool");
|
||||
pending.querySelector(".pending-text").textContent = text;
|
||||
$("messages").scrollTop = $("messages").scrollHeight;
|
||||
return;
|
||||
}
|
||||
showError(text);
|
||||
}
|
||||
function addImage(url) {
|
||||
const pending = document.getElementById("pending-tool");
|
||||
const article = pending || document.createElement("article");
|
||||
if (pending) {
|
||||
pending.removeAttribute("id");
|
||||
pending.className = "message assistant";
|
||||
pending.replaceChildren();
|
||||
} else {
|
||||
$("messages").querySelector(".empty")?.remove();
|
||||
article.className = "message assistant";
|
||||
}
|
||||
const speaker = document.createElement("span");
|
||||
speaker.className = "speaker";
|
||||
speaker.textContent = "Astra";
|
||||
|
|
@ -45,7 +81,7 @@ function addImage(url) {
|
|||
img.src = url;
|
||||
img.alt = "Von Astra erzeugtes Bild";
|
||||
article.append(speaker, img);
|
||||
$("messages").append(article);
|
||||
if (!pending) $("messages").append(article);
|
||||
$("messages").scrollTop = $("messages").scrollHeight;
|
||||
}
|
||||
function addUploadNote(filename) {
|
||||
|
|
@ -89,6 +125,8 @@ function receive(event) {
|
|||
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 === "tool_start") addToolStart(message.text);
|
||||
if (message.type === "tool_error") addToolError(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
Loading…
Add table
Add a link
Reference in a new issue