starfriend commited on
Commit
2aa40d1
·
verified ·
1 Parent(s): 377cfb8

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