Spaces:
Running
Running
XQ commited on
Commit ·
43c040b
1
Parent(s): 00b6bf5
Update UI
Browse files- src/ui/app.py +68 -10
src/ui/app.py
CHANGED
|
@@ -6,6 +6,8 @@ Single-page document search interface with clean sans-serif design.
|
|
| 6 |
|
| 7 |
import os
|
| 8 |
import random
|
|
|
|
|
|
|
| 9 |
|
| 10 |
import streamlit as st
|
| 11 |
import requests
|
|
@@ -30,6 +32,28 @@ EXAMPLE_QUESTIONS: list[str] = [
|
|
| 30 |
"Hvad er reglerne for forlænget tid til eksamen?",
|
| 31 |
]
|
| 32 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
# ---------------------------------------------------------------------------
|
| 34 |
# Internationalisation — all UI strings live here
|
| 35 |
# ---------------------------------------------------------------------------
|
|
@@ -70,6 +94,9 @@ TEXTS: dict[str, dict[str, str]] = {
|
|
| 70 |
"search_button": "Søg",
|
| 71 |
"example_button": "Tilfaeldigt eksempel",
|
| 72 |
"spinner": "Søger i dokumenterne ...",
|
|
|
|
|
|
|
|
|
|
| 73 |
"confidence_label": "Konfidensgrad",
|
| 74 |
"intent_label": "Intent",
|
| 75 |
"strategy_label": "Strategi",
|
|
@@ -142,6 +169,9 @@ TEXTS: dict[str, dict[str, str]] = {
|
|
| 142 |
"search_button": "Search",
|
| 143 |
"example_button": "Random question",
|
| 144 |
"spinner": "Searching documents ...",
|
|
|
|
|
|
|
|
|
|
| 145 |
"confidence_label": "Confidence",
|
| 146 |
"intent_label": "Intent",
|
| 147 |
"strategy_label": "Strategy",
|
|
@@ -439,7 +469,10 @@ with st.form(key="search_form", clear_on_submit=False):
|
|
| 439 |
# Query logic
|
| 440 |
# ---------------------------------------------------------------------------
|
| 441 |
if search_clicked and question.strip():
|
| 442 |
-
|
|
|
|
|
|
|
|
|
|
| 443 |
try:
|
| 444 |
resp = requests.post(
|
| 445 |
f"{API_BASE}/query",
|
|
@@ -451,19 +484,44 @@ if search_clicked and question.strip():
|
|
| 451 |
timeout=120,
|
| 452 |
)
|
| 453 |
resp.raise_for_status()
|
| 454 |
-
data = resp.json()
|
| 455 |
-
except
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 456 |
st.error(t["err_connection"])
|
| 457 |
-
|
| 458 |
-
|
| 459 |
-
if exc.response.status_code == 429:
|
| 460 |
st.warning(t["err_rate_limit"])
|
| 461 |
else:
|
| 462 |
-
st.error(f'{t["err_api"]}: {
|
| 463 |
-
|
| 464 |
-
except requests.Timeout:
|
| 465 |
st.error(t["err_timeout"])
|
| 466 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 467 |
|
| 468 |
# -- Metadata bar --
|
| 469 |
confidence = data.get("confidence", 0.0)
|
|
|
|
| 6 |
|
| 7 |
import os
|
| 8 |
import random
|
| 9 |
+
import threading
|
| 10 |
+
import time
|
| 11 |
|
| 12 |
import streamlit as st
|
| 13 |
import requests
|
|
|
|
| 32 |
"Hvad er reglerne for forlænget tid til eksamen?",
|
| 33 |
]
|
| 34 |
|
| 35 |
+
# ---------------------------------------------------------------------------
|
| 36 |
+
# Pipeline step labels shown during query processing
|
| 37 |
+
# Each tuple: (label, min_seconds_to_show_before_advancing)
|
| 38 |
+
# The last step has None duration — it stays until the request completes.
|
| 39 |
+
# ---------------------------------------------------------------------------
|
| 40 |
+
PIPELINE_STEPS: dict[str, list[tuple[str, float | None]]] = {
|
| 41 |
+
"da": [
|
| 42 |
+
("Klassificerer forespørgsel ...", 0.7),
|
| 43 |
+
("Oversætter til dansk om nødvendigt ...", 1.0),
|
| 44 |
+
("Søger med BM25 og vektorsøgning ...", 1.8),
|
| 45 |
+
("Fusionerer og reranker resultater ...", 1.2),
|
| 46 |
+
("Genererer svar med sprogmodel ...", None),
|
| 47 |
+
],
|
| 48 |
+
"en": [
|
| 49 |
+
("Classifying query intent ...", 0.7),
|
| 50 |
+
("Translating to Danish if needed ...", 1.0),
|
| 51 |
+
("Searching with BM25 and vector search ...", 1.8),
|
| 52 |
+
("Fusing and reranking results ...", 1.2),
|
| 53 |
+
("Generating answer with language model ...", None),
|
| 54 |
+
],
|
| 55 |
+
}
|
| 56 |
+
|
| 57 |
# ---------------------------------------------------------------------------
|
| 58 |
# Internationalisation — all UI strings live here
|
| 59 |
# ---------------------------------------------------------------------------
|
|
|
|
| 94 |
"search_button": "Søg",
|
| 95 |
"example_button": "Tilfaeldigt eksempel",
|
| 96 |
"spinner": "Søger i dokumenterne ...",
|
| 97 |
+
"status_label": "Behandler forespørgsel ...",
|
| 98 |
+
"status_done": "Færdig",
|
| 99 |
+
"status_error": "Noget gik galt",
|
| 100 |
"confidence_label": "Konfidensgrad",
|
| 101 |
"intent_label": "Intent",
|
| 102 |
"strategy_label": "Strategi",
|
|
|
|
| 169 |
"search_button": "Search",
|
| 170 |
"example_button": "Random question",
|
| 171 |
"spinner": "Searching documents ...",
|
| 172 |
+
"status_label": "Processing query ...",
|
| 173 |
+
"status_done": "Done",
|
| 174 |
+
"status_error": "Something went wrong",
|
| 175 |
"confidence_label": "Confidence",
|
| 176 |
"intent_label": "Intent",
|
| 177 |
"strategy_label": "Strategy",
|
|
|
|
| 469 |
# Query logic
|
| 470 |
# ---------------------------------------------------------------------------
|
| 471 |
if search_clicked and question.strip():
|
| 472 |
+
_result: dict = {}
|
| 473 |
+
|
| 474 |
+
def _call_api() -> None:
|
| 475 |
+
"""Run the blocking API request in a background thread."""
|
| 476 |
try:
|
| 477 |
resp = requests.post(
|
| 478 |
f"{API_BASE}/query",
|
|
|
|
| 484 |
timeout=120,
|
| 485 |
)
|
| 486 |
resp.raise_for_status()
|
| 487 |
+
_result["data"] = resp.json()
|
| 488 |
+
except Exception as _exc: # noqa: BLE001
|
| 489 |
+
_result["error"] = _exc
|
| 490 |
+
|
| 491 |
+
_thread = threading.Thread(target=_call_api, daemon=True)
|
| 492 |
+
_thread.start()
|
| 493 |
+
|
| 494 |
+
with st.status(t["status_label"], expanded=True) as _status:
|
| 495 |
+
for _step_label, _step_duration in PIPELINE_STEPS[lang]:
|
| 496 |
+
st.write(_step_label)
|
| 497 |
+
if _step_duration is not None:
|
| 498 |
+
_deadline = time.monotonic() + _step_duration
|
| 499 |
+
while time.monotonic() < _deadline and _thread.is_alive():
|
| 500 |
+
time.sleep(0.1)
|
| 501 |
+
else:
|
| 502 |
+
_thread.join()
|
| 503 |
+
|
| 504 |
+
if "error" in _result:
|
| 505 |
+
_status.update(label=t["status_error"], state="error", expanded=True)
|
| 506 |
+
else:
|
| 507 |
+
_status.update(label=t["status_done"], state="complete", expanded=False)
|
| 508 |
+
|
| 509 |
+
if "error" in _result:
|
| 510 |
+
_exc = _result["error"]
|
| 511 |
+
if isinstance(_exc, requests.ConnectionError):
|
| 512 |
st.error(t["err_connection"])
|
| 513 |
+
elif isinstance(_exc, requests.HTTPError):
|
| 514 |
+
if _exc.response.status_code == 429:
|
|
|
|
| 515 |
st.warning(t["err_rate_limit"])
|
| 516 |
else:
|
| 517 |
+
st.error(f'{t["err_api"]}: {_exc.response.status_code} -- {_exc.response.text}')
|
| 518 |
+
elif isinstance(_exc, requests.Timeout):
|
|
|
|
| 519 |
st.error(t["err_timeout"])
|
| 520 |
+
else:
|
| 521 |
+
st.error(str(_exc))
|
| 522 |
+
st.stop()
|
| 523 |
+
|
| 524 |
+
data = _result.get("data", {})
|
| 525 |
|
| 526 |
# -- Metadata bar --
|
| 527 |
confidence = data.get("confidence", 0.0)
|