Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -1,322 +1,121 @@
|
|
1 |
-
import os
|
2 |
import gradio as gr
|
|
|
|
|
3 |
import logging
|
4 |
-
import
|
5 |
from dotenv import load_dotenv
|
6 |
-
from
|
7 |
-
from langchain_qdrant import QdrantVectorStore
|
8 |
-
from langchain.chains import RetrievalQA
|
9 |
-
from langchain_groq import ChatGroq
|
10 |
-
from qdrant_client.models import PointStruct, VectorParams, Distance
|
11 |
-
import uuid
|
12 |
-
from qdrant_client.http import models
|
13 |
-
from datetime import datetime
|
14 |
-
from langchain_community.embeddings.fastembed import FastEmbedEmbeddings
|
15 |
-
from qdrant_client import QdrantClient
|
16 |
-
import cohere
|
17 |
-
from langchain.retrievers import ContextualCompressionRetriever
|
18 |
-
from langchain_cohere import CohereRerank
|
19 |
-
import re
|
20 |
-
from translation_service import TranslationService
|
21 |
-
|
22 |
# Load environment variables
|
23 |
load_dotenv()
|
24 |
-
|
25 |
-
# Initialize logging with INFO level and detailed format
|
26 |
logging.basicConfig(
|
27 |
-
filename='
|
28 |
level=logging.INFO,
|
29 |
format='%(asctime)s - %(levelname)s - %(message)s'
|
30 |
)
|
31 |
|
32 |
-
#
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
"""Initialize Qdrant client"""
|
37 |
-
try:
|
38 |
-
client = QdrantClient(
|
39 |
-
url=os.getenv("QDURL"),
|
40 |
-
api_key=os.getenv("API_KEY1"),
|
41 |
-
verify=True # Set to True if using SSL
|
42 |
-
)
|
43 |
-
logging.info("Qdrant client initialized successfully.")
|
44 |
-
return client
|
45 |
-
except Exception as e:
|
46 |
-
logging.error(f"Failed to initialize Qdrant client: {e}")
|
47 |
-
raise
|
48 |
-
|
49 |
-
def initialize_llm():
|
50 |
-
"""Initialize LLM with fallback"""
|
51 |
-
try:
|
52 |
-
llm = ChatGroq(
|
53 |
-
temperature=0,
|
54 |
-
model_name="llama3-8b-8192",
|
55 |
-
api_key=os.getenv("GROQ_API_KEY")
|
56 |
-
)
|
57 |
-
logging.info("ChatGroq initialized with model llama3-8b-8192.")
|
58 |
-
return llm
|
59 |
-
except Exception as e:
|
60 |
-
logging.warning(f"Failed to initialize ChatGroq with llama3: {e}. Falling back to mixtral.")
|
61 |
-
try:
|
62 |
-
llm = ChatGroq(
|
63 |
-
temperature=0,
|
64 |
-
model_name="mixtral-8x7b-32768",
|
65 |
-
api_key=os.getenv("GROQ_API_KEY")
|
66 |
-
)
|
67 |
-
logging.info("ChatGroq initialized with fallback model mixtral-8x7b-32768.")
|
68 |
-
return llm
|
69 |
-
except Exception as fallback_e:
|
70 |
-
logging.error(f"Failed to initialize fallback LLM: {fallback_e}")
|
71 |
-
raise
|
72 |
-
|
73 |
-
def initialize_services():
|
74 |
-
"""Initialize all services"""
|
75 |
try:
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
logging.info("FastEmbedEmbeddings initialized successfully.")
|
82 |
-
|
83 |
-
# Initialize Qdrant DB
|
84 |
-
db = QdrantVectorStore(
|
85 |
-
client=client,
|
86 |
-
embedding=embeddings,
|
87 |
-
collection_name="RR4"
|
88 |
-
)
|
89 |
-
logging.info("QdrantVectorStore initialized with collection 'RR4'.")
|
90 |
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
client=cohere_client,
|
95 |
-
top_n=6,
|
96 |
-
model="rerank-multilingual-v3.0"
|
97 |
-
)
|
98 |
-
base_retriever = db.as_retriever(search_kwargs={"k": 30})
|
99 |
-
retriever = ContextualCompressionRetriever(
|
100 |
-
base_compressor=reranker,
|
101 |
-
base_retriever=base_retriever
|
102 |
)
|
103 |
-
|
104 |
|
105 |
-
|
106 |
-
|
107 |
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
raise
|
112 |
|
113 |
-
def
|
114 |
-
"""
|
115 |
try:
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
collection_exists = any(c.name == "chat_feedback" for c in collections)
|
121 |
-
|
122 |
-
if not collection_exists:
|
123 |
-
# Create collection with proper configuration
|
124 |
-
client.create_collection(
|
125 |
-
collection_name="chat_feedback",
|
126 |
-
vectors_config=VectorParams(
|
127 |
-
size=768, # Ensure this matches the embedding size
|
128 |
-
distance=Distance.COSINE
|
129 |
-
)
|
130 |
-
)
|
131 |
-
logging.info("Created 'chat_feedback' collection with vector size 768 and Cosine distance.")
|
132 |
-
else:
|
133 |
-
logging.info("'chat_feedback' collection already exists.")
|
134 |
-
|
135 |
-
# Verify collection exists and has correct configuration
|
136 |
-
collection_info = client.get_collection("chat_feedback")
|
137 |
-
if collection_info.config.params.vectors.size != 768:
|
138 |
-
raise ValueError("Incorrect vector size in 'chat_feedback' collection.")
|
139 |
-
logging.info("'chat_feedback' collection verified successfully with correct vector size.")
|
140 |
-
|
141 |
-
return True
|
142 |
-
except Exception as e:
|
143 |
-
logging.error(f"Failed to initialize feedback collection: {e}")
|
144 |
-
raise
|
145 |
-
|
146 |
-
async def submit_feedback(feedback_type, chat_history, language_choice):
|
147 |
-
"""Submit feedback with improved error handling and logging."""
|
148 |
-
try:
|
149 |
-
if not chat_history or len(chat_history) < 2:
|
150 |
-
logging.warning("Attempted to submit feedback with insufficient chat history.")
|
151 |
return "No recent interaction to provide feedback for."
|
152 |
|
153 |
-
# Get last
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
|
158 |
-
|
159 |
-
|
160 |
-
|
161 |
-
|
162 |
-
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
|
170 |
-
#
|
171 |
payload = {
|
172 |
-
"
|
173 |
-
"
|
174 |
"language": language_choice,
|
175 |
-
"
|
176 |
-
"feedback": feedback_type
|
177 |
}
|
178 |
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
-
# Create embeddings for the Q&A pair
|
183 |
-
try:
|
184 |
-
embedding_text = f"{question} {answer}"
|
185 |
-
vector = await asyncio.to_thread(embeddings.embed_query, embedding_text)
|
186 |
-
logging.info(f"Generated embedding vector of length {len(vector)}.")
|
187 |
-
except Exception as embed_error:
|
188 |
-
logging.error(f"Embedding generation failed: {embed_error}")
|
189 |
-
return "Failed to generate embeddings for your feedback."
|
190 |
-
|
191 |
-
if not isinstance(vector, list) or not vector:
|
192 |
-
logging.error("Invalid vector generated from embeddings.")
|
193 |
-
return "Failed to generate valid embeddings for your feedback."
|
194 |
-
|
195 |
-
# Create point
|
196 |
-
point = PointStruct(
|
197 |
-
id=point_id,
|
198 |
-
payload=payload,
|
199 |
-
vector=vector
|
200 |
-
)
|
201 |
-
|
202 |
-
# Store in Qdrant
|
203 |
-
try:
|
204 |
-
operation_info = await asyncio.to_thread(
|
205 |
-
client.upsert,
|
206 |
-
collection_name="chat_feedback",
|
207 |
-
points=[point]
|
208 |
-
)
|
209 |
-
logging.info(f"Feedback submitted successfully: {point_id}")
|
210 |
-
return "Thanks for your feedback! Your response has been recorded."
|
211 |
-
except Exception as db_error:
|
212 |
-
logging.error(f"Failed to upsert point to Qdrant: {db_error}")
|
213 |
-
return "Sorry, there was an error submitting your feedback."
|
214 |
-
|
215 |
-
except Exception as e:
|
216 |
-
logging.error(f"Unexpected error in submit_feedback: {e}")
|
217 |
-
return "Sorry, there was an unexpected error submitting your feedback."
|
218 |
-
|
219 |
-
# Initialize services and feedback collection
|
220 |
-
try:
|
221 |
-
retriever, llm = initialize_services()
|
222 |
-
initialize_feedback_collection()
|
223 |
-
except Exception as initialization_error:
|
224 |
-
logging.critical(f"Initialization failed: {initialization_error}")
|
225 |
-
raise
|
226 |
-
|
227 |
-
# Prompt template
|
228 |
-
prompt_template = PromptTemplate(
|
229 |
-
template="""You are RRA Assistant, created by Cedric to help users get tax related information in Rwanda. Your task is to answer tax-related questions using the provided context.
|
230 |
-
|
231 |
-
Context: {context}
|
232 |
-
|
233 |
-
User's Question: {question}
|
234 |
-
|
235 |
-
Please follow these steps to answer the question:
|
236 |
-
|
237 |
-
Step 1: Analyze the question
|
238 |
-
Briefly explain your understanding of the question and any key points to address. If it is hi or hello, skip to step 3 and respond with a greeting.
|
239 |
-
|
240 |
-
Step 2: Provide relevant information
|
241 |
-
Using the context provided, give detailed information related to the question. Include specific facts, figures, or explanations from the context.
|
242 |
-
|
243 |
-
Step 3: Final answer
|
244 |
-
Provide a clear, concise answer to the original question. Start directly with the relevant information, avoiding phrases like "In summary" or "To conclude".
|
245 |
-
|
246 |
-
Remember:
|
247 |
-
- If you don't know the answer or can't find relevant information in the context, say so honestly.
|
248 |
-
- Do not make up information.
|
249 |
-
- Use the provided context to support your answer.
|
250 |
-
- Include "For more information, call 3004" at the end of every answer.
|
251 |
-
|
252 |
-
Your response:
|
253 |
-
""",
|
254 |
-
input_variables=['context', 'question']
|
255 |
-
)
|
256 |
-
|
257 |
-
async def process_query(message: str, language: str, chat_history: list) -> str:
|
258 |
-
try:
|
259 |
-
# Handle translation based on selected language
|
260 |
-
if language == "Kinyarwanda":
|
261 |
-
query = translator.translate(message, "rw", "en")
|
262 |
-
logging.info(f"Translated query to English: {query}")
|
263 |
-
else:
|
264 |
-
query = message
|
265 |
-
|
266 |
-
# Create QA chain
|
267 |
-
qa = RetrievalQA.from_chain_type(
|
268 |
-
llm=llm,
|
269 |
-
chain_type="stuff",
|
270 |
-
retriever=retriever,
|
271 |
-
chain_type_kwargs={"prompt": prompt_template},
|
272 |
-
return_source_documents=True
|
273 |
-
)
|
274 |
-
|
275 |
-
# Get response
|
276 |
-
response = await asyncio.to_thread(
|
277 |
-
lambda: qa.invoke({"query": query})
|
278 |
)
|
279 |
-
|
280 |
|
281 |
-
|
282 |
-
result_text = response.get('result', '')
|
283 |
-
final_answer_start = result_text.find("Step 3: Final answer")
|
284 |
-
if final_answer_start != -1:
|
285 |
-
answer = result_text[final_answer_start + len("Step 3: Final answer"):].strip()
|
286 |
-
else:
|
287 |
-
answer = result_text
|
288 |
-
|
289 |
-
# Clean up the answer
|
290 |
-
answer = re.sub(r'\*\*', '', answer).strip()
|
291 |
-
answer = re.sub(r'Step \d+:', '', answer).strip()
|
292 |
-
|
293 |
-
# Translate response if needed
|
294 |
-
if language == "Kinyarwanda":
|
295 |
-
answer = translator.translate(answer, "en", "rw")
|
296 |
-
logging.info(f"Translated answer to Kinyarwanda: {answer}")
|
297 |
|
298 |
-
|
|
|
|
|
299 |
except Exception as e:
|
300 |
-
logging.error(f"
|
301 |
-
return
|
302 |
-
|
303 |
-
# Define separate feedback submission functions to pass feedback type correctly
|
304 |
-
async def submit_positive_feedback(chat_history, language_choice):
|
305 |
-
return await submit_feedback("positive", chat_history, language_choice)
|
306 |
-
|
307 |
-
async def submit_negative_feedback(chat_history, language_choice):
|
308 |
-
return await submit_feedback("negative", chat_history, language_choice)
|
309 |
|
310 |
# Create Gradio interface
|
311 |
with gr.Blocks(title="RRA FAQ Chatbot") as demo:
|
|
|
|
|
312 |
gr.Markdown(
|
313 |
"""
|
314 |
# RRA FAQ Chatbot
|
315 |
Ask tax-related questions in English or Kinyarwanda
|
316 |
-
> π Your questions and interactions remain private unless you choose to submit feedback, which helps improve our service.
|
317 |
"""
|
318 |
)
|
319 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
320 |
# Add language selector
|
321 |
language = gr.Radio(
|
322 |
choices=["English", "Kinyarwanda"],
|
@@ -327,8 +126,7 @@ with gr.Blocks(title="RRA FAQ Chatbot") as demo:
|
|
327 |
chatbot = gr.Chatbot(
|
328 |
value=[],
|
329 |
show_label=False,
|
330 |
-
height=400
|
331 |
-
type='messages'
|
332 |
)
|
333 |
|
334 |
with gr.Row():
|
@@ -340,7 +138,8 @@ with gr.Blocks(title="RRA FAQ Chatbot") as demo:
|
|
340 |
submit = gr.Button("Send")
|
341 |
|
342 |
# Add feedback section
|
343 |
-
|
|
|
344 |
with gr.Column(scale=2):
|
345 |
feedback_label = gr.Markdown("Was this response helpful?")
|
346 |
with gr.Column(scale=1):
|
@@ -351,20 +150,77 @@ with gr.Blocks(title="RRA FAQ Chatbot") as demo:
|
|
351 |
# Add feedback status message
|
352 |
feedback_status = gr.Markdown("")
|
353 |
|
354 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
355 |
feedback_positive.click(
|
356 |
fn=submit_positive_feedback,
|
357 |
-
inputs=[
|
358 |
outputs=feedback_status
|
359 |
)
|
360 |
|
361 |
feedback_negative.click(
|
362 |
fn=submit_negative_feedback,
|
363 |
-
inputs=[
|
364 |
outputs=feedback_status
|
365 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
366 |
|
367 |
-
#
|
368 |
with gr.Row() as english_examples_row:
|
369 |
gr.Examples(
|
370 |
examples=[
|
@@ -391,12 +247,6 @@ with gr.Blocks(title="RRA FAQ Chatbot") as demo:
|
|
391 |
label="Kinyarwanda Examples"
|
392 |
)
|
393 |
|
394 |
-
async def respond(message, lang, chat_history):
|
395 |
-
bot_message = await process_query(message, lang, chat_history)
|
396 |
-
chat_history.append({"role": "user", "content": message})
|
397 |
-
chat_history.append({"role": "assistant", "content": bot_message})
|
398 |
-
return "", chat_history
|
399 |
-
|
400 |
def toggle_language_interface(language_choice):
|
401 |
if language_choice == "English":
|
402 |
placeholder_text = "Type your tax-related question here..."
|
@@ -413,10 +263,19 @@ with gr.Blocks(title="RRA FAQ Chatbot") as demo:
|
|
413 |
kinyarwanda_examples_row: gr.update(visible=True)
|
414 |
}
|
415 |
|
416 |
-
|
417 |
-
submit
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
418 |
|
419 |
-
# Update
|
420 |
language.change(
|
421 |
fn=toggle_language_interface,
|
422 |
inputs=language,
|
@@ -431,11 +290,9 @@ with gr.Blocks(title="RRA FAQ Chatbot") as demo:
|
|
431 |
|
432 |
**Disclaimer:** This chatbot provides general tax information. For official guidance,
|
433 |
consult RRA or call 3004.
|
434 |
-
π **Privacy:** Your interactions remain private unless you choose to submit feedback.
|
435 |
"""
|
436 |
)
|
437 |
|
438 |
-
# Launch the app
|
439 |
if __name__ == "__main__":
|
440 |
try:
|
441 |
demo.launch(share=False)
|
|
|
|
|
1 |
import gradio as gr
|
2 |
+
import requests
|
3 |
+
import json
|
4 |
import logging
|
5 |
+
import os
|
6 |
from dotenv import load_dotenv
|
7 |
+
from typing import Dict, List, Tuple
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
8 |
# Load environment variables
|
9 |
load_dotenv()
|
10 |
+
# Configure logging
|
|
|
11 |
logging.basicConfig(
|
12 |
+
filename='gradio_frontend.log',
|
13 |
level=logging.INFO,
|
14 |
format='%(asctime)s - %(levelname)s - %(message)s'
|
15 |
)
|
16 |
|
17 |
+
# API Configuration
|
18 |
+
API_BASE_URL=os.getenv("API_BASE_URL")
|
19 |
+
async def process_query(message: str, language: str, mode: str, chat_history: List[Dict]) -> Tuple[str, str]:
|
20 |
+
"""Make API request to get chatbot response"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
21 |
try:
|
22 |
+
payload = {
|
23 |
+
"message": message,
|
24 |
+
"language": language,
|
25 |
+
"mode": mode
|
26 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
27 |
|
28 |
+
response = requests.post(
|
29 |
+
f"{API_BASE_URL}/api/query",
|
30 |
+
json=payload
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
31 |
)
|
32 |
+
response.raise_for_status()
|
33 |
|
34 |
+
result = response.json()
|
35 |
+
return result["answer"], result.get("id")
|
36 |
|
37 |
+
except requests.RequestException as e:
|
38 |
+
logging.error(f"API request failed: {str(e)}")
|
39 |
+
return f"Sorry, there was an error processing your request: {str(e)}", None
|
|
|
40 |
|
41 |
+
async def submit_feedback(feedback_type: str, full_chat_history: List[Dict], language_choice: str, mode: str) -> str:
|
42 |
+
"""Submit feedback via API"""
|
43 |
try:
|
44 |
+
if mode == "Private":
|
45 |
+
return "Feedback is disabled in private mode."
|
46 |
+
|
47 |
+
if not full_chat_history or len(full_chat_history) < 2:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
48 |
return "No recent interaction to provide feedback for."
|
49 |
|
50 |
+
# Get the last assistant message with an ID
|
51 |
+
assistant_messages = [msg for msg in full_chat_history
|
52 |
+
if msg.get("role") == "assistant" and "id" in msg]
|
53 |
+
|
54 |
+
if not assistant_messages:
|
55 |
+
logging.error("No assistant messages with ID found in chat history")
|
56 |
+
return "No response found to provide feedback for."
|
57 |
+
|
58 |
+
last_message = assistant_messages[-1]
|
59 |
+
question_id = last_message.get("id")
|
60 |
+
|
61 |
+
if not question_id:
|
62 |
+
logging.error(f"No ID found in last message: {last_message}")
|
63 |
+
return "Question ID not found. Please try asking another question."
|
64 |
+
|
65 |
+
logging.info(f"Submitting feedback for question ID: {question_id}")
|
66 |
+
|
67 |
+
# Submit feedback
|
68 |
payload = {
|
69 |
+
"question_id": question_id,
|
70 |
+
"feedback_type": feedback_type,
|
71 |
"language": language_choice,
|
72 |
+
"mode": mode
|
|
|
73 |
}
|
74 |
|
75 |
+
response = requests.post(
|
76 |
+
f"{API_BASE_URL}/api/feedback",
|
77 |
+
json=payload
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
78 |
)
|
79 |
+
response.raise_for_status()
|
80 |
|
81 |
+
return "Thanks for your feedback! Your response has been recorded."
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
82 |
|
83 |
+
except requests.RequestException as e:
|
84 |
+
logging.error(f"Failed to submit feedback: {str(e)}")
|
85 |
+
return f"Sorry, there was an error submitting your feedback: {str(e)}"
|
86 |
except Exception as e:
|
87 |
+
logging.error(f"Unexpected error in submit_feedback: {str(e)}")
|
88 |
+
return "An unexpected error occurred. Please try again."
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
89 |
|
90 |
# Create Gradio interface
|
91 |
with gr.Blocks(title="RRA FAQ Chatbot") as demo:
|
92 |
+
full_chat_history = gr.State([]) # Add this line
|
93 |
+
|
94 |
gr.Markdown(
|
95 |
"""
|
96 |
# RRA FAQ Chatbot
|
97 |
Ask tax-related questions in English or Kinyarwanda
|
|
|
98 |
"""
|
99 |
)
|
100 |
|
101 |
+
# Add mode selector
|
102 |
+
with gr.Row():
|
103 |
+
interaction_mode = gr.Radio(
|
104 |
+
choices=["Normal", "Private"],
|
105 |
+
value="Normal",
|
106 |
+
label="Interaction Mode",
|
107 |
+
info="Normal: Stores interactions to improve service | Private: No data storage"
|
108 |
+
)
|
109 |
+
|
110 |
+
with gr.Row():
|
111 |
+
gr.Markdown(
|
112 |
+
"""
|
113 |
+
> π **Data Storage Notice:**
|
114 |
+
> - Normal Mode: Questions and interactions are stored to improve our service
|
115 |
+
> - Private Mode: No data is stored, feedback feature is disabled
|
116 |
+
"""
|
117 |
+
)
|
118 |
+
|
119 |
# Add language selector
|
120 |
language = gr.Radio(
|
121 |
choices=["English", "Kinyarwanda"],
|
|
|
126 |
chatbot = gr.Chatbot(
|
127 |
value=[],
|
128 |
show_label=False,
|
129 |
+
height=400
|
|
|
130 |
)
|
131 |
|
132 |
with gr.Row():
|
|
|
138 |
submit = gr.Button("Send")
|
139 |
|
140 |
# Add feedback section
|
141 |
+
feedback_container = gr.Row(visible=True)
|
142 |
+
with feedback_container:
|
143 |
with gr.Column(scale=2):
|
144 |
feedback_label = gr.Markdown("Was this response helpful?")
|
145 |
with gr.Column(scale=1):
|
|
|
150 |
# Add feedback status message
|
151 |
feedback_status = gr.Markdown("")
|
152 |
|
153 |
+
async def respond(message, lang, mode, chat_history, full_chat_history):
|
154 |
+
"""Process a user message and update chat history"""
|
155 |
+
try:
|
156 |
+
if chat_history is None:
|
157 |
+
chat_history = []
|
158 |
+
if full_chat_history is None:
|
159 |
+
full_chat_history = []
|
160 |
+
|
161 |
+
# Get response from API
|
162 |
+
bot_message, question_id = await process_query(message, lang, mode, chat_history)
|
163 |
+
if not bot_message:
|
164 |
+
return "", chat_history, full_chat_history
|
165 |
+
|
166 |
+
# Build new messages
|
167 |
+
user_message = {
|
168 |
+
"content": message,
|
169 |
+
"role": "user"
|
170 |
+
}
|
171 |
+
|
172 |
+
assistant_message = {
|
173 |
+
"content": bot_message,
|
174 |
+
"role": "assistant",
|
175 |
+
"id": question_id # Store ID in the message
|
176 |
+
}
|
177 |
+
|
178 |
+
# Append to full chat history
|
179 |
+
new_full_history = full_chat_history + [user_message, assistant_message]
|
180 |
+
|
181 |
+
# Prepare messages for chatbot display
|
182 |
+
new_chat_history = chat_history + [[message, bot_message]]
|
183 |
+
return "", new_chat_history, new_full_history
|
184 |
+
|
185 |
+
except Exception as e:
|
186 |
+
logging.error(f"Error in respond function: {e}")
|
187 |
+
return "", chat_history, full_chat_history
|
188 |
+
|
189 |
+
def update_mode(mode: str):
|
190 |
+
"""Update UI when mode changes"""
|
191 |
+
is_private = (mode == "Private")
|
192 |
+
return {
|
193 |
+
feedback_container: gr.update(visible=not is_private),
|
194 |
+
feedback_status: gr.update(value="" if not is_private else "Feedback is disabled in private mode")
|
195 |
+
}
|
196 |
+
|
197 |
+
# Connect feedback buttons
|
198 |
+
async def submit_positive_feedback(full_chat_history, language_choice, mode):
|
199 |
+
return await submit_feedback("positive", full_chat_history, language_choice, mode)
|
200 |
+
|
201 |
+
async def submit_negative_feedback(full_chat_history, language_choice, mode):
|
202 |
+
return await submit_feedback("negative", full_chat_history, language_choice, mode)
|
203 |
+
|
204 |
feedback_positive.click(
|
205 |
fn=submit_positive_feedback,
|
206 |
+
inputs=[full_chat_history, language, interaction_mode],
|
207 |
outputs=feedback_status
|
208 |
)
|
209 |
|
210 |
feedback_negative.click(
|
211 |
fn=submit_negative_feedback,
|
212 |
+
inputs=[full_chat_history, language, interaction_mode],
|
213 |
outputs=feedback_status
|
214 |
)
|
215 |
+
|
216 |
+
# Update UI when mode changes
|
217 |
+
interaction_mode.change(
|
218 |
+
fn=update_mode,
|
219 |
+
inputs=[interaction_mode],
|
220 |
+
outputs=[feedback_container, feedback_status]
|
221 |
+
)
|
222 |
|
223 |
+
# Example questions
|
224 |
with gr.Row() as english_examples_row:
|
225 |
gr.Examples(
|
226 |
examples=[
|
|
|
247 |
label="Kinyarwanda Examples"
|
248 |
)
|
249 |
|
|
|
|
|
|
|
|
|
|
|
|
|
250 |
def toggle_language_interface(language_choice):
|
251 |
if language_choice == "English":
|
252 |
placeholder_text = "Type your tax-related question here..."
|
|
|
263 |
kinyarwanda_examples_row: gr.update(visible=True)
|
264 |
}
|
265 |
|
266 |
+
# Connect user inputs
|
267 |
+
msg.submit(
|
268 |
+
respond,
|
269 |
+
[msg, language, interaction_mode, chatbot, full_chat_history],
|
270 |
+
[msg, chatbot, full_chat_history]
|
271 |
+
)
|
272 |
+
submit.click(
|
273 |
+
respond,
|
274 |
+
[msg, language, interaction_mode, chatbot, full_chat_history],
|
275 |
+
[msg, chatbot, full_chat_history]
|
276 |
+
)
|
277 |
|
278 |
+
# Update interface on language change
|
279 |
language.change(
|
280 |
fn=toggle_language_interface,
|
281 |
inputs=language,
|
|
|
290 |
|
291 |
**Disclaimer:** This chatbot provides general tax information. For official guidance,
|
292 |
consult RRA or call 3004.
|
|
|
293 |
"""
|
294 |
)
|
295 |
|
|
|
296 |
if __name__ == "__main__":
|
297 |
try:
|
298 |
demo.launch(share=False)
|