Spaces:
Running
Running
Update main.py
Browse files
main.py
CHANGED
|
@@ -9,7 +9,7 @@ from typing import Any, Optional
|
|
| 9 |
|
| 10 |
import numpy as np
|
| 11 |
from fastapi import FastAPI, Request
|
| 12 |
-
from fastapi.responses import HTMLResponse, JSONResponse
|
| 13 |
from fastapi.templating import Jinja2Templates
|
| 14 |
|
| 15 |
try:
|
|
@@ -284,6 +284,45 @@ def find_similar_conversation(
|
|
| 284 |
return {"conversation": conv, "score": score}
|
| 285 |
|
| 286 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 287 |
# ββββββββββββββββββββββ Actions ββββββββββββββββββββββ
|
| 288 |
|
| 289 |
def create_conversation(question: str, author: str = "Anonymous") -> dict[str, Any]:
|
|
@@ -359,10 +398,14 @@ def add_answer(
|
|
| 359 |
return None, "empty answer"
|
| 360 |
|
| 361 |
conversation = load_conversation(conversation_id)
|
| 362 |
-
|
| 363 |
-
|
| 364 |
-
|
| 365 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 366 |
|
| 367 |
now = now_iso()
|
| 368 |
version = normalize_version({
|
|
@@ -493,6 +536,14 @@ def home(request: Request):
|
|
| 493 |
)
|
| 494 |
|
| 495 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 496 |
@app.get("/health")
|
| 497 |
def health():
|
| 498 |
return {"ok": True}
|
|
@@ -538,6 +589,7 @@ async def api(request: Request):
|
|
| 538 |
if match and match.get("conversation"):
|
| 539 |
conversation = match["conversation"]
|
| 540 |
best = best_answer_payload(conversation)
|
|
|
|
| 541 |
return JSONResponse({
|
| 542 |
"ok": True,
|
| 543 |
"matched": True,
|
|
@@ -545,6 +597,7 @@ async def api(request: Request):
|
|
| 545 |
"conversation": conversation,
|
| 546 |
"assistant_text": best["text"] if best else "No answer yet. You can write one.",
|
| 547 |
"best_answer": best,
|
|
|
|
| 548 |
})
|
| 549 |
|
| 550 |
# 2) No match β create new
|
|
@@ -555,6 +608,7 @@ async def api(request: Request):
|
|
| 555 |
"conversation": conversation,
|
| 556 |
"assistant_text": "No answer yet. You can write one.",
|
| 557 |
"best_answer": None,
|
|
|
|
| 558 |
})
|
| 559 |
|
| 560 |
# ββ answer ββ
|
|
|
|
| 9 |
|
| 10 |
import numpy as np
|
| 11 |
from fastapi import FastAPI, Request
|
| 12 |
+
from fastapi.responses import FileResponse, HTMLResponse, JSONResponse
|
| 13 |
from fastapi.templating import Jinja2Templates
|
| 14 |
|
| 15 |
try:
|
|
|
|
| 284 |
return {"conversation": conv, "score": score}
|
| 285 |
|
| 286 |
|
| 287 |
+
def find_top_k_similar(question: str, k: int = 3) -> list[dict[str, Any]]:
|
| 288 |
+
idx = load_embed_index()
|
| 289 |
+
if not idx or SentenceTransformer is None:
|
| 290 |
+
return []
|
| 291 |
+
|
| 292 |
+
q_vec = np.array(embed_text(question), dtype=float)
|
| 293 |
+
if q_vec.size == 0:
|
| 294 |
+
return []
|
| 295 |
+
|
| 296 |
+
results: list[tuple[str, float]] = []
|
| 297 |
+
for cid, data in idx.items():
|
| 298 |
+
try:
|
| 299 |
+
vec = np.array(data["vector"], dtype=float)
|
| 300 |
+
except Exception:
|
| 301 |
+
continue
|
| 302 |
+
if vec.shape != q_vec.shape:
|
| 303 |
+
continue
|
| 304 |
+
score = float(vec @ q_vec)
|
| 305 |
+
results.append((cid, score))
|
| 306 |
+
|
| 307 |
+
results.sort(key=lambda x: x[1], reverse=True)
|
| 308 |
+
|
| 309 |
+
out: list[dict[str, Any]] = []
|
| 310 |
+
for cid, score in results[:k]:
|
| 311 |
+
conv = load_conversation(cid)
|
| 312 |
+
if not conv:
|
| 313 |
+
continue
|
| 314 |
+
best = best_answer_payload(conv)
|
| 315 |
+
if not best:
|
| 316 |
+
continue
|
| 317 |
+
out.append({
|
| 318 |
+
"conversation_id": cid,
|
| 319 |
+
"question": conv.get("question"),
|
| 320 |
+
"answer": best["text"],
|
| 321 |
+
"score": score,
|
| 322 |
+
})
|
| 323 |
+
return out
|
| 324 |
+
|
| 325 |
+
|
| 326 |
# ββββββββββββββββββββββ Actions ββββββββββββββββββββββ
|
| 327 |
|
| 328 |
def create_conversation(question: str, author: str = "Anonymous") -> dict[str, Any]:
|
|
|
|
| 398 |
return None, "empty answer"
|
| 399 |
|
| 400 |
conversation = load_conversation(conversation_id)
|
| 401 |
+
|
| 402 |
+
# If user is answering a NEW question (even if matched),
|
| 403 |
+
# create a new conversation instead of polluting the matched one.
|
| 404 |
+
if question_if_new:
|
| 405 |
+
if conversation is None or conversation.get("question") != question_if_new:
|
| 406 |
+
conversation = create_conversation(question_if_new, author)
|
| 407 |
+
elif conversation is None:
|
| 408 |
+
return None, "conversation not found"
|
| 409 |
|
| 410 |
now = now_iso()
|
| 411 |
version = normalize_version({
|
|
|
|
| 536 |
)
|
| 537 |
|
| 538 |
|
| 539 |
+
@app.get("/logo.png")
|
| 540 |
+
def logo():
|
| 541 |
+
logo_path = Path(__file__).with_name("logo.png")
|
| 542 |
+
if logo_path.exists():
|
| 543 |
+
return FileResponse(logo_path)
|
| 544 |
+
return JSONResponse({"ok": False, "error": "logo not found"}, status_code=404)
|
| 545 |
+
|
| 546 |
+
|
| 547 |
@app.get("/health")
|
| 548 |
def health():
|
| 549 |
return {"ok": True}
|
|
|
|
| 589 |
if match and match.get("conversation"):
|
| 590 |
conversation = match["conversation"]
|
| 591 |
best = best_answer_payload(conversation)
|
| 592 |
+
related = find_top_k_similar(question, k=3)
|
| 593 |
return JSONResponse({
|
| 594 |
"ok": True,
|
| 595 |
"matched": True,
|
|
|
|
| 597 |
"conversation": conversation,
|
| 598 |
"assistant_text": best["text"] if best else "No answer yet. You can write one.",
|
| 599 |
"best_answer": best,
|
| 600 |
+
"related": related,
|
| 601 |
})
|
| 602 |
|
| 603 |
# 2) No match β create new
|
|
|
|
| 608 |
"conversation": conversation,
|
| 609 |
"assistant_text": "No answer yet. You can write one.",
|
| 610 |
"best_answer": None,
|
| 611 |
+
"related": [],
|
| 612 |
})
|
| 613 |
|
| 614 |
# ββ answer ββ
|