| import os |
| import requests |
| import shutil |
| from langchain_community.vectorstores import FAISS |
| from fastapi import FastAPI |
| from pydantic import BaseModel |
| from langchain_huggingface import HuggingFaceEmbeddings |
| from langchain_core.runnables import RunnablePassthrough |
| from langchain_core.prompts import PromptTemplate |
| from langchain_groq import ChatGroq |
|
|
| |
| TEMP_CACHE_DIR = '/tmp/huggingface_cache' |
| os.environ['TRANSFORMERS_CACHE'] = TEMP_CACHE_DIR |
| os.environ['HF_HOME'] = TEMP_CACHE_DIR |
| os.environ['SENTENCE_TRANSFORMERS_HOME'] = TEMP_CACHE_DIR |
| os.makedirs(TEMP_CACHE_DIR, exist_ok=True) |
|
|
| |
| URL_FAISS = "https://drive.google.com/uc?export=download&id=1hiVycS4DQHO1MBdC-L_z1TXA6sJO_Y-r" |
| URL_PKL = "https://drive.google.com/uc?export=download&id=1vbG8unx88Kb5jn7puGv1gqSM4S6rIUQC" |
| DOWNLOAD_DIR = "/tmp/db_faiss" |
| DB_FAISS_PATH = DOWNLOAD_DIR |
|
|
| |
| CONDENSE_PROMPT = PromptTemplate( |
| template="""Dada la conversación y la pregunta, reescríbela para que sea independiente y clara sobre la UPT Aragua. |
| Historial: {chat_history} |
| Pregunta: {question} |
| Pregunta reescrita:""", |
| input_variables=["chat_history", "question"] |
| ) |
|
|
| INTENT_PROMPT = PromptTemplate( |
| template="Categoriza el mensaje en: SALUDO, UNIVERSIDAD u OTRO. Responde solo la palabra. Mensaje: {query}", |
| input_variables=["query"] |
| ) |
|
|
| SALUDO_PROMPT = PromptTemplate( |
| template="Eres UPTA bot. Saluda cordialmente. Mensaje: {query}", |
| input_variables=["query"] |
| ) |
|
|
| RAG_PROMPT = PromptTemplate( |
| template="""Eres UPTA bot. Responde usando solo el contexto. |
| Contexto: {context} |
| Pregunta: {question} |
| Respuesta:""", |
| input_variables=["context", "question"] |
| ) |
|
|
| |
| class QueryRequest(BaseModel): |
| query: str |
| history: list = [] |
|
|
| def download_file(url, local_path): |
| headers = {'User-Agent': 'Mozilla/5.0'} |
| os.makedirs(os.path.dirname(local_path), exist_ok=True) |
| with requests.get(url, stream=True, headers=headers, timeout=30) as r: |
| with open(local_path, 'wb') as f: |
| shutil.copyfileobj(r.raw, f) |
|
|
| def load_rag(): |
| download_file(URL_FAISS, os.path.join(DOWNLOAD_DIR, 'index.faiss')) |
| download_file(URL_PKL, os.path.join(DOWNLOAD_DIR, 'index.pkl')) |
| |
| embeddings = HuggingFaceEmbeddings( |
| model_name="sentence-transformers/all-MiniLM-L6-v2", |
| model_kwargs={'device': 'cpu'}, |
| cache_folder=TEMP_CACHE_DIR |
| ) |
| vectorstore = FAISS.load_local(DB_FAISS_PATH, embeddings, allow_dangerous_deserialization=True) |
| |
| |
| llm = ChatGroq(temperature=0.1, model_name="openai/gpt-oss-120b") |
| |
| retriever = vectorstore.as_retriever(search_kwargs={"k": 3}) |
| |
| return ( |
| CONDENSE_PROMPT | llm, |
| INTENT_PROMPT | llm, |
| SALUDO_PROMPT | llm, |
| ( {"context": retriever, "question": RunnablePassthrough()} | RAG_PROMPT | llm ), |
| retriever |
| ) |
|
|
| |
| app = FastAPI() |
| condense_c, intent_c, saludo_c, rag_c, retriever = (None, None, None, None, None) |
|
|
| @app.on_event("startup") |
| async def startup(): |
| global condense_c, intent_c, saludo_c, rag_c, retriever |
| condense_c, intent_c, saludo_c, rag_c, retriever = load_rag() |
|
|
| @app.get("/") |
| def root(): |
| return {"status": "ready"} |
|
|
| @app.post("/query") |
| async def process_query(request: QueryRequest): |
| try: |
| |
| chat_str = "\n".join([f"U: {m[0]}\nB: {m[1]}" for m in request.history]) |
|
|
| |
| q_final = request.query |
| if request.history: |
| res_c = condense_c.invoke({"chat_history": chat_str, "question": request.query}) |
| q_final = res_c.content.strip() |
|
|
| |
| res_i = intent_c.invoke({"query": q_final}) |
| intent = res_i.content.upper() |
|
|
| if "SALUDO" in intent: |
| res_s = saludo_c.invoke({"query": request.query}) |
| return {"response": res_s.content, "intent": "SALUDO"} |
| |
| elif "OTRO" in intent: |
| return {"response": "Solo respondo sobre la UPT Aragua.", "intent": "OTRO"} |
| |
| else: |
| |
| res_r = rag_c.invoke(q_final) |
| docs = retriever.invoke(q_final) |
| sources = list(set([d.metadata.get("source", "N/A") for d in docs])) |
| return { |
| "response": res_r.content, |
| "intent": "UNIVERSIDAD", |
| "sources": sources, |
| "contextual_query": q_final |
| } |
|
|
| except Exception as e: |
| |
| |
| return {"error": str(e)} |
| except Exception as e: |
| return {"error": f"Error al procesar la consulta: {e}"} |