from fastapi import APIRouter, Depends, HTTPException, Request from fastapi.responses import JSONResponse from utils.auth import get_current_user from utils.query_utils import llm, get_message_history, context_retriever,escape_template_string from constants.prompts import user_message from models.schemas import RequestModel ,SendChat, AddVisitorRequest from langchain_core.runnables.history import RunnableWithMessageHistory from langchain_core.output_parsers import StrOutputParser from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder from models.tables import Company, Queries, Chatbot_stats, QueryUsers from config.db import SessionLocal from sqlalchemy.orm import Session from typing import Annotated import datetime from utils.query_utils import count_tokens,meeting_finder import logging import traceback from constants.email import bot_chat_template from utils.mailket_utils import send_email_with_template import os from dotenv import load_dotenv load_dotenv() router = APIRouter(tags=['query']) # When true, skip the strict Origin/registered-domain match. Useful for demos # (e.g. testing from Swagger UI on a Hugging Face Space). Leave false in prod. DISABLE_ORIGIN_CHECK = os.getenv("DISABLE_ORIGIN_CHECK", "false").lower() == "true" tools=[] logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) def get_db(): db = SessionLocal() try: yield db finally: db.close() db_dependency = Annotated[Session, Depends(get_db)] @router.post("/query") async def answer_query(req: RequestModel, request: Request, db: db_dependency, user: dict = Depends(get_current_user)): try: collection_name = user.company_key chatbot_id = req.chatbot_id session_id = req.session_id logger.info("Chatbot ID: %s", req.chatbot_id) logger.info("Session ID: %s", req.session_id) logger.info("Company Key: %s", user.company_key) logger.info("Request Headers: %s", request.headers) origin_url = request.headers.get("origin") if origin_url is None and not DISABLE_ORIGIN_CHECK: logger.error("Missing Origin Header") raise HTTPException(status_code=400, detail="Missing Origin Header") chatbot_stats = db.query(Chatbot_stats).filter(Chatbot_stats.chatbot_id == req.chatbot_id).first() if not chatbot_stats: logger.error("Chatbot not found: %s", req.chatbot_id) raise HTTPException(status_code=404, detail="Chatbot not found") logger.info("Prompt tmeplate: Type %s", type(chatbot_stats.chatbot_prompt)) promt_template = chatbot_stats.chatbot_prompt logger.info("Prompt tmeplate: %s", promt_template) prompt = ChatPromptTemplate.from_messages( [ ("system", promt_template), MessagesPlaceholder(variable_name="history"), ("human", "{input}"), ] ) if not DISABLE_ORIGIN_CHECK: if chatbot_stats.origin_url is None: logger.error("Chatbot origin URL is None") raise HTTPException(status_code=500, detail="Chatbot origin URL is None") logger.info("Chatbot Origin URL: %s", chatbot_stats.origin_url) logger.info("Request Origin URL: %s", origin_url) if chatbot_stats.origin_url.strip().rstrip('/') != origin_url.strip().rstrip('/'): logger.warning("Unauthorized Domain: %s", origin_url) raise HTTPException(status_code=401, detail="Unauthorized Domain") rag_chain = prompt | llm | StrOutputParser() with_message_history = RunnableWithMessageHistory( rag_chain, get_message_history, input_messages_key="input", history_messages_key="history", ) final_response = await with_message_history.ainvoke( { "context": context_retriever(req.query, session_id, user.id, chatbot_id, db, collection_name), "input": req.query, "chatbot_name": chatbot_stats.chatbot_name, "base_url": user.base_url, }, config={"configurable": {"session_id": req.session_id}}, ) history = get_message_history(req.session_id) if history is None: logger.error("Message history retrieval failed for session ID: %s", req.session_id) raise HTTPException(status_code=500, detail="Message history retrieval failed") query_model = db.query(Queries).filter( Queries.company_id == user.id, Queries.chatbot_id == req.chatbot_id, Queries.session_id == req.session_id, Queries.query_text_user == req.query ).first() if query_model is None: logger.error("Query not found for user ID: %s, chatbot ID: %s, session ID: %s", user.id, req.chatbot_id, req.session_id) raise HTTPException(status_code=404, detail="Query not found") context = query_model.query_context if context is None: logger.error("Query context is None for session ID: %s", req.session_id) raise HTTPException(status_code=500, detail="Query context is None") combined_input = f"{context}\n{history}\n{req.query}" input_token = count_tokens(combined_input) + 10 output_token = count_tokens(final_response) query_model.query_text_bot = final_response query_model.input_tokens = input_token query_model.output_tokens = output_token query_model.origin_url = origin_url chatbot_stats.total_input_tokens += input_token chatbot_stats.total_output_tokens += output_token chatbot_stats.total_queries += 1 chatbot_stats.last_query_time = datetime.datetime.now() company = db.query(Company).filter(Company.id == user.id).first() if company: company.input_tokens += input_token company.output_tokens += output_token db.commit() logger.info("Successfully processed query for user ID: %s", user.id) return final_response except AttributeError as ae: logger.error("AttributeError: %s", str(ae)) raise HTTPException(status_code=500, detail=f"An error occurred: {str(ae)}") except Exception as e: logger.error("An unexpected error occurred: %s", str(e)) logger.error("Traceback: %s", traceback.format_exc()) db.rollback() # Rollback on error raise HTTPException(status_code=500, detail=f"An error occurred: {str(e)}") @router.post("/send-chat/") def send_chat_email(req: SendChat, request: Request, db: db_dependency,user: dict = Depends(get_current_user)): try: logger.info("Incoming Request: %s", request) chatbot_id = req.chatbot_id session_id = req.session_id chat_history = req.chatHistory logger.info("Chatbot ID: %s", req.chatbot_id) logger.info("Session ID: %s", req.session_id) ip_address = request.client.host logger.info("Incoming Request Ip: %s", ip_address) print(ip_address) origin_url = request.headers.get("origin") if origin_url is None: logger.error("Missing Origin Header") raise HTTPException(status_code=400, detail="Missing Origin Header") chatbot_stats = db.query(Chatbot_stats).filter(Chatbot_stats.chatbot_id == chatbot_id).first() email_chatter = db.query(QueryUsers).filter(QueryUsers.session_id == session_id).first() if not chatbot_stats: logger.error("Chatbot not found: %s", req.chatbot_id) raise HTTPException(status_code=404, detail="Chatbot not found") email = os.getenv('RECIPIENT_EMAIL') send_email_with_template( recipent_email=email, subject="Chatbot Chat", company_name=user.company_name, base_link=chatbot_stats.origin_url, chatbot_name=chatbot_stats.chatbot_name, session_id=session_id, email = email_chatter.email, ip_address=ip_address, chat_history=chat_history, template=bot_chat_template ) return JSONResponse(content={"status": "200", 'message': 'Email sent successfully'}) except Exception as e: logger.error("An unexpected error occurred: %s", str(e)) logger.error("Traceback: %s", traceback.format_exc()) db.rollback() raise HTTPException(status_code=500, detail=f"An error occurred: {str(e)}") @router.post("/add-visitor/") def add_visitor(req: AddVisitorRequest, request: Request,db:db_dependency): try: query_user = db.query(QueryUsers).filter(QueryUsers.session_id == req.session_id).first() if query_user: query_user.email = req.email query_user.phone_number = req.phone_number db.commit() logger.warning("Visitor with this session ID already exists and updated: %s", req.session_id) return {"status": "200", "message": "Visitor successfully updated"} ip_address = request.client.host logger.info("Incoming Request: %s", req) logger.info("Origin URL: %s, Session ID: %s,IP Address: %s" , req.origin_url, req.session_id,ip_address) new_visitor = QueryUsers( session_id=req.session_id, chatbot_id=req.chatbot_id, ip_address=ip_address, origin_url=req.origin_url, timezone=req.timezone, language=req.language, is_mobile=req.is_mobile, user_agent=req.user_agent, platform=req.platform, referrer=req.referrer, location=req.location, network_type=req.network_type, email=req.email, phone_number=req.phone_number ) db.add(new_visitor) db.commit() return {"status": "200", "message": "Visitor successfully added"} except Exception as e: # Rollback transaction if error occurs logger.error("An unexpected error occurred: %s", str(e)) logger.error("Traceback: %s", traceback.format_exc()) db.rollback() raise HTTPException(status_code=500, detail=f"An error occurred: {str(e)}")