File size: 3,486 Bytes
7e477c2
 
858a55a
7e477c2
858a55a
 
 
 
 
7e477c2
858a55a
 
 
 
 
 
 
 
 
7e477c2
 
 
 
858a55a
 
 
 
 
 
 
 
 
7e477c2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
858a55a
 
 
7e477c2
 
 
 
 
858a55a
 
 
fec7fe0
 
7e477c2
 
fec7fe0
 
7e477c2
 
 
 
 
 
fec7fe0
7e477c2
fec7fe0
7e477c2
fec7fe0
 
 
7e477c2
fec7fe0
7e477c2
fec7fe0
 
 
 
 
 
7e477c2
d753581
7e477c2
 
 
 
 
 
 
 
 
 
fec7fe0
 
7e477c2
fec7fe0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105

# 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}"
)

@app.on_event("startup")
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}")

@app.get("/")
async def health_check():
    return {
        "status": "ok",
        "kb_count": collection.count(),
        "docs_dir_exists": DOCS_DIR.exists()
    }

@app.post("/chat")
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()}"
    }