Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -3,15 +3,21 @@ import os, re, json, textwrap, traceback
|
|
| 3 |
from decimal import Decimal
|
| 4 |
from typing import List, Tuple
|
| 5 |
|
| 6 |
-
from flask import Flask, request, render_template
|
| 7 |
from flask_cors import CORS
|
| 8 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9 |
|
| 10 |
# ------------ LLM config ------------
|
| 11 |
-
LLM_PROVIDER
|
| 12 |
-
LLM_MODEL
|
| 13 |
-
LLM_API_KEY
|
| 14 |
-
OPENAI_BASE_URL
|
| 15 |
|
| 16 |
app = Flask(__name__)
|
| 17 |
app.secret_key = os.getenv("FLASK_SECRET_KEY", "change-me-please")
|
|
@@ -24,8 +30,8 @@ Return two things:
|
|
| 24 |
"candidates": [
|
| 25 |
{
|
| 26 |
"name": "string",
|
| 27 |
-
"score": 0, // 0..400
|
| 28 |
-
"score_pct": 0, // 0..100
|
| 29 |
"reasons": ["..."],
|
| 30 |
"tradeoffs": ["..."],
|
| 31 |
"citations": ["[1]", "[2]"]
|
|
@@ -36,7 +42,7 @@ Return two things:
|
|
| 36 |
Rules:
|
| 37 |
- Use only the provided context; cite with [1], [2]. No fabrication.
|
| 38 |
- Utilities per criterion are in [0,1]. Cost utility increases as cost decreases.
|
| 39 |
-
- Weights are independent 0..100 (
|
| 40 |
"""
|
| 41 |
|
| 42 |
ANSWER_TEMPLATE = """User constraints:
|
|
@@ -148,6 +154,10 @@ def healthz():
|
|
| 148 |
"has_api_key": bool(LLM_API_KEY),
|
| 149 |
}, 200
|
| 150 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 151 |
@app.get("/")
|
| 152 |
def index():
|
| 153 |
return render_template("index.html", default_k=DEFAULT_TOPK)
|
|
@@ -175,15 +185,14 @@ def recommend():
|
|
| 175 |
f"Consider budget={budget} and process={process}. "
|
| 176 |
f"Rank by performance, stability, cost, and availability.")
|
| 177 |
|
| 178 |
-
# RAG search
|
| 179 |
try:
|
| 180 |
hits = search(question, k=k)
|
|
|
|
| 181 |
except Exception as e:
|
| 182 |
app.logger.exception("RAG search failed")
|
| 183 |
hits = []
|
| 184 |
rag_error = f"RAG error: {type(e).__name__}: {e}"
|
| 185 |
-
else:
|
| 186 |
-
rag_error = ""
|
| 187 |
|
| 188 |
ctx, cites = format_context(hits)
|
| 189 |
|
|
@@ -195,7 +204,7 @@ def recommend():
|
|
| 195 |
context=ctx, citations=cites
|
| 196 |
)
|
| 197 |
|
| 198 |
-
# LLM call
|
| 199 |
raw = ""
|
| 200 |
try:
|
| 201 |
raw = call_llm_cloud(SYSTEM_RULES, user_prompt)
|
|
@@ -206,7 +215,6 @@ def recommend():
|
|
| 206 |
parsed = extract_json_block(raw) if raw else None
|
| 207 |
candidates = (parsed or {}).get("candidates", []) if parsed else []
|
| 208 |
|
| 209 |
-
# Append any RAG error to the raw output so you see it in UI
|
| 210 |
if rag_error:
|
| 211 |
raw = f"{rag_error}\n\n{raw}"
|
| 212 |
|
|
@@ -219,7 +227,6 @@ def recommend():
|
|
| 219 |
raw_output=raw or "",
|
| 220 |
)
|
| 221 |
except Exception as e:
|
| 222 |
-
# Last-resort guard: never return 500 to the user without context
|
| 223 |
app.logger.exception("recommend() hard failure")
|
| 224 |
tb = traceback.format_exc()
|
| 225 |
return render_template(
|
|
|
|
| 3 |
from decimal import Decimal
|
| 4 |
from typing import List, Tuple
|
| 5 |
|
| 6 |
+
from flask import Flask, request, render_template, url_for
|
| 7 |
from flask_cors import CORS
|
| 8 |
+
|
| 9 |
+
from rag_mini import (
|
| 10 |
+
search,
|
| 11 |
+
ensure_ready,
|
| 12 |
+
DEFAULT_TOPK,
|
| 13 |
+
rag_debug_info, # for /debug/rag
|
| 14 |
+
)
|
| 15 |
|
| 16 |
# ------------ LLM config ------------
|
| 17 |
+
LLM_PROVIDER = (os.getenv("LLM_PROVIDER") or "openai").strip().lower()
|
| 18 |
+
LLM_MODEL = (os.getenv("LLM_MODEL") or "gpt-4o-mini").strip()
|
| 19 |
+
LLM_API_KEY = os.getenv("OPENAI_API_KEY") or os.getenv("LLM_API_KEY")
|
| 20 |
+
OPENAI_BASE_URL = os.getenv("OPENAI_BASE_URL") # optional (Azure/proxy)
|
| 21 |
|
| 22 |
app = Flask(__name__)
|
| 23 |
app.secret_key = os.getenv("FLASK_SECRET_KEY", "change-me-please")
|
|
|
|
| 30 |
"candidates": [
|
| 31 |
{
|
| 32 |
"name": "string",
|
| 33 |
+
"score": 0, // 0..400 (sum of 4 independent 0..100 utilities)
|
| 34 |
+
"score_pct": 0, // 0..100 normalized display
|
| 35 |
"reasons": ["..."],
|
| 36 |
"tradeoffs": ["..."],
|
| 37 |
"citations": ["[1]", "[2]"]
|
|
|
|
| 42 |
Rules:
|
| 43 |
- Use only the provided context; cite with [1], [2]. No fabrication.
|
| 44 |
- Utilities per criterion are in [0,1]. Cost utility increases as cost decreases.
|
| 45 |
+
- Weights (performance, stability, cost, availability) are independent 0..100 (NOT normalized).
|
| 46 |
"""
|
| 47 |
|
| 48 |
ANSWER_TEMPLATE = """User constraints:
|
|
|
|
| 154 |
"has_api_key": bool(LLM_API_KEY),
|
| 155 |
}, 200
|
| 156 |
|
| 157 |
+
@app.get("/debug/rag")
|
| 158 |
+
def debug_rag():
|
| 159 |
+
return rag_debug_info(), 200
|
| 160 |
+
|
| 161 |
@app.get("/")
|
| 162 |
def index():
|
| 163 |
return render_template("index.html", default_k=DEFAULT_TOPK)
|
|
|
|
| 185 |
f"Consider budget={budget} and process={process}. "
|
| 186 |
f"Rank by performance, stability, cost, and availability.")
|
| 187 |
|
| 188 |
+
# RAG search (never crash UI)
|
| 189 |
try:
|
| 190 |
hits = search(question, k=k)
|
| 191 |
+
rag_error = ""
|
| 192 |
except Exception as e:
|
| 193 |
app.logger.exception("RAG search failed")
|
| 194 |
hits = []
|
| 195 |
rag_error = f"RAG error: {type(e).__name__}: {e}"
|
|
|
|
|
|
|
| 196 |
|
| 197 |
ctx, cites = format_context(hits)
|
| 198 |
|
|
|
|
| 204 |
context=ctx, citations=cites
|
| 205 |
)
|
| 206 |
|
| 207 |
+
# LLM call (never crash UI)
|
| 208 |
raw = ""
|
| 209 |
try:
|
| 210 |
raw = call_llm_cloud(SYSTEM_RULES, user_prompt)
|
|
|
|
| 215 |
parsed = extract_json_block(raw) if raw else None
|
| 216 |
candidates = (parsed or {}).get("candidates", []) if parsed else []
|
| 217 |
|
|
|
|
| 218 |
if rag_error:
|
| 219 |
raw = f"{rag_error}\n\n{raw}"
|
| 220 |
|
|
|
|
| 227 |
raw_output=raw or "",
|
| 228 |
)
|
| 229 |
except Exception as e:
|
|
|
|
| 230 |
app.logger.exception("recommend() hard failure")
|
| 231 |
tb = traceback.format_exc()
|
| 232 |
return render_template(
|