File size: 4,832 Bytes
0a729a6
 
 
 
 
 
 
 
 
 
 
9e0e903
0a729a6
 
 
 
 
 
9e0e903
0a729a6
 
 
 
 
9e0e903
ce734ca
9e0e903
 
 
 
ce734ca
 
 
0a729a6
9e0e903
0a729a6
 
 
 
9e0e903
0a729a6
 
 
 
9e0e903
ce734ca
 
0a729a6
 
 
 
9e0e903
0a729a6
 
9e0e903
0a729a6
 
 
ce734ca
9e0e903
 
 
0a729a6
9e0e903
ce734ca
9e0e903
ce734ca
 
 
 
 
 
 
 
9e0e903
 
ce734ca
9e0e903
ce734ca
9e0e903
 
 
 
 
 
ce734ca
0a729a6
9e0e903
ce734ca
9e0e903
0a729a6
ce734ca
9e0e903
 
 
 
 
 
 
0a729a6
 
 
9e0e903
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0a729a6
9e0e903
 
 
 
0a729a6
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
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

# 1. VARIABLES DE ENTORNO Y CACHÉ
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)

# 2. CONFIGURACIÓN DE RUTAS
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

# 3. PROMPTS
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"]
)

# 4. MODELOS
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)
    
    # Asegúrate de que la Key esté en los Secrets de Hugging Face
    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
    )

# 5. INICIALIZACIÓN DE API
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:
        # Convertir historial a texto
        chat_str = "\n".join([f"U: {m[0]}\nB: {m[1]}" for m in request.history])

        # Reescritura de pregunta
        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()

        # Intención
        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:
            # RAG
            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:
        # Aquí estaba tu error. Asegúrate de que esta línea esté 
        # alineada exactamente con el 'try:' de arriba.
        return {"error": str(e)}
    except Exception as e:
        return {"error": f"Error al procesar la consulta: {e}"}