ilaa-chenjeri-15 commited on
Commit
dcfce08
·
1 Parent(s): 2db7cb0
Files changed (1) hide show
  1. app.py +103 -52
app.py CHANGED
@@ -1,13 +1,3 @@
1
- import os
2
- import sys
3
-
4
- DEBUG = False # ← set False to hide ALL noise
5
-
6
- os.environ["TRANSFORMERS_VERBOSITY"] = "error"
7
- os.environ["HF_HUB_DISABLE_PROGRESS_BARS"] = "1"
8
- os.environ["TOKENIZERS_PARALLELISM"] = "false"
9
- os.environ["HF_HUB_DISABLE_TELEMETRY"] = "1"
10
-
11
  import time
12
  import numpy as np
13
  from flask import Flask, render_template, request, jsonify
@@ -20,67 +10,64 @@ from src.retrieval.query import (
20
  )
21
  from src.generation.generate import generate_answer, build_prompt, build_context
22
 
23
- class SuppressOutput:
24
- def __enter__(self):
25
- if not DEBUG:
26
- self._stdout = sys.stdout
27
- self._stderr = sys.stderr
28
- sys.stdout = open(os.devnull, "w")
29
- sys.stderr = open(os.devnull, "w")
30
-
31
- def __exit__(self, *args):
32
- if not DEBUG:
33
- sys.stdout.close()
34
- sys.stderr.close()
35
- sys.stdout = self._stdout
36
- sys.stderr = self._stderr
37
 
 
 
 
 
 
38
  app = Flask(__name__)
39
 
40
- with SuppressOutput():
41
- _tokenizer = AutoTokenizer.from_pretrained("BAAI/bge-small-en-v1.5")
42
 
43
 
44
- # -----------------------------
45
- # BM25 IDF
46
- # -----------------------------
47
  def _build_idf(bm25):
48
  return {term: max(0.0, float(val)) for term, val in bm25.idf.items()}
49
 
 
50
  _idf_map = _build_idf(bm25_index)
51
  _max_idf = max(_idf_map.values()) if _idf_map else 1.0
52
 
53
 
54
- # -----------------------------
55
- # PCA FIT
56
- # -----------------------------
57
  def _fit_pca(n_components=128):
58
  import random
59
  from sentence_transformers import SentenceTransformer
60
 
61
  sample = random.sample(docs_all, min(200, len(docs_all)))
62
-
63
- with SuppressOutput():
64
- model = SentenceTransformer("BAAI/bge-small-en-v1.5")
65
- embs = model.encode(sample, normalize_embeddings=True)
66
 
67
  n_comp = min(n_components, embs.shape[0], embs.shape[1])
68
  pca = PCA(n_components=n_comp)
69
  pca.fit(embs)
 
70
  return pca
71
 
72
 
73
  _pca = _fit_pca(128)
74
 
75
 
76
- # -----------------------------
77
- # ROUTES
78
- # -----------------------------
79
  @app.route("/")
80
  def home():
81
  return render_template("index.html")
82
 
83
 
 
84
  @app.route("/analyze_query", methods=["POST"])
85
  def analyze_query():
86
  data = request.json
@@ -100,22 +87,28 @@ def analyze_query():
100
  for tok in tokens:
101
  word = tok["token"].lstrip("##").lower()
102
  raw_idf = _idf_map.get(word, 0.0)
 
103
  tok["idf"] = round(raw_idf, 4)
104
  tok["idf_normalized"] = round(raw_idf / _max_idf, 4) if _max_idf else 0.0
105
 
106
  idf_vals = [t["idf"] for t in tokens]
107
  avg_idf = round(sum(idf_vals) / len(idf_vals), 4) if idf_vals else 0.0
108
  unique_toks = len({t["token"] for t in tokens})
109
- complexity = round(min(1.0, (len(tokens) / 20) * 0.4 + (avg_idf / _max_idf) * 0.6), 3)
 
 
 
 
110
 
111
  q_emb = embed_query(query)
112
  projected = _pca.transform(q_emb.reshape(1, -1))[0]
 
113
  p_min, p_max = projected.min(), projected.max()
114
 
115
- normed = (
116
- ((projected - p_min) / (p_max - p_min) * 2 - 1).tolist()
117
- if p_max != p_min else [0.0] * len(projected)
118
- )
119
 
120
  return jsonify({
121
  "tokens": tokens,
@@ -129,6 +122,7 @@ def analyze_query():
129
  })
130
 
131
 
 
132
  @app.route("/mmr_rerun", methods=["POST"])
133
  def mmr_rerun():
134
  if _session["query_emb"] is None:
@@ -155,18 +149,20 @@ def mmr_rerun():
155
  })
156
 
157
 
 
158
  @app.route("/ask", methods=["POST"])
159
  def ask():
160
  data = request.json
161
  query = data.get("query")
162
 
163
- if DEBUG:
164
- print(f"\n[API QUERY]: {query}\n")
165
 
 
166
  t0 = time.perf_counter()
167
  results, debug = retrieve(query)
168
  t_retrieve = time.perf_counter() - t0
169
 
 
170
  results = sorted(results, key=lambda x: (
171
  x["meta"].get("chunk_id", 0),
172
  x["meta"].get("global_chunk_id", 0)
@@ -176,22 +172,67 @@ def ask():
176
  metas = [r["meta"] for r in results]
177
  raw_scores = [float(r["rerank_score"]) for r in results]
178
 
 
179
  context = build_context(docs, metas, raw_scores)
180
 
 
181
  t1 = time.perf_counter()
182
  prompt = build_prompt(query, context)
183
  answer = generate_answer(prompt)
184
  t_llm = time.perf_counter() - t1
185
 
 
186
  sources = [
187
- {"title": meta.get("title", "Source"), "url": meta.get("url", "")}
 
 
 
188
  for meta in metas
189
  ]
190
 
 
191
  stage_timings = debug.get("timings", {})
192
  stage_timings["llm"] = round(t_llm * 1000)
193
  stage_timings["total"] = round((t_retrieve + t_llm) * 1000)
194
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
195
  return jsonify({
196
  "answer": answer,
197
  "sources": sources,
@@ -199,12 +240,22 @@ def ask():
199
  "scores": raw_scores,
200
  "raw_scores": raw_scores,
201
  "debug": debug,
202
- "timings": stage_timings
 
 
 
 
 
 
 
 
 
 
 
 
203
  })
204
 
205
 
206
- # -----------------------------
207
- # RUN
208
- # -----------------------------
209
  if __name__ == "__main__":
210
- app.run(debug=DEBUG)
 
 
 
 
 
 
 
 
 
 
 
1
  import time
2
  import numpy as np
3
  from flask import Flask, render_template, request, jsonify
 
10
  )
11
  from src.generation.generate import generate_answer, build_prompt, build_context
12
 
13
+ import os
14
+ import logging
15
+ import warnings
16
+
17
+ # ---------------- ENV + LOGGING ----------------
18
+ os.environ["TRANSFORMERS_VERBOSITY"] = "error"
19
+ os.environ["HF_HUB_DISABLE_PROGRESS_BARS"] = "1"
20
+ os.environ["TOKENIZERS_PARALLELISM"] = "false"
21
+
22
+ warnings.filterwarnings("ignore")
 
 
 
 
23
 
24
+ logging.getLogger("transformers").setLevel(logging.ERROR)
25
+ logging.getLogger("sentence_transformers").setLevel(logging.ERROR)
26
+ logging.getLogger("urllib3").setLevel(logging.ERROR)
27
+
28
+ # ---------------- APP ----------------
29
  app = Flask(__name__)
30
 
31
+ # ---------------- TOKENIZER ----------------
32
+ _tokenizer = AutoTokenizer.from_pretrained("BAAI/bge-small-en-v1.5")
33
 
34
 
35
+ # ---------------- IDF ----------------
 
 
36
  def _build_idf(bm25):
37
  return {term: max(0.0, float(val)) for term, val in bm25.idf.items()}
38
 
39
+
40
  _idf_map = _build_idf(bm25_index)
41
  _max_idf = max(_idf_map.values()) if _idf_map else 1.0
42
 
43
 
44
+ # ---------------- PCA ----------------
 
 
45
  def _fit_pca(n_components=128):
46
  import random
47
  from sentence_transformers import SentenceTransformer
48
 
49
  sample = random.sample(docs_all, min(200, len(docs_all)))
50
+ model = SentenceTransformer("BAAI/bge-small-en-v1.5")
51
+ embs = model.encode(sample, normalize_embeddings=True)
 
 
52
 
53
  n_comp = min(n_components, embs.shape[0], embs.shape[1])
54
  pca = PCA(n_components=n_comp)
55
  pca.fit(embs)
56
+
57
  return pca
58
 
59
 
60
  _pca = _fit_pca(128)
61
 
62
 
63
+ # ---------------- ROUTES ----------------
64
+
 
65
  @app.route("/")
66
  def home():
67
  return render_template("index.html")
68
 
69
 
70
+ # ---------------- QUERY ANALYSIS ----------------
71
  @app.route("/analyze_query", methods=["POST"])
72
  def analyze_query():
73
  data = request.json
 
87
  for tok in tokens:
88
  word = tok["token"].lstrip("##").lower()
89
  raw_idf = _idf_map.get(word, 0.0)
90
+
91
  tok["idf"] = round(raw_idf, 4)
92
  tok["idf_normalized"] = round(raw_idf / _max_idf, 4) if _max_idf else 0.0
93
 
94
  idf_vals = [t["idf"] for t in tokens]
95
  avg_idf = round(sum(idf_vals) / len(idf_vals), 4) if idf_vals else 0.0
96
  unique_toks = len({t["token"] for t in tokens})
97
+
98
+ complexity = round(
99
+ min(1.0, (len(tokens) / 20) * 0.4 + (avg_idf / _max_idf) * 0.6),
100
+ 3
101
+ )
102
 
103
  q_emb = embed_query(query)
104
  projected = _pca.transform(q_emb.reshape(1, -1))[0]
105
+
106
  p_min, p_max = projected.min(), projected.max()
107
 
108
+ if p_max != p_min:
109
+ normed = ((projected - p_min) / (p_max - p_min) * 2 - 1).tolist()
110
+ else:
111
+ normed = [0.0] * len(projected)
112
 
113
  return jsonify({
114
  "tokens": tokens,
 
122
  })
123
 
124
 
125
+ # ---------------- MMR RERUN ----------------
126
  @app.route("/mmr_rerun", methods=["POST"])
127
  def mmr_rerun():
128
  if _session["query_emb"] is None:
 
149
  })
150
 
151
 
152
+ # ---------------- MAIN RAG ----------------
153
  @app.route("/ask", methods=["POST"])
154
  def ask():
155
  data = request.json
156
  query = data.get("query")
157
 
158
+ print(f"\n[API QUERY]: {query}\n")
 
159
 
160
+ # -------- RETRIEVE --------
161
  t0 = time.perf_counter()
162
  results, debug = retrieve(query)
163
  t_retrieve = time.perf_counter() - t0
164
 
165
+ # -------- SORT --------
166
  results = sorted(results, key=lambda x: (
167
  x["meta"].get("chunk_id", 0),
168
  x["meta"].get("global_chunk_id", 0)
 
172
  metas = [r["meta"] for r in results]
173
  raw_scores = [float(r["rerank_score"]) for r in results]
174
 
175
+ # -------- CONTEXT --------
176
  context = build_context(docs, metas, raw_scores)
177
 
178
+ # -------- LLM --------
179
  t1 = time.perf_counter()
180
  prompt = build_prompt(query, context)
181
  answer = generate_answer(prompt)
182
  t_llm = time.perf_counter() - t1
183
 
184
+ # -------- SOURCES --------
185
  sources = [
186
+ {
187
+ "title": meta.get("title", "Source"),
188
+ "url": meta.get("url", "")
189
+ }
190
  for meta in metas
191
  ]
192
 
193
+ # -------- TIMINGS --------
194
  stage_timings = debug.get("timings", {})
195
  stage_timings["llm"] = round(t_llm * 1000)
196
  stage_timings["total"] = round((t_retrieve + t_llm) * 1000)
197
 
198
+ # -------- COMPARISON --------
199
+ score_lookup = debug.get("score_lookup", {})
200
+ full_rerank = debug.get("rerank_full", [])
201
+
202
+ hybrid_order = {
203
+ int(k): rank for rank, k in enumerate(score_lookup.keys())
204
+ }
205
+
206
+ comparison_rows = []
207
+
208
+ for post_rank, (idx, rerank_score) in enumerate(full_rerank):
209
+ idx = int(idx)
210
+ sk = score_lookup.get(str(idx), [0, 0, 0])
211
+
212
+ pre_rank = hybrid_order.get(idx, post_rank)
213
+
214
+ comparison_rows.append({
215
+ "idx": idx,
216
+ "pre_rank": pre_rank,
217
+ "post_rank": post_rank,
218
+ "rank_delta": pre_rank - post_rank,
219
+ "vector_score": round(float(sk[0]), 4),
220
+ "bm25_score": round(float(sk[1]), 4),
221
+ "hybrid_score": round(float(sk[2]), 4),
222
+ "rerank_score": round(float(rerank_score), 4),
223
+ "passed_threshold": float(rerank_score) >= 0.3,
224
+ "text_preview": " ".join(
225
+ docs_all[idx]
226
+ .replace("passage: ", "")
227
+ .strip()
228
+ .lstrip("`")
229
+ .split()
230
+ )[:120],
231
+ "text_full": docs_all[idx].replace("passage: ", ""),
232
+ "title": metas_all[idx].get("title", ""),
233
+ })
234
+
235
+ # -------- RESPONSE --------
236
  return jsonify({
237
  "answer": answer,
238
  "sources": sources,
 
240
  "scores": raw_scores,
241
  "raw_scores": raw_scores,
242
  "debug": debug,
243
+ "timings": stage_timings,
244
+ "comparison_rows": comparison_rows,
245
+
246
+ # 🔥 CRITICAL (DO NOT CHANGE)
247
+ "mmr_data": {
248
+ "umap_coords": debug.get("umap_coords"),
249
+ "sim_matrix": debug.get("sim_matrix"),
250
+ "doc_indices": debug.get("doc_indices", []),
251
+ "sims": debug.get("sims", []),
252
+ "doc_previews": debug.get("doc_previews", []),
253
+ "mmr_selected": debug.get("mmr_selected", []),
254
+ "no_mmr_selected": debug.get("no_mmr_selected", []),
255
+ }
256
  })
257
 
258
 
259
+ # ---------------- RUN ----------------
 
 
260
  if __name__ == "__main__":
261
+ app.run(debug=True)