from fastapi import FastAPI, File, UploadFile , Request
from fastapi.routing import APIRoute
from fastapi.responses import HTMLResponse, JSONResponse
from fastapi.middleware import Middleware
from fastapi.middleware.cors import CORSMiddleware
from starlette.middleware.base import BaseHTTPMiddleware
from fastapi.staticfiles import StaticFiles
import os, pandas as pd
from typing import List
from langchain_community.embeddings import OpenAIEmbeddings
from langchain_community.vectorstores import FAISS
from langchain_openai import ChatOpenAI
from langchain.chains import RetrievalQA
from langchain.prompts import PromptTemplate
from datetime import datetime, timedelta, timezone
from dotenv import load_dotenv
import io
from starlette.responses import Response
class CaseInsensitiveRoute(APIRoute):
def matches(self, scope: dict) -> tuple[bool, dict]:
# Only lowercase if it's not a static file
if scope["type"] == "http" and not scope["path"].startswith("/static"):
scope["path"] = scope["path"].lower()
return super().matches(scope)
load_dotenv()
app = FastAPI()
app.mount("/static", StaticFiles(directory="static"), name="static")
middleware = [
Middleware(CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"])
]
app.router.route_class = CaseInsensitiveRoute
PERSISTENT_FAISS_PATH = "/tmp/faiss_store"
os.makedirs(PERSISTENT_FAISS_PATH, exist_ok=True)
vector_store = None
@app.get("/bs-nagesh")
async def serve_index():
return HTMLResponse(content="""
AI Receptionist
Idle. Please say "hello" to begin.
""")
@app.post("/upload_file")
async def upload_file(file: UploadFile = File(...)):
global vector_store
# β Get API key at runtime inside endpoint
openai_key = os.getenv("OPENAI_API_KEY")
if not openai_key:
return JSONResponse(status_code=500, content={"error": "OPENAI_API_KEY not found in environment."})
# β Load uploaded file
try:
if file.filename.endswith("xlsx"):
contents = await file.read()
df = pd.read_excel(io.BytesIO(contents))
elif file.filename.endswith("csv"):
df = pd.read_csv(file.file)
else:
return JSONResponse(status_code=400, content={"error": "Unsupported file format. Use .csv or .xlsx"})
except Exception as e:
return JSONResponse(status_code=500, content={"error": f"File parsing failed: {str(e)}"})
# β Convert DataFrame to document strings
try:
docs = [", ".join(f"{col}: {val}" for col, val in row.items()) for _, row in df.iterrows()]
except Exception as e:
return JSONResponse(status_code=500, content={"error": f"Data conversion failed: {str(e)}"})
# β Create and persist vector store
try:
embeddings = OpenAIEmbeddings(openai_api_key=openai_key)
vector_store = FAISS.from_texts(docs, embedding=embeddings)
vector_store.save_local(PERSISTENT_FAISS_PATH)
except Exception as e:
return JSONResponse(status_code=500, content={"error": f"Vector store error: {str(e)}"})
return {"message": "Data indexed successfully."}
@app.post("/ask")
async def ask_question(payload: dict):
global vector_store
query = payload.get("query")
if not query:
return JSONResponse(status_code=400, content={"error": "Query missing."})
if vector_store is None:
if os.path.exists(os.path.join(PERSISTENT_FAISS_PATH, "index.faiss")):
vector_store = FAISS.load_local(
PERSISTENT_FAISS_PATH,
OpenAIEmbeddings(),
allow_dangerous_deserialization=True
)
else:
return JSONResponse(status_code=500, content={"error": "Vector store not initialized."})
ist = timezone(timedelta(hours=5, minutes=30))
current_time = datetime.now(ist).strftime("%A, %d %B %Y, %I:%M %p")
prompt_template = PromptTemplate(
input_variables=["context", "question"],
template=f"""
You are an intelligent AI UnoAvatar for Mr. BS Nagesh. You give very crisp and short answers. You talk as him in first person. You answer in brief and short sentences
You are trained on his life and you know everything about him. You are a participant at the the Food Forum 2025.
Answer user queries based on the provided context below.
Use natural tone, be helpful and factual. Todayβs IST date and time is: {current_time}.
Context:
{{context}}
Question: {{question}}
Answer:"""
)
llm = ChatOpenAI(model_name="gpt-4o-mini-2024-07-18", temperature=0.3)
retriever = vector_store.as_retriever()
qa = RetrievalQA.from_chain_type(
llm=llm,
retriever=retriever,
chain_type="stuff",
chain_type_kwargs={"prompt": prompt_template}
)
result = qa.run(query)
return {"answer": result}
@app.post("/ask_foodforum")
async def ask_foodforum(payload: dict):
global vector_store
query = payload.get("query")
if not query:
return JSONResponse(status_code=400, content={"error": "Query missing."})
if vector_store is None:
if os.path.exists(os.path.join(PERSISTENT_FAISS_PATH, "index.faiss")):
vector_store = FAISS.load_local(
PERSISTENT_FAISS_PATH,
OpenAIEmbeddings(),
allow_dangerous_deserialization=True
)
else:
return JSONResponse(status_code=500, content={"error": "Vector store not initialized."})
ist = timezone(timedelta(hours=5, minutes=30))
current_time = datetime.now(ist).strftime("%A, %d %B %Y, %I:%M %p")
# π Food Forum 2025 Prompt
prompt_template = PromptTemplate(
input_variables=["context", "question"],
template=f"""
You are the AI Concierge for **Food Forum 2025**, Indiaβs most influential summit on food, beverage, and hospitality innovation.
You are informative, helpful, and polite β designed to guide participants, media, and partners through the event experience.
Answer briefly and clearly in **English** unless asked otherwise.
Only use the context provided to you β do not guess.
If unsure, say: "Please check with the Food Forum Help Desk for the latest details."
Date & Time (IST): {current_time}
Context:
{{context}}
Question: {{question}}
Answer:
"""
)
llm = ChatOpenAI(model_name="gpt-4o-mini-2024-07-18", temperature=0.3)
retriever = vector_store.as_retriever()
qa = RetrievalQA.from_chain_type(
llm=llm,
retriever=retriever,
chain_type="stuff",
chain_type_kwargs={"prompt": prompt_template}
)
result = qa.run(query)
return {"answer": result}
@app.get("/welcome", response_class=HTMLResponse)
async def food_forum():
return HTMLResponse(content="""
Food Forum 2025
""")
# Endpoint to render the video playback page
@app.get("/food-forum", response_class=HTMLResponse)
async def food_forum():
return HTMLResponse(content="""
Food Forum 2025
""")
@app.get("/jewellery")
async def serve_index():
return HTMLResponse(content="""
AD Yogesh β AI Receptionist