Add duplicate entry prevention to LLM tool pipeline
Three-layer guard on create_note and create_task: 1. Exact title match (get_note_by_title, case-insensitive) → hard block, redirects model to use update_note instead 2. Fuzzy title match (SequenceMatcher ≥82%, punct-stripped word search for candidates) → hard block; catches "Game Premise" vs "Game Premise Notes" 3. Semantic content similarity (semantic_search_notes threshold=0.87) → soft block with requires_confirmation:true; model asks user then retries with confirmed:true; graceful no-op if embedding model is unavailable create_project and create_milestone now always require confirmed:true before creation (structural entities — must be intentional). Both are also guarded by exact + fuzzy title checks post-confirmation. create_note and create_task gain an optional confirmed parameter (not required by default; only used to bypass a content-similarity soft-block after the user has been asked). Helpers added to tools.py: - _fuzzy_title_match(title, candidates, threshold=0.82) - _PUNCT_RE for stripping punctuation before word-search candidate fetch - difflib.SequenceMatcher and re imported at module level Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+11
-2
@@ -12,7 +12,7 @@
|
||||
> Include file-level details in the commit body when the change is non-trivial.
|
||||
|
||||
## Last Updated
|
||||
2026-03-03 — Project/milestone association bug fixes (tasks route was silently dropping project_id/milestone_id); PATCH /api/notes endpoint; parent_title in task API response; milestone_id support in NoteEditorView + notes store; sub-tasks inherit milestone; list_notes OR query for project view; AppHeader redesigned (centered nav, gear dropdown for settings)
|
||||
2026-03-03 — Duplicate entry prevention in LLM tool pipeline: exact-title block, fuzzy-title block (SequenceMatcher ≥82%), semantic content check (≥87% cosine similarity via existing `semantic_search_notes`); `create_project`/`create_milestone` require explicit `confirmed:true`; `create_note`/`create_task` have optional `confirmed` for content-similarity soft-block; `create_project`/`create_milestone` also protected by exact+fuzzy title checks
|
||||
|
||||
## Project Overview
|
||||
Fabled Assistant is a self-hosted note-taking and task-tracking application with
|
||||
@@ -612,7 +612,15 @@ When adding a new migration, follow these conventions:
|
||||
System prompt includes today's date for relative date resolution. Graceful degradation: models without
|
||||
tool support respond normally.
|
||||
Full tool suite:
|
||||
- `create_task` / `create_note` — create new items
|
||||
- `create_task` / `create_note` — create new items; protected by a three-layer duplicate guard:
|
||||
1. **Exact title** (case-insensitive via `get_note_by_title`) → hard block, redirect to `update_note`
|
||||
2. **Fuzzy title** (`SequenceMatcher` ≥82%; strips punctuation before word search for candidates) → hard block
|
||||
3. **Semantic content** (`semantic_search_notes` threshold 0.87, graceful no-op if embedding model down) → soft block with `requires_confirmation:true`; model must ask user then retry with `confirmed:true`
|
||||
Both tools have an optional `confirmed` parameter (not required by default — only needed when a content-similarity warning fires).
|
||||
- `create_project` — always requires `confirmed:true` before creation (structural, must be intentional);
|
||||
additionally guarded by exact + fuzzy title checks (≥82%) against existing projects
|
||||
- `create_milestone` — always requires `confirmed:true`; additionally guarded by exact + fuzzy title
|
||||
checks against existing milestones within the same project
|
||||
- `update_note` — edit note content (replace/append) AND update task fields: `status`, `priority`,
|
||||
`due_date`, `tags` (with `tag_mode`: replace/add/remove); finds by exact title first, falls back to fuzzy search
|
||||
- `delete_note` / `delete_task` — permanently delete (require user confirmation via confirm UI;
|
||||
@@ -625,6 +633,7 @@ When adding a new migration, follow these conventions:
|
||||
- Full CalDAV suite: `create_event`, `list_events`, `search_events`, `update_event`, `delete_event`,
|
||||
`list_calendars`, `create_todo`, `list_todos`, `search_todos`, `update_todo`, `complete_todo`, `delete_todo`
|
||||
(`search_todos` keyword-filters the todo list — companion to `list_todos`)
|
||||
- **Duplicate guard helpers** in `services/tools.py`: `_fuzzy_title_match(title, candidates, threshold=0.82)` uses `SequenceMatcher`; semantic check uses `semantic_search_notes` at threshold 0.87 (vs. 0.45 for RAG suggestions). `_PUNCT_RE` strips punctuation from title before word-search to avoid false-negative candidates (e.g. "Shackled - Game Premise" → "Shackled Game Premise" query).
|
||||
- **Streaming status transparency:** The backend emits `status` SSE events at each pipeline stage
|
||||
so the user always sees what's happening instead of a blank progress dot. Stages:
|
||||
(1) intent ack text streamed as a `chunk` event (tool responses — TTFT ~400ms) or
|
||||
|
||||
Reference in New Issue
Block a user