From 1890f0c7f1ec99227df0ed0e90a267aee800875b Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Sun, 1 Mar 2026 13:19:59 -0500 Subject: [PATCH] Fix image search: embed markdown instead of describing URLs - tools.py: search_images result now includes 'embed' (ready-to-use markdown image syntax) and 'citation' fields instead of raw 'local_url'; adds 'instructions' field so the model knows to render them verbatim - llm.py: system prompt now explicitly tells the model to embed images using the 'embed' field rather than describing or listing URLs - markdown.ts: explicitly allow src/alt in PURIFY_OPTS_FULL so img tags are never stripped by DOMPurify Co-Authored-By: Claude Sonnet 4.6 --- frontend/src/utils/markdown.ts | 2 +- src/fabledassistant/services/llm.py | 5 +++++ src/fabledassistant/services/tools.py | 20 +++++++++++++++----- 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/frontend/src/utils/markdown.ts b/frontend/src/utils/markdown.ts index 124fa39..925d626 100644 --- a/frontend/src/utils/markdown.ts +++ b/frontend/src/utils/markdown.ts @@ -37,7 +37,7 @@ const headingRenderer = { marked.use({ renderer: headingRenderer }); const PURIFY_OPTS_FULL = { - ADD_ATTR: ["data-tag", "data-title"], + ADD_ATTR: ["data-tag", "data-title", "src", "alt"], FORCE_BODY: true, }; diff --git a/src/fabledassistant/services/llm.py b/src/fabledassistant/services/llm.py index 8d00305..34781ad 100644 --- a/src/fabledassistant/services/llm.py +++ b/src/fabledassistant/services/llm.py @@ -398,6 +398,11 @@ async def build_context( tool_lines.append("Use search_todos to find a specific CalDAV todo by keyword when list_todos would return too many results.") tool_lines.append("For relative dates like 'Friday' or 'next week', resolve them to YYYY-MM-DD format.") tool_lines.append("When creating notes, use the `tags` parameter — do not embed #tag text in the note body.") + tool_lines.append( + "When search_images returns results, embed each image directly in your response by writing " + "the 'embed' field verbatim (e.g. ![Cat](/api/images/3)), then the 'citation' field on the " + "next line. Never describe images as text or list their URLs — always render them as markdown images." + ) tool_lines.append( "Use update_note to edit/expand an existing note OR to update a task's status/priority/due_date. " "Use create_note ONLY for genuinely new notes with a different title. " diff --git a/src/fabledassistant/services/tools.py b/src/fabledassistant/services/tools.py index e586039..64fbf1c 100644 --- a/src/fabledassistant/services/tools.py +++ b/src/fabledassistant/services/tools.py @@ -1264,18 +1264,28 @@ async def execute_tool(user_id: int, tool_name: str, arguments: dict) -> dict: source_domain=r.get("source_domain"), ) if record: + title = record.title or query + page_url = r.get("page_url", "") + source_domain = r.get("source_domain", "") images.append({ - "local_url": f"/api/images/{record.id}", - "page_url": r.get("page_url", ""), - "source_domain": r.get("source_domain", ""), - "title": record.title or query, + "embed": f"![{title}](/api/images/{record.id})", + "citation": f"*Source: [{source_domain or 'image'}]({page_url})*", + "page_url": page_url, + "title": title, }) if not images: return {"success": False, "error": f"Could not retrieve images for '{query}'"} return { "success": True, "type": "image_search", - "data": {"query": query, "images": images}, + "data": { + "query": query, + "images": images, + "instructions": ( + "Embed each image in your response by writing the 'embed' field verbatim, " + "then the 'citation' field on the next line. Do not paraphrase or list URLs." + ), + }, } else: