from quart import Blueprint, jsonify, request from fabledassistant.auth import login_required, get_current_user_id from fabledassistant.services.embeddings import semantic_search_notes search_bp = Blueprint("search", __name__, url_prefix="/api/search") def _content_type_to_is_task(content_type: str) -> bool | None: """Map content_type query param to semantic_search_notes is_task arg.""" if content_type == "note": return False if content_type == "task": return True return None # "all" or unknown → no filter @search_bp.route("", methods=["GET"]) @login_required async def search_route(): uid = get_current_user_id() q = (request.args.get("q") or "").strip() if not q: return jsonify({"error": "q is required"}), 400 content_type = request.args.get("content_type", "all") limit = min(request.args.get("limit", 10, type=int), 50) is_task = _content_type_to_is_task(content_type) results = await semantic_search_notes( uid, q, limit=limit, is_task=is_task, threshold=0.3 ) return jsonify({ "results": [ { "id": note.id, "title": note.title, "body": note.body or "", "is_task": note.is_task, "tags": note.tags or [], "similarity": score, } for score, note in results # semantic_search_notes returns list[tuple[float, Note]] ], "total": len(results), })