mirror of
https://github.com/Jeuners/astra-vision.git
synced 2026-09-09 15:02:35 +02:00
feat: show RSS headlines as clickable links in the transcript
read_news bisher zeigte nur Klartext-Titel in der Karte an — man konnte im Chat selbst nicht auf den Artikel klicken (nur das Modell kannte den Link, für ein mögliches read_article danach). Jetzt trägt der tool_result-Event zusätzlich strukturierte 'items' (source, title, link), das Frontend rendert daraus eine Liste mit echten <a>-Links statt reinem Text. read_article bleibt unverändert bei reinem Text (items bleibt leer, kein Link zum Verlinken vorhanden). Verifiziert: die Rendering-Logik direkt per simuliertem Datenkanal- Event getestet (unabhängig von der Modell-Zuverlässigkeit, die bei read_news aktuell bei ~15-20% liegt) — zwei Links korrekt mit href, target=_blank, rel=noopener und Quelle gerendert. NICHT gepusht — weiterhin nur lokal, wie angewiesen. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LVgSHNHdRx3UNTBodFmhRA
This commit is contained in:
parent
0d2e826dd3
commit
b0282900d2
4 changed files with 46 additions and 9 deletions
|
|
@ -47,8 +47,15 @@ def build_tools(config: Settings, notify: Callable[[dict], None], media_store: d
|
|||
notify({"type": "tool_error", "text": str(exc)})
|
||||
await params.result_callback({"error": str(exc)})
|
||||
return
|
||||
headlines = "\n".join(f"- ({entry.source}) {entry.title}" for entry in entries)
|
||||
notify({"type": "tool_result", "text": headlines})
|
||||
notify(
|
||||
{
|
||||
"type": "tool_result",
|
||||
"text": "\n".join(f"- ({entry.source}) {entry.title}" for entry in entries),
|
||||
"items": [
|
||||
{"source": e.source, "title": e.title, "link": e.link} for e in entries
|
||||
],
|
||||
}
|
||||
)
|
||||
await params.result_callback(
|
||||
{
|
||||
"status": "ok",
|
||||
|
|
|
|||
|
|
@ -98,6 +98,10 @@ async def test_read_news_tool_reports_headlines(monkeypatch):
|
|||
assert [n["type"] for n in notifications] == ["activity", "tool_start", "tool_result"]
|
||||
assert "KI-Durchbruch" in notifications[2]["text"]
|
||||
assert "Neuer Chip" in notifications[2]["text"]
|
||||
assert notifications[2]["items"] == [
|
||||
{"source": "heise online", "title": "KI-Durchbruch", "link": "https://x"},
|
||||
{"source": "Golem.de", "title": "Neuer Chip", "link": "https://y"},
|
||||
]
|
||||
assert params.results[0]["status"] == "ok"
|
||||
assert len(params.results[0]["headlines"]) == 2
|
||||
|
||||
|
|
|
|||
38
web/app.js
38
web/app.js
|
|
@ -62,7 +62,7 @@ function addToolError(text) {
|
|||
}
|
||||
showError(text);
|
||||
}
|
||||
function addToolResult(text) {
|
||||
function addToolResult(text, items) {
|
||||
const pending = document.getElementById("pending-tool");
|
||||
const article = pending || document.createElement("article");
|
||||
if (pending) {
|
||||
|
|
@ -76,10 +76,36 @@ function addToolResult(text) {
|
|||
const speaker = document.createElement("span");
|
||||
speaker.className = "speaker";
|
||||
speaker.textContent = "Astra";
|
||||
const body = document.createElement("p");
|
||||
body.className = "tool-result";
|
||||
body.textContent = text;
|
||||
article.append(speaker, body);
|
||||
article.append(speaker);
|
||||
if (items?.length) {
|
||||
const list = document.createElement("ul");
|
||||
list.className = "tool-result-list";
|
||||
for (const item of items) {
|
||||
const li = document.createElement("li");
|
||||
if (item.link) {
|
||||
const link = document.createElement("a");
|
||||
link.href = item.link;
|
||||
link.target = "_blank";
|
||||
link.rel = "noopener";
|
||||
link.textContent = item.title;
|
||||
li.append(link);
|
||||
} else {
|
||||
li.textContent = item.title;
|
||||
}
|
||||
if (item.source) {
|
||||
const source = document.createElement("small");
|
||||
source.textContent = ` (${item.source})`;
|
||||
li.append(source);
|
||||
}
|
||||
list.append(li);
|
||||
}
|
||||
article.append(list);
|
||||
} else {
|
||||
const body = document.createElement("p");
|
||||
body.className = "tool-result";
|
||||
body.textContent = text;
|
||||
article.append(body);
|
||||
}
|
||||
if (!pending) $("messages").append(article);
|
||||
$("messages").scrollTop = $("messages").scrollHeight;
|
||||
}
|
||||
|
|
@ -148,7 +174,7 @@ function receive(event) {
|
|||
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 === "tool_result") addToolResult(message.text);
|
||||
if (message.type === "tool_result") addToolResult(message.text, message.items);
|
||||
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