refactor(tools): consolidate LLM tools from 42 to 38
Merge create_task into create_note (set status='todo' for tasks, omit for notes), merge delete_task into delete_note, consolidate entity tools (create/update_person → save_person, create/update_place → save_place), rename get_note → read_note with clearer descriptions, move calculate out of rag.py into utility.py, and extract shared duplicate detection into check_duplicate() helper. Updates all downstream references in generation_task.py, quick_capture.py, ToolCallCard.vue, and WorkspaceView.vue. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -75,3 +75,50 @@ def fuzzy_title_match(title: str, candidates, threshold: float = 0.82):
|
||||
if r >= threshold and r > best_r:
|
||||
best, best_r = c, r
|
||||
return best, best_r
|
||||
|
||||
|
||||
async def check_duplicate(
|
||||
user_id: int,
|
||||
title: str,
|
||||
body: str,
|
||||
is_task: bool,
|
||||
confirmed: bool,
|
||||
) -> dict | None:
|
||||
"""Check for exact, fuzzy, and semantic duplicates. Returns error dict or None."""
|
||||
from fabledassistant.services.notes import list_notes
|
||||
|
||||
item_label = "task" if is_task else "note"
|
||||
|
||||
existing, _ = await list_notes(user_id=user_id, q=title, is_task=is_task, limit=1)
|
||||
exact = next((n for n in existing if n.title.lower() == title.lower()), None)
|
||||
if exact is not None:
|
||||
return {
|
||||
"success": False,
|
||||
"error": f"A {item_label} titled '{title}' already exists (id: {exact.id}). Use update_note to modify it instead of creating a duplicate.",
|
||||
}
|
||||
|
||||
clean_q = _PUNCT_RE.sub(" ", title).strip()
|
||||
candidates, _ = await list_notes(user_id=user_id, q=clean_q, is_task=is_task, limit=20)
|
||||
near, ratio = fuzzy_title_match(title, candidates)
|
||||
if near is not None:
|
||||
return {
|
||||
"success": False,
|
||||
"requires_confirmation": True,
|
||||
"similar_note": {"id": near.id, "title": near.title},
|
||||
"error": f"A {item_label} with a very similar title '{near.title}' already exists (similarity: {ratio:.0%}). Ask the user to confirm before creating a separate entry.",
|
||||
}
|
||||
|
||||
if not confirmed and len(body.strip()) >= 80:
|
||||
from fabledassistant.services.embeddings import semantic_search_notes as _ssn
|
||||
sem_query = f"{title}\n{body}".strip()
|
||||
sem_hits = await _ssn(user_id, sem_query, limit=3, threshold=0.90, is_task=is_task)
|
||||
if sem_hits:
|
||||
best_score, best_note = sem_hits[0]
|
||||
return {
|
||||
"success": False,
|
||||
"requires_confirmation": True,
|
||||
"similar_note": {"id": best_note.id, "title": best_note.title},
|
||||
"error": f"A {item_label} with very similar content exists: '{best_note.title}' (semantic similarity: {best_score:.0%}). Ask the user to confirm before creating a separate entry.",
|
||||
}
|
||||
|
||||
return None
|
||||
|
||||
Reference in New Issue
Block a user