jimytech commited on
Commit
9e0e903
·
verified ·
1 Parent(s): ce734ca

Update rag_api.py

Browse files
Files changed (1) hide show
  1. rag_api.py +71 -81
rag_api.py CHANGED
@@ -9,77 +9,61 @@ from langchain_core.runnables import RunnablePassthrough
9
  from langchain_core.prompts import PromptTemplate
10
  from langchain_groq import ChatGroq
11
 
12
- # --------------------------------------------------------
13
- # CACHÉ EN /tmp
14
- # --------------------------------------------------------
15
  TEMP_CACHE_DIR = '/tmp/huggingface_cache'
16
  os.environ['TRANSFORMERS_CACHE'] = TEMP_CACHE_DIR
17
  os.environ['HF_HOME'] = TEMP_CACHE_DIR
18
  os.environ['SENTENCE_TRANSFORMERS_HOME'] = TEMP_CACHE_DIR
19
  os.makedirs(TEMP_CACHE_DIR, exist_ok=True)
20
 
21
- # --------------------------------------------------------
22
- # 1. CONFIGURACIÓN Y PROMPTS
23
- # --------------------------------------------------------
24
  URL_FAISS = "https://drive.google.com/uc?export=download&id=1hiVycS4DQHO1MBdC-L_z1TXA6sJO_Y-r"
25
  URL_PKL = "https://drive.google.com/uc?export=download&id=1vbG8unx88Kb5jn7puGv1gqSM4S6rIUQC"
26
  DOWNLOAD_DIR = "/tmp/db_faiss"
27
  DB_FAISS_PATH = DOWNLOAD_DIR
28
 
29
- # --- NUEVO: PROMPT PARA RE-ESCRIBIR LA PREGUNTA ---
30
  CONDENSE_PROMPT = PromptTemplate(
31
- template="""Dada la siguiente conversación y una pregunta de seguimiento, reescribe la pregunta de seguimiento para que sea una pregunta independiente que contenga todo el contexto, especialmente si se refiere a la UPT Aragua.
32
-
33
- Historial:
34
- {chat_history}
35
- Pregunta de seguimiento: {question}
36
- Pregunta independiente reescrita:""",
37
  input_variables=["chat_history", "question"]
38
  )
39
 
40
  INTENT_PROMPT = PromptTemplate(
41
- template="""Eres un clasificador de intenciones para la UPT Aragua. Clasifica en: SALUDO, UNIVERSIDAD u OTRO.
42
- Responde SOLO con la categoría.
43
- Mensaje: {query}
44
- Categoría:""",
45
  input_variables=["query"]
46
  )
47
 
48
  SALUDO_PROMPT = PromptTemplate(
49
- template="""Eres UPTA bot, saluda amigablemente y menciona que puedes ayudar con info de la UPT Aragua.
50
- Mensaje: {query}
51
- Respuesta:""",
52
  input_variables=["query"]
53
  )
54
 
55
  RAG_PROMPT = PromptTemplate(
56
- template="""Eres UPTA bot, experto de la UPT Aragua. Responde usando el contexto. Si no lo sabes, pide ser más específico.
57
  Contexto: {context}
58
  Pregunta: {question}
59
  Respuesta:""",
60
  input_variables=["context", "question"]
61
  )
62
 
63
- # --------------------------------------------------------
64
- # 2. MODELOS DE DATOS
65
- # --------------------------------------------------------
66
  class QueryRequest(BaseModel):
67
  query: str
68
- history: list = [] # Aquí recibiremos el historial desde Gradio
69
 
70
- # --------------------------------------------------------
71
- # 3. FUNCIONES DE CARGA
72
- # --------------------------------------------------------
73
  def download_file(url, local_path):
74
  headers = {'User-Agent': 'Mozilla/5.0'}
75
- response = requests.get(url, stream=True, headers=headers, timeout=30)
76
  os.makedirs(os.path.dirname(local_path), exist_ok=True)
77
- with open(local_path, 'wb') as f:
78
- shutil.copyfileobj(response.raw, f)
 
79
 
80
- def load_and_configure_rag():
81
  download_file(URL_FAISS, os.path.join(DOWNLOAD_DIR, 'index.faiss'))
82
- download_file(URL_PKL, os.path.join(DOWNLOAD_DIR, 'index.pkl'))
83
 
84
  embeddings = HuggingFaceEmbeddings(
85
  model_name="sentence-transformers/all-MiniLM-L6-v2",
@@ -88,64 +72,70 @@ def load_and_configure_rag():
88
  )
89
  vectorstore = FAISS.load_local(DB_FAISS_PATH, embeddings, allow_dangerous_deserialization=True)
90
 
91
- # Asegúrate de tener la variable de entorno GROQ_API_KEY configurada en Hugging Face
92
- llm = ChatGroq(temperature=0.15, model_name="openai/gpt-oss-120b")
93
 
94
- retriever = vectorstore.as_retriever(search_kwargs={"k": 4})
95
 
96
- # Creamos todas las cadenas
97
- condense_chain = CONDENSE_PROMPT | llm
98
- intent_chain = INTENT_PROMPT | llm
99
- saludo_chain = SALUDO_PROMPT | llm
100
- rag_chain = (
101
- {"context": retriever, "question": RunnablePassthrough()}
102
- | RAG_PROMPT
103
- | llm
104
  )
105
-
106
- return condense_chain, intent_chain, saludo_chain, rag_chain, retriever
107
 
108
- # --------------------------------------------------------
109
- # 4. API FASTAPI
110
- # --------------------------------------------------------
111
  app = FastAPI()
112
- condense_chain = intent_chain = saludo_chain = rag_chain = retriever = None
113
 
114
  @app.on_event("startup")
115
- async def startup_event():
116
- global condense_chain, intent_chain, saludo_chain, rag_chain, retriever
117
- condense_chain, intent_chain, saludo_chain, rag_chain, retriever = load_and_configure_rag()
 
 
 
 
118
 
119
  @app.post("/query")
120
  async def process_query(request: QueryRequest):
121
- # 1. Convertir historial a texto
122
- chat_str = ""
123
- for user_msg, bot_msg in request.history:
124
- chat_str += f"Usuario: {user_msg}\nBot: {bot_msg}\n"
125
-
126
- # 2. Re-escribir consulta si hay historial
127
- query_to_process = request.query
128
- if request.history:
129
- res = condense_chain.invoke({"chat_history": chat_str, "question": request.query})
130
- query_to_process = res.content.strip()
131
-
132
- # 3. Clasificar intención
133
- intent_res = intent_chain.invoke({"query": query_to_process})
134
- intent = intent_res.content.upper()
135
-
136
- if "SALUDO" in intent:
137
- resp = saludo_chain.invoke({"query": request.query})
138
- return {"response": resp.content, "intent": "SALUDO"}
139
-
140
- elif "OTRO" in intent:
141
- return {"response": "Solo puedo ayudarte con temas de la UPT Aragua.", "intent": "OTRO"}
142
-
143
- else:
144
- # RAG con la consulta re-escrita
145
- resp = rag_chain.invoke(query_to_process)
146
- docs = retriever.invoke(query_to_process)
147
- sources = list(set([doc.metadata.get("source", "N/A") for doc in docs]))
148
- return {"response": resp.content, "intent": "UNIVERSIDAD", "sources": sources}
 
 
 
 
149
 
 
 
 
 
150
  except Exception as e:
151
  return {"error": f"Error al procesar la consulta: {e}"}
 
9
  from langchain_core.prompts import PromptTemplate
10
  from langchain_groq import ChatGroq
11
 
12
+ # 1. VARIABLES DE ENTORNO Y CACHÉ
 
 
13
  TEMP_CACHE_DIR = '/tmp/huggingface_cache'
14
  os.environ['TRANSFORMERS_CACHE'] = TEMP_CACHE_DIR
15
  os.environ['HF_HOME'] = TEMP_CACHE_DIR
16
  os.environ['SENTENCE_TRANSFORMERS_HOME'] = TEMP_CACHE_DIR
17
  os.makedirs(TEMP_CACHE_DIR, exist_ok=True)
18
 
19
+ # 2. CONFIGURACIÓN DE RUTAS
 
 
20
  URL_FAISS = "https://drive.google.com/uc?export=download&id=1hiVycS4DQHO1MBdC-L_z1TXA6sJO_Y-r"
21
  URL_PKL = "https://drive.google.com/uc?export=download&id=1vbG8unx88Kb5jn7puGv1gqSM4S6rIUQC"
22
  DOWNLOAD_DIR = "/tmp/db_faiss"
23
  DB_FAISS_PATH = DOWNLOAD_DIR
24
 
25
+ # 3. PROMPTS
26
  CONDENSE_PROMPT = PromptTemplate(
27
+ template="""Dada la conversación y la pregunta, reescríbela para que sea independiente y clara sobre la UPT Aragua.
28
+ Historial: {chat_history}
29
+ Pregunta: {question}
30
+ Pregunta reescrita:""",
 
 
31
  input_variables=["chat_history", "question"]
32
  )
33
 
34
  INTENT_PROMPT = PromptTemplate(
35
+ template="Categoriza el mensaje en: SALUDO, UNIVERSIDAD u OTRO. Responde solo la palabra. Mensaje: {query}",
 
 
 
36
  input_variables=["query"]
37
  )
38
 
39
  SALUDO_PROMPT = PromptTemplate(
40
+ template="Eres UPTA bot. Saluda cordialmente. Mensaje: {query}",
 
 
41
  input_variables=["query"]
42
  )
43
 
44
  RAG_PROMPT = PromptTemplate(
45
+ template="""Eres UPTA bot. Responde usando solo el contexto.
46
  Contexto: {context}
47
  Pregunta: {question}
48
  Respuesta:""",
49
  input_variables=["context", "question"]
50
  )
51
 
52
+ # 4. MODELOS
 
 
53
  class QueryRequest(BaseModel):
54
  query: str
55
+ history: list = []
56
 
 
 
 
57
  def download_file(url, local_path):
58
  headers = {'User-Agent': 'Mozilla/5.0'}
 
59
  os.makedirs(os.path.dirname(local_path), exist_ok=True)
60
+ with requests.get(url, stream=True, headers=headers, timeout=30) as r:
61
+ with open(local_path, 'wb') as f:
62
+ shutil.copyfileobj(r.raw, f)
63
 
64
+ def load_rag():
65
  download_file(URL_FAISS, os.path.join(DOWNLOAD_DIR, 'index.faiss'))
66
+ download_file(URL_PKL, os.path.join(DOWNLOAD_DIR, 'index.pkl'))
67
 
68
  embeddings = HuggingFaceEmbeddings(
69
  model_name="sentence-transformers/all-MiniLM-L6-v2",
 
72
  )
73
  vectorstore = FAISS.load_local(DB_FAISS_PATH, embeddings, allow_dangerous_deserialization=True)
74
 
75
+ # Asegúrate de que la Key esté en los Secrets de Hugging Face
76
+ llm = ChatGroq(temperature=0.1, model_name="openai/gpt-oss-120b")
77
 
78
+ retriever = vectorstore.as_retriever(search_kwargs={"k": 3})
79
 
80
+ return (
81
+ CONDENSE_PROMPT | llm,
82
+ INTENT_PROMPT | llm,
83
+ SALUDO_PROMPT | llm,
84
+ ( {"context": retriever, "question": RunnablePassthrough()} | RAG_PROMPT | llm ),
85
+ retriever
 
 
86
  )
 
 
87
 
88
+ # 5. INICIALIZACIÓN DE API
 
 
89
  app = FastAPI()
90
+ condense_c, intent_c, saludo_c, rag_c, retriever = (None, None, None, None, None)
91
 
92
  @app.on_event("startup")
93
+ async def startup():
94
+ global condense_c, intent_c, saludo_c, rag_c, retriever
95
+ condense_c, intent_c, saludo_c, rag_c, retriever = load_rag()
96
+
97
+ @app.get("/")
98
+ def root():
99
+ return {"status": "ready"}
100
 
101
  @app.post("/query")
102
  async def process_query(request: QueryRequest):
103
+ try:
104
+ # Convertir historial a texto
105
+ chat_str = "\n".join([f"U: {m[0]}\nB: {m[1]}" for m in request.history])
106
+
107
+ # Reescritura de pregunta
108
+ q_final = request.query
109
+ if request.history:
110
+ res_c = condense_c.invoke({"chat_history": chat_str, "question": request.query})
111
+ q_final = res_c.content.strip()
112
+
113
+ # Intención
114
+ res_i = intent_c.invoke({"query": q_final})
115
+ intent = res_i.content.upper()
116
+
117
+ if "SALUDO" in intent:
118
+ res_s = saludo_c.invoke({"query": request.query})
119
+ return {"response": res_s.content, "intent": "SALUDO"}
120
+
121
+ elif "OTRO" in intent:
122
+ return {"response": "Solo respondo sobre la UPT Aragua.", "intent": "OTRO"}
123
+
124
+ else:
125
+ # RAG
126
+ res_r = rag_c.invoke(q_final)
127
+ docs = retriever.invoke(q_final)
128
+ sources = list(set([d.metadata.get("source", "N/A") for d in docs]))
129
+ return {
130
+ "response": res_r.content,
131
+ "intent": "UNIVERSIDAD",
132
+ "sources": sources,
133
+ "contextual_query": q_final
134
+ }
135
 
136
+ except Exception as e:
137
+ # Aquí estaba tu error. Asegúrate de que esta línea esté
138
+ # alineada exactamente con el 'try:' de arriba.
139
+ return {"error": str(e)}
140
  except Exception as e:
141
  return {"error": f"Error al procesar la consulta: {e}"}