Backend efficiency & consistency pass

- Rewrite get_all_tags() with SQL unnest instead of loading all notes
- Consolidate convert_note_to_task/convert_task_to_note to single-session ops
- Add search_notes_for_context() with single OR-keyword query for build_context()
- Drop selectinload from list_conversations(), use correlated subquery for message_count
- Add set_settings_batch() for single-transaction multi-setting upserts
- Extract get_installed_models() shared helper into services/llm.py
- Delete services/tasks.py pass-through wrapper; routes/tasks.py imports from services.notes
- Add B-tree indexes on notes.title and conversations.updated_at (migration 0007)
- Add logging to services/notes.py, services/chat.py, services/settings.py
- Safe Conversation.to_dict() when messages relationship is not loaded

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-11 08:27:11 -05:00
parent fb18d2c41d
commit db01106714
12 changed files with 225 additions and 174 deletions
+13 -12
View File
@@ -3,12 +3,12 @@ from datetime import date
from quart import Blueprint, jsonify, request
from fabledassistant.models.note import TaskPriority, TaskStatus
from fabledassistant.services.tasks import (
create_task,
delete_task,
get_task,
list_tasks,
update_task,
from fabledassistant.services.notes import (
create_note,
delete_note,
get_note,
list_notes,
update_note,
)
from fabledassistant.utils.tags import extract_tags
@@ -26,9 +26,10 @@ async def list_tasks_route():
limit = request.args.get("limit", 50, type=int)
offset = request.args.get("offset", 0, type=int)
tasks, total = await list_tasks(
tasks, total = await list_notes(
q=q,
tags=tag or None,
is_task=True,
status=status,
priority=priority,
sort=sort,
@@ -54,7 +55,7 @@ async def create_task_route():
TaskPriority(data["priority"]).value if "priority" in data else TaskPriority.none.value
)
task = await create_task(
task = await create_note(
title=data.get("title", ""),
body=body,
status=status,
@@ -67,7 +68,7 @@ async def create_task_route():
@tasks_bp.route("/<int:task_id>", methods=["GET"])
async def get_task_route(task_id: int):
task = await get_task(task_id)
task = await get_note(task_id)
if task is None:
return jsonify({"error": "Task not found"}), 404
return jsonify(task.to_dict())
@@ -95,7 +96,7 @@ async def update_task_route(task_id: int):
if "body" in fields:
fields["tags"] = extract_tags(fields["body"])
task = await update_task(task_id, **fields)
task = await update_note(task_id, **fields)
if task is None:
return jsonify({"error": "Task not found"}), 404
return jsonify(task.to_dict())
@@ -113,7 +114,7 @@ async def patch_task_status(task_id: int):
except ValueError:
return jsonify({"error": f"Invalid status: {status_val}"}), 400
task = await update_task(task_id, status=status_val)
task = await update_note(task_id, status=status_val)
if task is None:
return jsonify({"error": "Task not found"}), 404
return jsonify(task.to_dict())
@@ -121,7 +122,7 @@ async def patch_task_status(task_id: int):
@tasks_bp.route("/<int:task_id>", methods=["DELETE"])
async def delete_task_route(task_id: int):
deleted = await delete_task(task_id)
deleted = await delete_note(task_id)
if not deleted:
return jsonify({"error": "Task not found"}), 404
return "", 204