From a90342c9cb8c3901e7bfa69d6a3d483ffe6f276d Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Tue, 10 Mar 2026 08:20:20 -0400 Subject: [PATCH] Restore semantic duplicate check at 0.90 threshold, 200-char minimum MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Raises body character limit from 80 → 200 (skips short/stub notes) and threshold from 0.87 → 0.90 (only flags near-identical content). Enemy design notes sharing a template will pass through cleanly; accidentally duplicated notes with the same detailed content will still trigger the confirmation prompt. Co-Authored-By: Claude Sonnet 4.6 --- src/fabledassistant/services/tools.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/fabledassistant/services/tools.py b/src/fabledassistant/services/tools.py index 3e54998..14cfced 100644 --- a/src/fabledassistant/services/tools.py +++ b/src/fabledassistant/services/tools.py @@ -825,6 +825,15 @@ async def execute_tool(user_id: int, tool_name: str, arguments: dict) -> dict: item_type = "task" if near.status is not None else "note" return {"success": False, "requires_confirmation": True, "similar_note": {"id": near.id, "title": near.title}, "error": f"A {item_type} with a very similar title '{near.title}' already exists (similarity: {ratio:.0%}). Ask the user to confirm before creating a separate entry."} + if not arguments.get("confirmed") and len(task_body.strip()) >= 200: + from fabledassistant.services.embeddings import semantic_search_notes as _ssn + sem_query = f"{task_title}\n{task_body}".strip() + sem_hits = await _ssn(user_id, sem_query, limit=3, threshold=0.90) + if sem_hits: + best_score, best_note = sem_hits[0] + item_type = "task" if best_note.status is not None else "note" + return {"success": False, "requires_confirmation": True, "similar_note": {"id": best_note.id, "title": best_note.title}, "error": f"A {item_type} with very similar content exists: '{best_note.title}' (semantic similarity: {best_score:.0%}). Ask the user to confirm before creating a separate entry."} + note = await create_note( user_id=user_id, title=task_title, @@ -894,6 +903,15 @@ async def execute_tool(user_id: int, tool_name: str, arguments: dict) -> dict: item_type = "task" if near.status is not None else "note" return {"success": False, "requires_confirmation": True, "similar_note": {"id": near.id, "title": near.title}, "error": f"A {item_type} with a very similar title '{near.title}' already exists (similarity: {ratio:.0%}). Ask the user to confirm before creating a separate entry."} + if not arguments.get("confirmed") and len(note_body.strip()) >= 200: + from fabledassistant.services.embeddings import semantic_search_notes as _ssn + sem_query = f"{note_title}\n{note_body}".strip() + sem_hits = await _ssn(user_id, sem_query, limit=3, threshold=0.90) + if sem_hits: + best_score, best_note = sem_hits[0] + item_type = "task" if best_note.status is not None else "note" + return {"success": False, "requires_confirmation": True, "similar_note": {"id": best_note.id, "title": best_note.title}, "error": f"A {item_type} with very similar content exists: '{best_note.title}' (semantic similarity: {best_score:.0%}). Ask the user to confirm before creating a separate entry."} + note = await create_note( user_id=user_id, title=note_title,