Spaces:
Build error
Build error
| import os | |
| import gradio as gr | |
| import pandas as pd | |
| import torch | |
| import numpy as np | |
| from sentence_transformers import util | |
| import google.generativeai as genai | |
| import chromadb | |
| from langchain_chroma import Chroma | |
| # === Configuration === | |
| genai.configure(api_key=os.environ["GEMINI_API_KEY"]) | |
| embedding_model = "models/embedding-001" | |
| llm_model_name = "models/gemma-3-4b-it" | |
| collection_name = "xeno_collection" | |
| # === Load and Clean Knowledge Base === | |
| df_kb = pd.read_json("XENO_Uganda_KnowledgeBase_Advisory.json") | |
| df_kb.dropna(subset=['Content'], inplace=True) | |
| def prepare_documents(data): | |
| documents, metadatas, ids = [], [], [] | |
| for item in data: | |
| documents.append(f"Question: {item['Question']}\nAnswer: {item['Content']}") | |
| metadatas.append({ | |
| "question": item["Question"], | |
| "content": item["Content"], | |
| "section": item.get("Section", ""), | |
| "source": item.get("Source", ""), | |
| "owner": item.get("Owner", ""), | |
| "tag": item.get("Tag", "") | |
| }) | |
| ids.append(item["ID"]) | |
| return documents, metadatas, ids | |
| xeno_data_list = df_kb.to_dict('records') | |
| documents, metadatas, ids = prepare_documents(xeno_data_list) | |
| # === Setup ChromaDB === | |
| client = chromadb.PersistentClient(path="./xeno_db") | |
| try: | |
| collection = client.get_collection(name=collection_name) | |
| except: | |
| collection = client.create_collection(name=collection_name) | |
| collection.add(documents=documents, metadatas=metadatas, ids=ids) | |
| vector_store = Chroma(client=client, collection_name=collection_name) | |
| retriever = vector_store.as_retriever(search_type="similarity", search_kwargs={"k": 4}) | |
| # === Prompt System === | |
| SYSTEM_PROMPT = """You are a friendly XENO Support Assistant, an AI-powered helpful and professional customer service representative. | |
| Use only the information provided in the knowledge base context to answer user queries. | |
| Do not hallucinate. If context doesn't contain relevant info, say so in a calm polite manner. | |
| Only use context that is clearly relevant to the user's question. | |
| For greetings like “hi” or “hello”, respond politely without using the context. | |
| remember previous conversations. | |
| """ | |
| # === Context Processing === | |
| def process_context(results, cosine_scores, max_results=2): | |
| sorted_indices = np.argsort(cosine_scores)[::-1][:max_results] | |
| formatted_context = "" | |
| for i, idx in enumerate(sorted_indices, 1): | |
| result = results[idx] | |
| score = cosine_scores[idx] | |
| formatted_context += f"Knowledge Entry {i}:\n" | |
| formatted_context += f"Q: {result.metadata.get('question', 'N/A')}\n" | |
| formatted_context += f"A: {result.metadata.get('content', 'N/A')}\n" | |
| formatted_context += "-" * 40 + "\n" | |
| return formatted_context | |
| # === LLM Generation === | |
| def generate_xeno_response(context, question): | |
| model = genai.GenerativeModel(llm_model_name) | |
| prompt = f"""{SYSTEM_PROMPT} | |
| ### CONTEXT ### | |
| {context} | |
| ### QUESTION ### | |
| {question}""" | |
| response = model.generate_content(prompt) | |
| return response.text.strip() | |
| # === Main Interface Logic === | |
| def get_context_and_answer(message, history): | |
| if message.lower().strip() in {"hi", "hello", "hey"}: | |
| return "Hello! How can I assist you with XENO services today?" | |
| queried_results = retriever.invoke(message) | |
| query_embedding = genai.embed_content(model=embedding_model, | |
| content=message, | |
| task_type="retrieval_query")['embedding'] | |
| cosine_scores = [] | |
| for doc in queried_results: | |
| doc_embedding = genai.embed_content(model=embedding_model, | |
| content=doc.page_content, | |
| task_type="retrieval_document")['embedding'] | |
| cos_sim = util.cos_sim(torch.tensor(query_embedding).float(), torch.tensor(doc_embedding).float())[0][0].item() | |
| cosine_scores.append(cos_sim) | |
| # If none of the results have sufficient similarity, fallback | |
| if max(cosine_scores) < 0.4: | |
| return "I'm sorry, I couldn't find the specific information you're looking for in my knowledge base." | |
| context = process_context(queried_results, cosine_scores) | |
| return generate_xeno_response(context, message) | |
| # === Gradio UI === | |
| iface = gr.ChatInterface( | |
| fn=get_context_and_answer, | |
| title="ASKXENO", | |
| description="Ask anything about XENO's financial services.", | |
| theme="soft" | |
| ) | |
| if __name__ == "__main__": | |
| iface.launch() |