Spaces:
Sleeping
Sleeping
| # main.py | |
| import os | |
| os.environ["POSTHOG_DISABLED"] = "true" | |
| import requests | |
| from fastapi import FastAPI, HTTPException | |
| from fastapi.middleware.cors import CORSMiddleware | |
| from pydantic import BaseModel | |
| from dotenv import load_dotenv | |
| from kb_embed import search_knowledge_base, ingest_documents, collection, DOCS_DIR | |
| import logging | |
| logging.basicConfig(level=logging.INFO) | |
| load_dotenv() | |
| app = FastAPI() | |
| app.add_middleware( | |
| CORSMiddleware, | |
| allow_origins=[ | |
| "https://jaita-chatbot-react-frontend-v1.hf.space", # frontend space origin | |
| "https://jaita-chatbot-fastapi-backend.hf.space", # backend space origin | |
| ], | |
| allow_credentials=True, | |
| allow_methods=["*"], | |
| allow_headers=["*"], | |
| ) | |
| class ChatInput(BaseModel): | |
| user_message: str | |
| GEMINI_API_KEY = os.getenv("GEMINI_API_KEY") | |
| GEMINI_URL = ( | |
| f"https://generativelanguage.googleapis.com/v1beta/models/" | |
| f"gemini-2.5-flash-lite:generateContent?key={GEMINI_API_KEY}" | |
| ) | |
| def startup_ingest(): | |
| try: | |
| # Make sure DOCS_DIR exists and has .docx files, then ingest | |
| if DOCS_DIR.exists(): | |
| logging.info(f"Starting KB ingestion from: {DOCS_DIR}") | |
| ingest_documents(str(DOCS_DIR)) | |
| else: | |
| logging.warning(f"Docs directory not found: {DOCS_DIR}") | |
| logging.info(f"Chroma collection count after startup: {collection.count()}") | |
| except Exception as e: | |
| logging.exception(f"KB ingestion failed: {e}") | |
| async def health_check(): | |
| return { | |
| "status": "ok", | |
| "kb_count": collection.count(), | |
| "docs_dir_exists": DOCS_DIR.exists() | |
| } | |
| async def chat_with_gemini(input_data: ChatInput): | |
| # 1. Search Knowledge Base | |
| kb_results = search_knowledge_base(input_data.user_message, top_k=10) | |
| logging.info(f"KB query results keys: {list(kb_results.keys()) if kb_results else 'None'}") | |
| context = "" | |
| relevant_docs = [] | |
| # 2. Extract relevant KB docs if present | |
| if kb_results and kb_results.get("documents") and len(kb_results["documents"]) > 0: | |
| # kb_results["documents"] is a list of lists (one per query) | |
| first_query_docs = kb_results["documents"][0] | |
| relevant_docs = first_query_docs[:2] | |
| context = "\n\n".join(relevant_docs) | |
| # 3. If KB contains direct answer → RETURN it (No LLM call) | |
| if context.strip(): | |
| kb_answer = f"From knowledge base:\n\n{context}" | |
| return { | |
| "bot_response": kb_answer, | |
| "debug_info": f"Context found: YES, docs used: {len(relevant_docs)}, kb_count: {collection.count()}" | |
| } | |
| # 4. If KB empty → fallback to Gemini | |
| enhanced_prompt = ( | |
| f"User question: {input_data.user_message}\n\n" | |
| "No relevant KB found. You must raise a ticket.\n" | |
| "Say: 'I'm raising a ticket. Ticket# 12345'." | |
| ) | |
| headers = {"Content-Type": "application/json"} | |
| payload = {"contents": [{"parts": [{"text": enhanced_prompt}]}]} | |
| try: | |
| response = requests.post(GEMINI_URL, headers=headers, json=payload) | |
| result = response.json() | |
| bot_response = result["candidates"][0]["content"]["parts"][0]["text"] | |
| except Exception as e: | |
| logging.exception(f"Gemini call failed: {e}") | |
| raise HTTPException(status_code=500, detail="LLM call failed") | |
| return { | |
| "bot_response": bot_response, | |
| "debug_info": f"Context found: NO, kb_count: {collection.count()}" | |
| } | |