Fix queued bubbles, queue persistence, duplicate confirm UX, semantic check
- Queued message bubbles now match user bubble style (primary colour, right-aligned, opacity 0.45) in both ChatView and WorkspaceView - Queue persisted to localStorage (fa_conv_queue_<id>); survives page refresh, restored on fetchConversation, cleared on delete - ToolCallCard confirm/deny: buttons now inline in header row; "Create anyway" calls POST /api/notes or /api/tasks directly (no LLM round-trip); shows "✓ Created" / "Skipped" state; removed chatStore dependency - POST /api/notes and POST /api/tasks now accept project (name string) and resolve to project_id server-side, matching tools.py behaviour - Remove semantic similarity duplicate check from create_note and create_task — 0.87 threshold was too aggressive for topically-related notes; title-based exact/fuzzy checks are sufficient Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -92,13 +92,20 @@ async def create_note_route():
|
||||
if isinstance(due_date, tuple):
|
||||
return due_date
|
||||
|
||||
project_id = data.get("project_id")
|
||||
if project_id is None and data.get("project"):
|
||||
from fabledassistant.services.projects import get_project_by_title as _gpbt
|
||||
proj = await _gpbt(uid, data["project"])
|
||||
if proj:
|
||||
project_id = proj.id
|
||||
|
||||
note = await create_note(
|
||||
uid,
|
||||
title=data.get("title", ""),
|
||||
body=body,
|
||||
tags=tags,
|
||||
parent_id=data.get("parent_id"),
|
||||
project_id=data.get("project_id"),
|
||||
project_id=project_id,
|
||||
milestone_id=data.get("milestone_id"),
|
||||
status=status,
|
||||
priority=priority,
|
||||
|
||||
@@ -68,6 +68,13 @@ async def create_task_route():
|
||||
TaskPriority(data["priority"]).value if "priority" in data else TaskPriority.none.value
|
||||
)
|
||||
|
||||
project_id = data.get("project_id")
|
||||
if project_id is None and data.get("project"):
|
||||
from fabledassistant.services.projects import get_project_by_title as _gpbt
|
||||
proj = await _gpbt(uid, data["project"])
|
||||
if proj:
|
||||
project_id = proj.id
|
||||
|
||||
task = await create_note(
|
||||
uid,
|
||||
title=data.get("title", ""),
|
||||
@@ -76,7 +83,7 @@ async def create_task_route():
|
||||
priority=priority,
|
||||
due_date=due_date,
|
||||
tags=tags,
|
||||
project_id=data.get("project_id"),
|
||||
project_id=project_id,
|
||||
milestone_id=data.get("milestone_id"),
|
||||
parent_id=data.get("parent_id"),
|
||||
)
|
||||
|
||||
@@ -825,15 +825,6 @@ 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()) >= 80:
|
||||
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.87)
|
||||
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,
|
||||
@@ -903,15 +894,6 @@ 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()) >= 80:
|
||||
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.87)
|
||||
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,
|
||||
|
||||
Reference in New Issue
Block a user