Hybrid_RAG_CHAT / api.py
Aakash010's picture
Upload folder using huggingface_hub
5f0251d verified
Raw
History Blame Contribute Delete
3.87 kB
"""
Flask API backend for Hybrid RAG chatbot.
Exposes two endpoints consumed by the Gradio frontend:
POST /ingest — upload & index documents for a session user
POST /chat — ask a question with optional history
"""
import os
import uuid
import tempfile
import dotenv
from flask import Flask, request, jsonify
from langchain_core.messages import HumanMessage, AIMessage
from Ingestion import ingest_docs
from LlmIntegration import history_aware_generation
dotenv.load_dotenv()
app = Flask(__name__)
# In-memory chat history store: { user_id: [LangChain message, ...] }
# For production consider Redis; fine for HF Spaces (single-process)
_chat_histories: dict[str, list] = {}
@app.route("/health", methods=["GET"])
def health():
return jsonify({"status": "ok"})
@app.route("/ingest", methods=["POST"])
def ingest():
"""
Expects multipart/form-data:
- files: one or more files (PDF / .txt)
- user_id: (optional) existing session ID; a new one is created if absent
Returns JSON:
{ "user_id": "...", "chunks_ingested": N, "message": "..." }
"""
user_id = request.form.get("user_id") or str(uuid.uuid4())
uploaded_files = request.files.getlist("files")
if not uploaded_files:
return jsonify({"error": "No files provided"}), 400
saved_paths: list[str] = []
tmp_dir = tempfile.mkdtemp()
for f in uploaded_files:
safe_name = os.path.basename(f.filename or "upload")
dest = os.path.join(tmp_dir, safe_name)
f.save(dest)
saved_paths.append(dest)
try:
count = ingest_docs(saved_paths, user_id=user_id)
except Exception as e:
return jsonify({"error": str(e)}), 500
finally:
# Clean up temp files
for p in saved_paths:
try:
os.remove(p)
except OSError:
pass
# Reset chat history when new docs are ingested
_chat_histories[user_id] = []
return jsonify(
{
"user_id": user_id,
"chunks_ingested": count,
"message": f"Successfully ingested {count} chunks from {len(saved_paths)} file(s).",
}
)
@app.route("/chat", methods=["POST"])
def chat():
"""
Expects JSON body:
{ "user_id": "...", "question": "..." }
Returns JSON:
{
"answer": "...",
"sources": ["file1.pdf", ...],
"user_id": "..."
}
"""
data = request.get_json(force=True, silent=True) or {}
user_id = data.get("user_id", "").strip()
question = data.get("question", "").strip()
if not user_id:
return jsonify({"error": "user_id is required"}), 400
if not question:
return jsonify({"error": "question is required"}), 400
history = _chat_histories.get(user_id, [])
try:
docs, answer = history_aware_generation(question, history, user_id=user_id)
except Exception as e:
return jsonify({"error": str(e)}), 500
# Update history
history.append(HumanMessage(content=question))
history.append(AIMessage(content=answer))
_chat_histories[user_id] = history
sources = list({doc["metadata"].get("source", "unknown") for doc in docs})
return jsonify(
{
"answer": answer,
"sources": sources,
"user_id": user_id,
}
)
@app.route("/reset", methods=["POST"])
def reset():
"""
Clears chat history for a user (does NOT delete vectors from Pinecone).
Expects JSON: { "user_id": "..." }
"""
data = request.get_json(force=True, silent=True) or {}
user_id = data.get("user_id", "").strip()
if user_id in _chat_histories:
_chat_histories[user_id] = []
return jsonify({"message": "Chat history cleared.", "user_id": user_id})
if __name__ == "__main__":
app.run(host="0.0.0.0", port=7861, debug=False)