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:
@@ -29,7 +29,7 @@ async def list_conversations_route():
|
||||
offset = request.args.get("offset", 0, type=int)
|
||||
conversations, total = await list_conversations(limit=limit, offset=offset)
|
||||
return jsonify({
|
||||
"conversations": [c.to_dict() for c in conversations],
|
||||
"conversations": conversations,
|
||||
"total": total,
|
||||
})
|
||||
|
||||
@@ -180,7 +180,7 @@ async def chat_status_route():
|
||||
resp.raise_for_status()
|
||||
result["ollama"] = "available"
|
||||
data = resp.json()
|
||||
model_names = [m["name"] for m in data.get("models", [])]
|
||||
model_names = {m["name"] for m in data.get("models", [])}
|
||||
# Match with or without :latest tag
|
||||
if default_model in model_names or f"{default_model}:latest" in model_names:
|
||||
result["model"] = "ready"
|
||||
|
||||
@@ -1,29 +1,11 @@
|
||||
import httpx
|
||||
from quart import Blueprint, jsonify, request
|
||||
|
||||
from fabledassistant.config import Config
|
||||
from fabledassistant.services.settings import get_all_settings, set_setting
|
||||
from fabledassistant.services.llm import get_installed_models
|
||||
from fabledassistant.services.settings import get_all_settings, set_settings_batch
|
||||
|
||||
settings_bp = Blueprint("settings", __name__, url_prefix="/api/settings")
|
||||
|
||||
|
||||
async def _get_installed_models() -> set[str]:
|
||||
"""Return set of installed Ollama model names, with and without :latest."""
|
||||
try:
|
||||
async with httpx.AsyncClient(timeout=5.0) as client:
|
||||
resp = await client.get(f"{Config.OLLAMA_URL}/api/tags")
|
||||
resp.raise_for_status()
|
||||
data = resp.json()
|
||||
names: set[str] = set()
|
||||
for m in data.get("models", []):
|
||||
name = m["name"]
|
||||
names.add(name)
|
||||
names.add(name.replace(":latest", ""))
|
||||
return names
|
||||
except Exception:
|
||||
return set()
|
||||
|
||||
|
||||
@settings_bp.route("", methods=["GET"])
|
||||
async def get_settings_route():
|
||||
settings = await get_all_settings()
|
||||
@@ -38,11 +20,10 @@ async def update_settings_route():
|
||||
|
||||
if "default_model" in data:
|
||||
model = str(data["default_model"])
|
||||
installed = await _get_installed_models()
|
||||
installed = await get_installed_models()
|
||||
if installed and model not in installed:
|
||||
return jsonify({"error": f"Model '{model}' is not installed"}), 400
|
||||
|
||||
for key, value in data.items():
|
||||
await set_setting(key, str(value))
|
||||
await set_settings_batch({k: str(v) for k, v in data.items()})
|
||||
settings = await get_all_settings()
|
||||
return jsonify(settings)
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user