WebIQ_backend / main.py
schandel08's picture
Frontend Origin added
639d612
# main.py
from dotenv import load_dotenv
load_dotenv()
import sys
import uuid
import asyncio
import logging
from fastapi import FastAPI, HTTPException, WebSocket, WebSocketDisconnect, BackgroundTasks
from fastapi.middleware.cors import CORSMiddleware
from chatbot import Chatbot
# -----------------------------
# Windows Asyncio Fix
# -----------------------------
if sys.platform == "win32":
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
# -----------------------------
# FastAPI app & CORS
# -----------------------------
app = FastAPI(
title="Session-Based RAG Chatbot API",
description="Session-based RAG Chatbot API with WebSocket support",
version="1.1.0"
)
origins = [
"http://localhost:8080",
"http://127.0.0.1:8080",
"http://127.0.0.1:5500",
"https://vaishnavibasukar20.github.io/WebIQ-Frontend"
]
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# -----------------------------
# Session storage
# -----------------------------
chatbot_sessions = {} # {session_id: Chatbot instance or None if failed}
# -----------------------------
# Root endpoint
# -----------------------------
@app.get("/")
def read_root():
return {"message": "Welcome to the Session-Based RAG Chatbot API!", "status": "Ready"}
@app.get("/create_session")
def create_session():
return {"session":str(uuid.uuid4())}
@app.get("/session_status/{session_id}")
def session_status(session_id: str):
"""
Returns the current status of a chatbot session.
Status can be:
- initializing (session exists but chatbot not ready)
- ready (chatbot instance ready)
- failed (chatbot initialization failed)
"""
if session_id not in chatbot_sessions:
return {"status": "not_found"}
chatbot = chatbot_sessions[session_id]
if chatbot is None:
return {"status": "initializing"}
elif chatbot == "err":
chatbot = None
return {"status": "failed"}
return {"status": "ready"}
# -----------------------------
# Helper: Run async init in background
# -----------------------------
def run_chatbot_init(session_id, urls, llm_model, embedding_model, api_key):
asyncio.create_task(initialize_chatbot(session_id, urls, llm_model, embedding_model, api_key))
# -----------------------------
# Scrape & initialize chatbot
# -----------------------------
@app.post("/scrape/")
async def scrape_and_load(response: dict, background_tasks: BackgroundTasks):
session_id = response.get("session_id")
urls = response.get("urls")
llm_model = response.get("llm_model", "TheBloke/Llama-2-7B-Chat-GGML")
embedding_model = response.get("embedding_model", "BAAI/bge-small-en")
api_key = response.get("api_key", None)
if not urls:
raise HTTPException(status_code=400, detail="urls are required.")
if session_id in chatbot_sessions:
return {"message": f"Chatbot for session {session_id} already initialized.", "session_id": session_id}
# Mark session as initializing
chatbot_sessions[session_id] = None
# Use a **blocking wrapper** to run async in thread safely
async def init_wrapper():
try:
await initialize_chatbot(session_id, urls, llm_model, embedding_model, api_key)
except Exception as e:
logging.error(f"[{session_id}] Initialization error: {e}", exc_info=True)
chatbot_sessions[session_id] = None
background_tasks.add_task(init_wrapper)
logging.info(f"[{session_id}] Chatbot initialization scheduled in background.")
return {"message": "Chatbot initialization started.", "session_id": session_id}
# -----------------------------
# Initialize chatbot
# -----------------------------
async def initialize_chatbot(session_id, urls, llm_model, embedding_model, api_key):
try:
logging.info(f"[{session_id}] Initializing chatbot...")
chatbot = Chatbot(
url=urls,
llm_model=llm_model,
embedding_model=embedding_model,
api_key=api_key
)
await chatbot.initialize()
chatbot_sessions[session_id] = chatbot
logging.info(f"[{session_id}] Chatbot ready.")
except NotImplementedError as e:
logging.error(f"[{session_id}] Playwright async not supported on Windows: {e}", exc_info=True)
chatbot_sessions[session_id] = None
except Exception as e:
logging.error(f"[{session_id}] Initialization failed: {e}", exc_info=True)
chatbot_sessions[session_id] = "err"
# -----------------------------
# WebSocket endpoint
# -----------------------------
@app.websocket("/ws/chat/{session_id}")
async def websocket_endpoint(websocket: WebSocket, session_id: str):
await websocket.accept()
logging.info(f"[{session_id}] WebSocket connected.")
try:
# Wait until chatbot is ready
while session_id not in chatbot_sessions or chatbot_sessions[session_id] is None:
await websocket.send_json({"text": "Initializing chatbot, please wait..."})
await asyncio.sleep(1)
chatbot_instance = chatbot_sessions[session_id]
if chatbot_instance is None:
await websocket.send_json({
"text": "Chatbot initialization failed. Likely due to Playwright async issue on Windows."
})
return
await websocket.send_json({"text": f"Chatbot session {session_id} is ready! You can start chatting."})
while True:
data = await websocket.receive_json()
query = data.get("query")
if not query:
continue
response_text = await chatbot_instance.query(query)
await websocket.send_json({"text": response_text})
except WebSocketDisconnect:
logging.info(f"[{session_id}] WebSocket disconnected.")
except Exception as e:
logging.error(f"[{session_id}] WebSocket error: {e}", exc_info=True)
try:
await websocket.send_json({"text": "An unexpected server error occurred."})
except:
pass
# -----------------------------
# Run with: uvicorn main:app --reload
# -----------------------------
if __name__ == "__main__":
import uvicorn
uvicorn.run("main:app", host="127.0.0.1", port=8000, reload=True)