File size: 3,291 Bytes
e59ae36
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
"""
Lightweight Flask API wrapping the Decision Debate Agent's LangGraph
pipeline, for a Lovable (React/TypeScript) frontend to call.

This file intentionally contains no agent logic of its own -- it is a
thin adapter. All actual reasoning happens in graph.py / baseline.py,
unchanged. This keeps the CLI (evaluate.py) and the API using the exact
same underlying pipeline, so the UI never behaves differently from what
was evaluated.

Run with: python api.py
Serves on http://localhost:5000 by default.
"""

import os
from flask import Flask, request, jsonify
from flask_cors import CORS
from dotenv import load_dotenv

load_dotenv()

from graph import run_debate
from baseline import run_baseline
from memory.vector_store import DecisionMemory

app = Flask(__name__)
# CORS is required because the Lovable dev server runs on a different
# port (typically 5173/8080) than this API (5000) -- without this,
# browser requests from the frontend would be blocked.
CORS(app)

_memory = DecisionMemory()


@app.route("/health", methods=["GET"])
def health():
    return jsonify({"status": "ok"})


@app.route("/debate", methods=["POST"])
def debate():
    """
    Request body:  {"query": "Should I take the higher paying job..."}
    Response body: {
        "optimist_view": str,
        "skeptic_view": str,
        "analyst_view": str,
        "moderator_output": str,
        "verification_retries": int,
        "retrieved_context": str
    }
    """
    data = request.get_json(silent=True) or {}
    query = (data.get("query") or "").strip()
    if not query:
        return jsonify({"error": "Missing 'query' in request body"}), 400

    try:
        result = run_debate(query, _memory)
    except RuntimeError as e:
        if "DAILY quota exhausted" in str(e):
            return jsonify({"error": "Gemini free-tier daily quota exhausted. Try again later or change GEMINI_MODEL_NAME in .env."}), 503
        return jsonify({"error": str(e)}), 500
    except Exception as e:
        return jsonify({"error": str(e)}), 500

    return jsonify({
        "optimist_view": result["optimist_view"],
        "skeptic_view": result["skeptic_view"],
        "analyst_view": result["analyst_view"],
        "moderator_output": result["moderator_output"],
        "verification_retries": result.get("retry_count", 1) - 1,
        "retrieved_context": result.get("retrieved_context", ""),
        "safety_category": result.get("safety_category", "normal"),
    })


@app.route("/baseline", methods=["POST"])
def baseline():
    """
    Request body:  {"query": "..."}
    Response body: {"output": str}

    Exposed separately so the frontend can optionally show the baseline
    side-by-side with the agent's result, making the improvement visible
    directly in the UI rather than only in eval_data/results.json.
    """
    data = request.get_json(silent=True) or {}
    query = (data.get("query") or "").strip()
    if not query:
        return jsonify({"error": "Missing 'query' in request body"}), 400

    try:
        output = run_baseline(query)
    except Exception as e:
        return jsonify({"error": str(e)}), 500

    return jsonify({"output": output})


if __name__ == "__main__":
    port = int(os.environ.get("API_PORT", 5000))
    app.run(host="0.0.0.0", port=port, debug=True)