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
+4 -23
View File
@@ -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)