Spaces:
Sleeping
Sleeping
Upload 3 files
Browse files
metadata_search/research_host_app.py
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""Research-enabled host for the existing WaterScope-AI search application.
|
| 3 |
+
|
| 4 |
+
IMPORTANT:
|
| 5 |
+
- Existing metadata_search/app.py is NOT modified.
|
| 6 |
+
- Existing /api/search and all production routes remain unchanged.
|
| 7 |
+
- This host imports the production Flask app, registers research-only routes,
|
| 8 |
+
and then starts the same production application.
|
| 9 |
+
|
| 10 |
+
Use this file only when the research API is needed.
|
| 11 |
+
"""
|
| 12 |
+
|
| 13 |
+
from __future__ import annotations
|
| 14 |
+
|
| 15 |
+
# Importing production app first preserves its normal dataset bootstrap,
|
| 16 |
+
# configuration resolution, routes, CORS handling, and startup behavior.
|
| 17 |
+
from metadata_search.app import app, main
|
| 18 |
+
from metadata_search.research_routes import research_bp
|
| 19 |
+
|
| 20 |
+
# Avoid accidental duplicate registration during interactive imports.
|
| 21 |
+
if "research_search" not in app.blueprints:
|
| 22 |
+
app.register_blueprint(research_bp)
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
if __name__ == "__main__":
|
| 26 |
+
main()
|
metadata_search/research_routes.py
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Research-only Flask routes for configurable WaterScope-AI retrieval experiments.
|
| 2 |
+
|
| 3 |
+
This module is a Blueprint so it can be attached to the existing production
|
| 4 |
+
Flask application without changing any production search route or search logic.
|
| 5 |
+
"""
|
| 6 |
+
|
| 7 |
+
from __future__ import annotations
|
| 8 |
+
|
| 9 |
+
from flask import Blueprint, jsonify, request
|
| 10 |
+
|
| 11 |
+
from metadata_search.config import PAPER_CSV
|
| 12 |
+
from metadata_search.research_search import research_search
|
| 13 |
+
|
| 14 |
+
research_bp = Blueprint("research_search", __name__, url_prefix="/api/research")
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
def _float_arg(name: str, default: float) -> float:
|
| 18 |
+
raw = request.args.get(name)
|
| 19 |
+
return default if raw in {None, ""} else float(raw)
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
def _int_arg(name: str, default: int | None = None) -> int | None:
|
| 23 |
+
raw = request.args.get(name)
|
| 24 |
+
if raw in {None, ""}:
|
| 25 |
+
return default
|
| 26 |
+
return int(raw)
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
@research_bp.get("/health")
|
| 30 |
+
def research_health():
|
| 31 |
+
return jsonify(
|
| 32 |
+
{
|
| 33 |
+
"status": "ok",
|
| 34 |
+
"service": "WaterScopeAI Research Search",
|
| 35 |
+
"paper_csv": str(PAPER_CSV),
|
| 36 |
+
}
|
| 37 |
+
)
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
@research_bp.get("/search")
|
| 41 |
+
def api_research_search():
|
| 42 |
+
query = request.args.get("q", "").strip()
|
| 43 |
+
if not query:
|
| 44 |
+
return jsonify({"error": "q is required"}), 400
|
| 45 |
+
|
| 46 |
+
try:
|
| 47 |
+
payload = research_search(
|
| 48 |
+
query,
|
| 49 |
+
top_k=int(request.args.get("top_k", "100")),
|
| 50 |
+
bm25_weight=_float_arg("bm25_weight", 0.5),
|
| 51 |
+
semantic_weight=_float_arg("semantic_weight", 0.5),
|
| 52 |
+
rrf_k=int(request.args.get("rrf_k", "60")),
|
| 53 |
+
year_from=_int_arg("year_from"),
|
| 54 |
+
year_to=_int_arg("year_to"),
|
| 55 |
+
)
|
| 56 |
+
return jsonify(payload)
|
| 57 |
+
except (TypeError, ValueError) as exc:
|
| 58 |
+
return jsonify({"error": str(exc)}), 400
|
| 59 |
+
except Exception as exc:
|
| 60 |
+
return jsonify({"error": f"{type(exc).__name__}: {exc}"}), 500
|