Sebunya commited on
Commit
ff2d4b2
·
verified ·
1 Parent(s): 147efab

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +98 -19
app.py CHANGED
@@ -7,6 +7,10 @@ from sentence_transformers import util
7
  import google.generativeai as genai
8
  import chromadb
9
  from langchain_chroma import Chroma
 
 
 
 
10
  import re
11
  from typing import Dict, List, Tuple
12
 
@@ -16,6 +20,55 @@ embedding_model = "models/embedding-001"
16
  llm_model_name = "models/gemma-3-4b-it"
17
  collection_name = "xeno_collection"
18
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  # === Intent Classification System ===
20
  class IntentClassifier:
21
  def __init__(self):
@@ -66,16 +119,13 @@ class IntentClassifier:
66
  """
67
  message_lower = message.lower().strip()
68
 
69
- # Check for each intent pattern
70
  for intent_name, intent_data in self.intent_patterns.items():
71
  for pattern in intent_data['patterns']:
72
  if re.search(pattern, message_lower, re.IGNORECASE):
73
- # Return random response from available responses
74
  import random
75
  response = random.choice(intent_data['responses'])
76
  return intent_name, response
77
 
78
- # If no simple intent found, it's a query that needs RAG
79
  return 'query', ''
80
 
81
  def is_simple_intent(self, intent: str) -> bool:
@@ -100,7 +150,8 @@ def prepare_documents(data):
100
  "section": item.get("Section", ""),
101
  "source": item.get("Source", ""),
102
  "owner": item.get("Owner", ""),
103
- "tag": item.get("Tag", "")
 
104
  })
105
  ids.append(item["ID"])
106
  return documents, metadatas, ids
@@ -109,12 +160,18 @@ xeno_data_list = df_kb.to_dict('records')
109
  documents, metadatas, ids = prepare_documents(xeno_data_list)
110
 
111
  # === Setup ChromaDB ===
112
- client = chromadb.PersistentClient(path="./xeno_db")
113
  try:
114
- collection = client.get_collection(name=collection_name)
115
- except:
116
- collection = client.create_collection(name=collection_name)
117
- collection.add(documents=documents, metadatas=metadatas, ids=ids)
 
 
 
 
 
 
 
118
 
119
  vector_store = Chroma(client=client, collection_name=collection_name)
120
  retriever = vector_store.as_retriever(search_type="similarity", search_kwargs={"k": 4})
@@ -131,14 +188,20 @@ remember previous conversations."""
131
  def process_context(results, cosine_scores, max_results=2):
132
  sorted_indices = np.argsort(cosine_scores)[::-1][:max_results]
133
  formatted_context = ""
 
 
134
  for i, idx in enumerate(sorted_indices, 1):
135
  result = results[idx]
136
  score = cosine_scores[idx]
 
 
137
  formatted_context += f"Knowledge Entry {i}:\n"
138
- formatted_context += f"Q: {result.metadata.get('question', 'N/A')}\n"
139
- formatted_context += f"A: {result.metadata.get('content', 'N/A')}\n"
140
  formatted_context += "-" * 40 + "\n"
141
- return formatted_context
 
 
142
 
143
  # === LLM Generation ===
144
  def generate_xeno_response(context, question):
@@ -161,13 +224,16 @@ def get_context_and_answer(message, history):
161
 
162
  # Step 2: Handle simple intents directly
163
  if intent_classifier.is_simple_intent(intent) and direct_response:
 
164
  return direct_response
165
 
166
  # Step 3: For queries that need RAG processing
167
  if intent == 'query':
168
  # Check if message is too short or unclear
169
  if len(message.strip()) < 3:
170
- return "I'd be happy to help! Could you please provide more details about what you'd like to know about XENO services?"
 
 
171
 
172
  # Retrieve relevant documents
173
  try:
@@ -193,16 +259,29 @@ def get_context_and_answer(message, history):
193
 
194
  # If none of the results have sufficient similarity, fallback
195
  if max(cosine_scores) < 0.4:
196
- return "I'm sorry, I couldn't find the specific information you're looking for in my knowledge base. Could you try rephrasing your question or contact XENO support directly for assistance?"
 
 
197
 
198
- context = process_context(queried_results, cosine_scores)
199
- return generate_xeno_response(context, message)
 
 
200
 
201
  except Exception as e:
202
- return "I apologize, but I'm experiencing a technical issue. Please contact XENO support directly for assistance with your query."
 
 
203
 
 
 
 
 
 
204
  # Fallback for any unhandled cases
205
- return "I'm here to help with XENO financial services. What would you like to know?"
 
 
206
 
207
  # === Enhanced Gradio UI ===
208
  def create_interface():
@@ -226,4 +305,4 @@ I can help you with questions about XENO financial services including:
226
  # === Main Execution ===
227
  if __name__ == "__main__":
228
  iface = create_interface()
229
- iface.launch()
 
7
  import google.generativeai as genai
8
  import chromadb
9
  from langchain_chroma import Chroma
10
+ import gspread
11
+ from google.oauth2.service_account import Credentials
12
+ import json
13
+ from datetime import datetime
14
  import re
15
  from typing import Dict, List, Tuple
16
 
 
20
  llm_model_name = "models/gemma-3-4b-it"
21
  collection_name = "xeno_collection"
22
 
23
+ # === Google Sheets Setup for Hugging Face ===
24
+ def get_google_sheets_credentials():
25
+ credentials_json = os.environ.get("GOOGLE_SHEETS_CREDENTIALS")
26
+ if not credentials_json:
27
+ raise ValueError("GOOGLE_SHEETS_CREDENTIALS environment variable not set.")
28
+ credentials_dict = json.loads(credentials_json)
29
+ scope = ["https://spreadsheets.google.com/feeds", "https://www.googleapis.com/auth/drive"]
30
+ creds = Credentials.from_service_account_info(credentials_dict, scopes=scope)
31
+ return creds
32
+
33
+ # Authenticate with Google Sheets
34
+ client_gspread = gspread.authorize(get_google_sheets_credentials())
35
+
36
+ # Open the Google Sheet
37
+ sheet = client_gspread.open("Response_Log").sheet1
38
+
39
+ def log_response(question, answer, source_ids, knowledge_pairs):
40
+ """
41
+ Log a question, answer, source IDs, and knowledge base question-answer pairs to the Google Sheet.
42
+
43
+ Args:
44
+ question (str): The question asked by the user.
45
+ answer (str): The answer provided by the model.
46
+ source_ids (str): Comma-separated list of source IDs used.
47
+ knowledge_pairs (list): List of tuples containing (question, answer) from the knowledge base.
48
+ """
49
+ timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
50
+ knowledge_question_1 = knowledge_pairs[0][0] if len(knowledge_pairs) > 0 else "N/A"
51
+ knowledge_answer_1 = knowledge_pairs[0][1] if len(knowledge_pairs) > 0 else "N/A"
52
+ knowledge_question_2 = knowledge_pairs[1][0] if len(knowledge_pairs) > 1 else "N/A"
53
+ knowledge_answer_2 = knowledge_pairs[1][1] if len(knowledge_pairs) > 1 else "N/A"
54
+ row = [
55
+ timestamp,
56
+ question,
57
+ answer,
58
+ source_ids,
59
+ knowledge_question_1,
60
+ knowledge_answer_1,
61
+ knowledge_question_2,
62
+ knowledge_answer_2
63
+ ]
64
+ try:
65
+ sheet.append_row(row)
66
+ print(f"Logged: {question} | Source IDs: {source_ids}")
67
+ except Exception as e:
68
+ print(f"Failed to log to Google Sheet: {e}")
69
+ with open("/tmp/response_log.txt", "a") as f:
70
+ f.write(f"{timestamp},{question},{answer},{source_ids},{knowledge_question_1},{knowledge_answer_1},{knowledge_question_2},{knowledge_answer_2}\n")
71
+
72
  # === Intent Classification System ===
73
  class IntentClassifier:
74
  def __init__(self):
 
119
  """
120
  message_lower = message.lower().strip()
121
 
 
122
  for intent_name, intent_data in self.intent_patterns.items():
123
  for pattern in intent_data['patterns']:
124
  if re.search(pattern, message_lower, re.IGNORECASE):
 
125
  import random
126
  response = random.choice(intent_data['responses'])
127
  return intent_name, response
128
 
 
129
  return 'query', ''
130
 
131
  def is_simple_intent(self, intent: str) -> bool:
 
150
  "section": item.get("Section", ""),
151
  "source": item.get("Source", ""),
152
  "owner": item.get("Owner", ""),
153
+ "tag": item.get("Tag", ""),
154
+ "id": item["ID"]
155
  })
156
  ids.append(item["ID"])
157
  return documents, metadatas, ids
 
160
  documents, metadatas, ids = prepare_documents(xeno_data_list)
161
 
162
  # === Setup ChromaDB ===
 
163
  try:
164
+ client = chromadb.PersistentClient(path="/tmp/xeno_db")
165
+ try:
166
+ collection = client.get_collection(name=collection_name)
167
+ print(f"Loaded existing ChromaDB collection: {collection_name}")
168
+ except:
169
+ print(f"Creating new ChromaDB collection: {collection_name}")
170
+ collection = client.create_collection(name=collection_name)
171
+ collection.add(documents=documents, metadatas=metadatas, ids=ids)
172
+ except Exception as e:
173
+ print(f"Failed to initialize ChromaDB: {e}")
174
+ raise
175
 
176
  vector_store = Chroma(client=client, collection_name=collection_name)
177
  retriever = vector_store.as_retriever(search_type="similarity", search_kwargs={"k": 4})
 
188
  def process_context(results, cosine_scores, max_results=2):
189
  sorted_indices = np.argsort(cosine_scores)[::-1][:max_results]
190
  formatted_context = ""
191
+ source_ids = []
192
+ knowledge_pairs = []
193
  for i, idx in enumerate(sorted_indices, 1):
194
  result = results[idx]
195
  score = cosine_scores[idx]
196
+ question = result.metadata.get('question', 'N/A')
197
+ answer = result.metadata.get('content', 'N/A')
198
  formatted_context += f"Knowledge Entry {i}:\n"
199
+ formatted_context += f"Q: {question}\n"
200
+ formatted_context += f"A: {answer}\n"
201
  formatted_context += "-" * 40 + "\n"
202
+ source_ids.append(result.metadata.get('id', 'N/A'))
203
+ knowledge_pairs.append((question, answer))
204
+ return formatted_context, source_ids, knowledge_pairs
205
 
206
  # === LLM Generation ===
207
  def generate_xeno_response(context, question):
 
224
 
225
  # Step 2: Handle simple intents directly
226
  if intent_classifier.is_simple_intent(intent) and direct_response:
227
+ log_response(message, direct_response, "N/A", [])
228
  return direct_response
229
 
230
  # Step 3: For queries that need RAG processing
231
  if intent == 'query':
232
  # Check if message is too short or unclear
233
  if len(message.strip()) < 3:
234
+ answer = "I'd be happy to help! Could you please provide more details about what you'd like to know about XENO services?"
235
+ log_response(message, answer, "N/A", [])
236
+ return answer
237
 
238
  # Retrieve relevant documents
239
  try:
 
259
 
260
  # If none of the results have sufficient similarity, fallback
261
  if max(cosine_scores) < 0.4:
262
+ answer = "I'm sorry, I couldn't find the specific information you're looking for in my knowledge base. Could you try rephrasing your question or contact XENO support directly for assistance?"
263
+ log_response(message, answer, "N/A", [])
264
+ return answer
265
 
266
+ context, source_ids, knowledge_pairs = process_context(queried_results, cosine_scores)
267
+ answer = generate_xeno_response(context, message)
268
+ log_response(message, answer, ", ".join(source_ids), knowledge_pairs)
269
+ return answer
270
 
271
  except Exception as e:
272
+ answer = "I apologize, but I'm experiencing a technical issue. Please contact XENO support directly for assistance with your query."
273
+ log_response(message, answer, "N/A", [])
274
+ return answer
275
 
276
+ # Handle goodbye intent (not simple, but has direct response)
277
+ if intent == 'goodbye' and direct_response:
278
+ log_response(message, direct_response, "N/A", [])
279
+ return direct_response
280
+
281
  # Fallback for any unhandled cases
282
+ answer = "I'm here to help with XENO financial services. What would you like to know?"
283
+ log_response(message, answer, "N/A", [])
284
+ return answer
285
 
286
  # === Enhanced Gradio UI ===
287
  def create_interface():
 
305
  # === Main Execution ===
306
  if __name__ == "__main__":
307
  iface = create_interface()
308
+ iface.launch(share=False)