Nguyen5 commited on
Commit
160e79a
·
1 Parent(s): dedb74d
Files changed (1) hide show
  1. app.py +58 -49
app.py CHANGED
@@ -10,119 +10,128 @@ from rag_pipeline import rag_answer
10
  client = OpenAI()
11
  BUCKET = os.environ["SUPABASE_BUCKET"]
12
 
13
- # --------------------------------------------------------
14
- # PDF base64 viewer (tránh Chrome block)
15
- # --------------------------------------------------------
 
 
 
 
 
 
 
16
  def encode_pdf_src():
17
  pdf_bytes = load_file_bytes(BUCKET, "pruefungsordnung.pdf")
18
  b64 = base64.b64encode(pdf_bytes).decode("utf-8")
19
  return f"data:application/pdf;base64,{b64}"
20
 
21
- # --------------------------------------------------------
 
22
  # HTML viewer
23
- # --------------------------------------------------------
24
  def encode_html():
25
  html_bytes = load_file_bytes(BUCKET, "hochschulgesetz.html")
26
  return html_bytes.decode("utf-8", errors="ignore")
27
 
28
- # --------------------------------------------------------
29
- # Speech-to-text
30
- # --------------------------------------------------------
31
- def transcribe(audio_path: str) -> str:
 
32
  if audio_path is None:
33
  return ""
34
  with open(audio_path, "rb") as f:
35
  result = client.audio.transcriptions.create(
36
  model="whisper-1",
37
  file=f,
 
 
38
  )
39
  return (result.text or "").strip()
40
 
41
- # --------------------------------------------------------
42
- # Chat logic
43
- # --------------------------------------------------------
 
44
  def chat_fn(text, audio, history):
45
  text = (text or "").strip()
46
 
47
- # 1) Ưu tiên TEXT: nếu user đã gõ thì KHÔNG dùng audio nữa
48
  if text:
49
  question = text
50
- spoken = ""
51
- # 2) Nếu không có text nhưng có audio thì mới dùng Whisper
52
  elif audio is not None:
53
- spoken = transcribe(audio)
54
- question = spoken
55
  else:
56
- # không cả text lẫn audio
57
- return history, "<p>Bitte Text eingeben oder Mikrofon benutzen.</p>", None
58
 
59
  if not question:
60
- return history, "<p>Spracherkennung fehlgeschlagen. Bitte erneut sprechen oder Text tippen.</p>", None
61
 
62
- # 3) Gọi RAG pipeline (history là list[dict{role, content}])
63
  answer, docs = rag_answer(question, history or [])
64
 
65
- # 4) Xây dựng HTML nguồn
66
  html = "<ol>"
67
  for i, d in enumerate(docs):
68
  meta = d.get("metadata", {}) or {}
69
  src = meta.get("source", "?")
 
 
 
 
 
 
70
  page = meta.get("page", None)
71
- page_info = f" (Seite {page})" if page else ""
 
72
  snippet = (d.get("content") or "")[:200]
73
- html += f"<li><b>{src}{page_info}</b><br>{snippet}...</li>"
 
 
 
 
 
 
 
 
74
  html += "</ol>"
75
 
76
- # 5) Lịch sử ở dạng messages (Gradio message-format)
77
  new_history = (history or []) + [
78
  {"role": "user", "content": question},
79
  {"role": "assistant", "content": answer},
80
  ]
81
 
82
- # Trả về None cho audio để xóa bản ghi cũ
83
- return new_history, html, None
84
 
85
 
86
- # --------------------------------------------------------
87
- # UI
88
- # --------------------------------------------------------
89
  with gr.Blocks() as demo:
90
  gr.Markdown("# ⚖️ Sprachbasierter Chatbot für Prüfungsrecht")
91
 
92
  with gr.Row():
93
  with gr.Column(scale=3):
94
- # Gradio trên HF hiện đang dùng messages-format
95
- chatbot = gr.Chatbot(label="Chat")
96
-
97
- text_input = gr.Textbox(
98
- label="Text Eingabe",
99
- placeholder="Frage hier eintippen ..."
100
- )
101
-
102
- audio_input = gr.Audio(
103
- type="filepath",
104
- label="Spracheingabe (Mikrofon)"
105
- )
106
-
107
  send_btn = gr.Button("Senden")
108
 
109
  with gr.Column(scale=2):
110
  gr.Markdown("### 📄 Prüfungsordnung PDF")
111
  gr.HTML(
112
- f"<iframe src='{encode_pdf_src()}' "
113
- "width='100%' height='250'></iframe>"
114
  )
115
 
116
  gr.Markdown("### 📜 Hochschulgesetz NRW")
117
  gr.HTML(
118
- "<div style='overflow:auto;height:250px;"
119
- "border:1px solid #ccc;padding:10px;'>"
120
- f"{encode_html()}</div>"
121
  )
122
 
123
  sources_html = gr.HTML()
124
 
125
- # Lưu ý: thêm audio_input vào outputs để có thể reset về None
126
  send_btn.click(
127
  chat_fn,
128
  inputs=[text_input, audio_input, chatbot],
 
10
  client = OpenAI()
11
  BUCKET = os.environ["SUPABASE_BUCKET"]
12
 
13
+ # ------------------------------------------
14
+ # Public URLs để mở PDF/HTML khi nhấn Quelle
15
+ # ------------------------------------------
16
+ PDF_URL = f"{os.environ['SUPABASE_URL']}/storage/v1/object/public/{BUCKET}/pruefungsordnung.pdf"
17
+ HG_URL = f"{os.environ['SUPABASE_URL']}/storage/v1/object/public/{BUCKET}/hochschulgesetz.html"
18
+
19
+
20
+ # ------------------------------------------
21
+ # Viewer PDF base64
22
+ # ------------------------------------------
23
  def encode_pdf_src():
24
  pdf_bytes = load_file_bytes(BUCKET, "pruefungsordnung.pdf")
25
  b64 = base64.b64encode(pdf_bytes).decode("utf-8")
26
  return f"data:application/pdf;base64,{b64}"
27
 
28
+
29
+ # ------------------------------------------
30
  # HTML viewer
31
+ # ------------------------------------------
32
  def encode_html():
33
  html_bytes = load_file_bytes(BUCKET, "hochschulgesetz.html")
34
  return html_bytes.decode("utf-8", errors="ignore")
35
 
36
+
37
+ # ------------------------------------------
38
+ # Speech-to-text FIXED
39
+ # ------------------------------------------
40
+ def transcribe(audio_path):
41
  if audio_path is None:
42
  return ""
43
  with open(audio_path, "rb") as f:
44
  result = client.audio.transcriptions.create(
45
  model="whisper-1",
46
  file=f,
47
+ language="de", # ép tiếng Đức
48
+ temperature=0.0 # ổn định kết quả
49
  )
50
  return (result.text or "").strip()
51
 
52
+
53
+ # ------------------------------------------
54
+ # MAIN CHAT FUNCTION
55
+ # ------------------------------------------
56
  def chat_fn(text, audio, history):
57
  text = (text or "").strip()
58
 
59
+ # 1) Ưu tiên text, không dùng audio nếu text có
60
  if text:
61
  question = text
 
 
62
  elif audio is not None:
63
+ question = transcribe(audio)
 
64
  else:
65
+ return history, "<p>Bitte Text oder Mikrofon benutzen.</p>", None
 
66
 
67
  if not question:
68
+ return history, "<p>Spracherkennung fehlgeschlagen.</p>", None
69
 
70
+ # 2) RAG
71
  answer, docs = rag_answer(question, history or [])
72
 
73
+ # 3) Build Quellen (click được)
74
  html = "<ol>"
75
  for i, d in enumerate(docs):
76
  meta = d.get("metadata", {}) or {}
77
  src = meta.get("source", "?")
78
+
79
+ if "Prüfungsordnung" in src:
80
+ link = PDF_URL
81
+ else:
82
+ link = HG_URL
83
+
84
  page = meta.get("page", None)
85
+ page_info = f"(Seite {page})" if page else ""
86
+
87
  snippet = (d.get("content") or "")[:200]
88
+
89
+ html += f"""
90
+ <li>
91
+ <a href="{link}" target="_blank">
92
+ <b>Quelle {i+1}: {src} {page_info}</b>
93
+ </a><br>
94
+ {snippet}...
95
+ </li>
96
+ """
97
  html += "</ol>"
98
 
99
+ # 4) Gradio message history
100
  new_history = (history or []) + [
101
  {"role": "user", "content": question},
102
  {"role": "assistant", "content": answer},
103
  ]
104
 
105
+ # Reset audio input
106
+ return new_history, html, gr.update(value=None)
107
 
108
 
109
+ # ------------------------------------------
110
+ # UI LAYOUT
111
+ # ------------------------------------------
112
  with gr.Blocks() as demo:
113
  gr.Markdown("# ⚖️ Sprachbasierter Chatbot für Prüfungsrecht")
114
 
115
  with gr.Row():
116
  with gr.Column(scale=3):
117
+ chatbot = gr.Chatbot(label="Chat (RAG)")
118
+ text_input = gr.Textbox(label="Text Eingabe")
119
+ audio_input = gr.Audio(type="filepath", label="Spracheingabe (Mikrofon)")
 
 
 
 
 
 
 
 
 
 
120
  send_btn = gr.Button("Senden")
121
 
122
  with gr.Column(scale=2):
123
  gr.Markdown("### 📄 Prüfungsordnung PDF")
124
  gr.HTML(
125
+ f"<iframe src='{encode_pdf_src()}' width='100%' height='250'></iframe>"
 
126
  )
127
 
128
  gr.Markdown("### 📜 Hochschulgesetz NRW")
129
  gr.HTML(
130
+ f"<div style='overflow:auto;height:250px;'>{encode_html()}</div>"
 
 
131
  )
132
 
133
  sources_html = gr.HTML()
134
 
 
135
  send_btn.click(
136
  chat_fn,
137
  inputs=[text_input, audio_input, chatbot],