import time from quart import Blueprint, jsonify, request from scribe.auth import login_required, get_current_user_id from scribe.services.access import owner_names_for from scribe.services.embeddings import semantic_search_notes from scribe.services.retrieval_telemetry import record_retrieval # This route searches with a looser floor than the MCP tool default — it powers # an interactive feed where loosely-related hits still have value. _REST_SEARCH_THRESHOLD = 0.3 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) t0 = time.perf_counter() results = await semantic_search_notes( uid, q, limit=limit, is_task=is_task, threshold=_REST_SEARCH_THRESHOLD, # The user typed this, so it reaches everything they may read. scope="read", ) record_retrieval( user_id=uid, source="rest_search", query=q, threshold=_REST_SEARCH_THRESHOLD, limit=limit, project_id=None, is_task=is_task, results=results, duration_ms=(time.perf_counter() - t0) * 1000.0, ) owners = await owner_names_for( {int(note.user_id) for _s, note in results if note.user_id != uid} ) return jsonify({ "results": [ { "id": note.id, "title": note.title, "body": note.body or "", "is_task": note.is_task, "tags": note.tags or [], "similarity": score, **( {"shared": True, "owner": owners.get(int(note.user_id))} if note.user_id != uid else {} ), } for score, note in results # semantic_search_notes returns list[tuple[float, Note]] ], "total": len(results), })