fs-lawrisk/app.py

188 lines
7.1 KiB
Python

from __future__ import annotations
import os
from flask import Flask, jsonify, request
from env_loader import load_env
import time
from concurrent.futures import ThreadPoolExecutor
from smart_cors_middleware import init_smart_cors
from lawrisk_service import (
ensure_database,
ensure_schema,
search_subjects,
search_subjects_llm,
shortlist_subjects,
suggest_questions_from_subjects,
suggest_questions_embed,
)
from lawrisk_v2_service import search_v2
def create_app() -> Flask:
# Load .env before creating app to make CORS/env configs available
load_env()
# Ensure DB and schema exist before serving
try:
ensure_database()
ensure_schema()
except Exception:
# Do not block app start; errors will surface on first request
pass
app = Flask(__name__)
# Enable CORS using existing middleware
init_smart_cors(app)
def _extract_params():
if request.method == "GET":
query = request.args.get("query") or request.args.get("q") or request.args.get("text")
debug_flag = request.args.get("debug") in {"1", "true", "yes", "on"}
top_k = request.args.get("top")
try:
top_k_int = int(top_k) if top_k else 5
except Exception:
top_k_int = 5
mode_value = (request.args.get("mode") or "llm").lower()
else:
if request.is_json:
payload = request.get_json(silent=True) or {}
else:
payload = request.form.to_dict(flat=True) if request.form else {}
query = payload.get("query") or payload.get("q") or payload.get("text")
debug_flag = str(payload.get("debug", "")).strip().lower() in {"1", "true", "yes", "on"}
try:
top_k_int = int(payload.get("top", 5))
except Exception:
top_k_int = 5
mode_value = str(payload.get("mode", "llm")).lower()
return query, debug_flag, top_k_int, mode_value
@app.route("/fs-ai-asistant/api/workflow/lawrisk", methods=["POST", "GET"])
def lawrisk_search():
query, debug_flag, top_k_int, mode = _extract_params()
if not query or not isinstance(query, str):
return jsonify({"error": "query is required"}), 400
try:
t0 = time.time()
with ThreadPoolExecutor(max_workers=3) as ex:
fut_ret = ex.submit(
search_subjects if mode == "embed" else search_subjects_llm,
query,
debug_flag,
top_k_int,
)
# Use embedding-based question suggestion (falls back internally if not available)
fut_qs = ex.submit(suggest_questions_embed, query, max(1, top_k_int))
result = fut_ret.result()
rec_questions = fut_qs.result() or []
# If debug requested, still log to backend for visibility
if debug_flag and isinstance(result, dict) and "debug" in result:
dbg = result["debug"]
model = dbg.get("model") or "embed"
app.logger.info("[LAWRISK-DEBUG] mode=%s", model)
# Extract risk_subject and optional debug
risk_subject = []
dbg = {}
if isinstance(result, dict):
risk_subject = result.get("risk_subject", [])
if debug_flag:
dbg = result.get("debug", {})
found = bool(risk_subject)
llm_resp = "" if found else "抱歉,无法检索到相关答案"
exec_time = int((time.time() - t0) * 1000)
# rec_questions 已由 embedding 建议生成(内部包含兜底)
data = {
"llmRespond": llm_resp,
"lawRisk": "",
"questionExtend": rec_questions,
"conversationId": "",
"messageId": "",
"roundNumber": 0,
"conversationInfo": {},
"knowledgeSources": [],
"totalKnowledgeSources": 0,
"executionTime": exec_time,
"workflowStatus": "ok" if found else "no_match",
"executionSteps": [],
"costStatistics": {},
"workflowTrackingId": "",
# extra fields requested
"risk_subject": risk_subject,
"debug": dbg if debug_flag else {},
}
resp = {"success": True, "message": "OK", "data": data}
return jsonify(resp)
except Exception as e:
app.logger.exception("lawrisk_search error")
return jsonify({"success": False, "message": str(e), "data": {}}), 500
@app.route("/fs-ai-asistant/api/workflow/lawrisk/v2", methods=["POST", "GET"])
def lawrisk_search_v2():
query, debug_flag, top_k_int, _mode = _extract_params()
if not query or not isinstance(query, str):
return jsonify({"error": "query is required"}), 400
try:
t0 = time.time()
with ThreadPoolExecutor(max_workers=2) as ex:
fut_subject = ex.submit(search_v2, query, debug_flag)
fut_questions = ex.submit(suggest_questions_embed, query, max(1, top_k_int))
result_v2 = fut_subject.result()
rec_questions = fut_questions.result() or []
risk_subject = result_v2.get("risk_subject", []) if isinstance(result_v2, dict) else []
found = bool(risk_subject)
exec_time = int((time.time() - t0) * 1000)
data = {
"llmRespond": "" if found else "抱歉,无法检索到相关答案",
"lawRisk": "",
"questionExtend": rec_questions,
"conversationId": "",
"messageId": "",
"roundNumber": 0,
"conversationInfo": {},
"knowledgeSources": [],
"totalKnowledgeSources": 0,
"executionTime": exec_time,
"workflowStatus": "ok" if found else "no_match",
"executionSteps": [],
"costStatistics": {},
"workflowTrackingId": "",
"risk_subject": risk_subject,
"debug": result_v2.get("debug", {}) if (debug_flag and isinstance(result_v2, dict)) else {},
}
resp = {"success": True, "message": "OK", "data": data}
return jsonify(resp)
except Exception as e:
app.logger.exception("lawrisk_search_v2 error")
return jsonify({"success": False, "message": str(e), "data": {}}), 500
# Basic health check
@app.get("/healthz")
def healthz():
return jsonify({"status": "ok"})
app.logger.setLevel("INFO")
app.logger.info("Registered routes:")
for rule in sorted(app.url_map.iter_rules(), key=lambda r: r.rule):
methods = ",".join(sorted(rule.methods - {"HEAD", "OPTIONS"}))
app.logger.info(" %s -> %s", rule.rule, methods)
return app
if __name__ == "__main__":
port = int(os.getenv("PORT", "8000"))
app = create_app()
app.run(host="0.0.0.0", port=port)