Antoni341 commited on
Commit
4145718
·
verified ·
1 Parent(s): 50c0cd1

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +24 -24
app.py CHANGED
@@ -1,4 +1,5 @@
1
  import os
 
2
  import gradio as gr
3
  from langchain_openai import ChatOpenAI
4
  from langchain_community.vectorstores import Qdrant
@@ -10,16 +11,14 @@ from langchain.chains.combine_documents import create_stuff_documents_chain
10
  from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
11
  from langchain_community.chat_message_histories import ChatMessageHistory
12
  from langchain_core.runnables.history import RunnableWithMessageHistory
13
- from dotenv import load_dotenv
14
- load_dotenv()
15
-
16
 
 
17
  QDRANT_URL = os.getenv("QDRANT_URL")
18
  QDRANT_API_KEY = os.getenv("QDRANT_API_KEY")
19
  OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
20
  COLLECTION_NAME = "dgt_documents_qdrant_memory_filter_fixed_2"
21
 
22
-
23
  OPCIONES_CATEGORIAS = [
24
  "Todas",
25
  "Documentos de la SUMA",
@@ -33,22 +32,21 @@ OPCIONES_CATEGORIAS = [
33
  # Cliente Qdrant
34
  client = QdrantClient(url=QDRANT_URL, api_key=QDRANT_API_KEY)
35
 
36
- # Embeddings (Mismo modelo que usaste para subir los datos)
37
- # IMPORTANTE: device='cpu' para que funcione en el plan gratuito de HF
38
  embeddings_model = HuggingFaceEmbeddings(
39
  model_name="intfloat/e5-large-v2",
40
  model_kwargs={'device': 'cpu'},
41
  encode_kwargs={'normalize_embeddings': False}
42
  )
43
 
44
- # LLM (Modelo de chat)
45
  llm_openai = ChatOpenAI(
46
- model="gpt-4o-mini", # Corregido a un modelo válido y económico
47
  temperature=0.1,
48
  api_key=OPENAI_API_KEY
49
  )
50
 
51
- # Conexión a la VectorDB (Solo lectura)
52
  vectordb = Qdrant(
53
  client=client,
54
  collection_name=COLLECTION_NAME,
@@ -56,7 +54,7 @@ vectordb = Qdrant(
56
  content_payload_key="content"
57
  )
58
 
59
- # --- 3. PROMPTS Y CADENAS ---
60
 
61
  contextualize_q_system_prompt = """Dado un historial de chat y la última pregunta del usuario \
62
  que podría hacer referencia al contexto en el historial de chat, formula una pregunta independiente \
@@ -74,7 +72,7 @@ contextualize_q_prompt = ChatPromptTemplate.from_messages(
74
  qa_system_prompt = """Eres un asistente especializado en los documentos sobre la Sociedad Musical de Alberic (SUMA). \
75
  Utiliza los siguientes fragmentos de contexto recuperado para responder a la pregunta. \
76
  Si no sabes la respuesta, di que no lo sabes. \
77
- Menciona siempre de qué documentos has extraído la información (usando el metadato 'source' si es posible). \
78
  Profundiza en la respuesta.
79
 
80
  Contexto:
@@ -88,7 +86,7 @@ qa_prompt = ChatPromptTemplate.from_messages(
88
  ]
89
  )
90
 
91
- # --- 4. GESTIÓN DE MEMORIA ---
92
  store = {}
93
 
94
  def get_session_history(session_id: str):
@@ -96,7 +94,7 @@ def get_session_history(session_id: str):
96
  store[session_id] = ChatMessageHistory()
97
  return store[session_id]
98
 
99
- # --- 5. LÓGICA DEL CHAT CON FILTROS ---
100
 
101
  def build_qdrant_filter(category_name):
102
  if not category_name or category_name == "Todas":
@@ -110,10 +108,11 @@ def build_qdrant_filter(category_name):
110
  ]
111
  )
112
 
113
- def chat_logic(message, history, selected_category):
114
- # Identificador de sesión (en demo simple usamos uno fijo o aleatorio simple)
115
- session_id = "usuario_web"
116
-
 
117
  # 1. Construir filtro
118
  qdrant_filter = build_qdrant_filter(selected_category)
119
 
@@ -125,7 +124,7 @@ def chat_logic(message, history, selected_category):
125
  }
126
  )
127
 
128
- # 3. Cadenas
129
  history_aware_retriever = create_history_aware_retriever(
130
  llm_openai, dynamic_retriever, contextualize_q_prompt
131
  )
@@ -140,7 +139,7 @@ def chat_logic(message, history, selected_category):
140
  output_messages_key="answer",
141
  )
142
 
143
- # 4. Generar respuesta (Streaming)
144
  full_response = ""
145
  for chunk in conversational_rag_chain.stream(
146
  {"input": message},
@@ -150,9 +149,8 @@ def chat_logic(message, history, selected_category):
150
  full_response += chunk["answer"]
151
  yield full_response
152
 
153
- # --- 6. INTERFAZ GRADIO ---
154
 
155
- # CSS personalizado para ocultar el footer y ajustar estilo
156
  custom_css = """
157
  footer {visibility: hidden}
158
  .gradio-container {background-color: #f9fafb}
@@ -162,10 +160,12 @@ tema_musical = gr.themes.Soft(primary_hue="indigo", secondary_hue="slate")
162
 
163
  with gr.Blocks(theme=tema_musical, css=custom_css, title="Chatbot SUMA") as demo:
164
 
 
 
 
165
  gr.Markdown("# 🎵 Asistente Virtual SUMA")
166
  gr.Markdown("Pregunta sobre normativas, manuales y documentos internos.")
167
 
168
- # Dropdown de filtro
169
  filtro_dropdown = gr.Dropdown(
170
  choices=OPCIONES_CATEGORIAS,
171
  value="Todas",
@@ -173,10 +173,10 @@ with gr.Blocks(theme=tema_musical, css=custom_css, title="Chatbot SUMA") as demo
173
  info="Acota la búsqueda a un tipo de documento específico."
174
  )
175
 
176
- # Chat Interface
177
  chat_interface = gr.ChatInterface(
178
  fn=chat_logic,
179
- additional_inputs=[filtro_dropdown],
 
180
  examples=[
181
  ["¿Cuáles son los requisitos para ser socio?"],
182
  ["Resumen del manual de procedimientos"],
 
1
  import os
2
+ import uuid
3
  import gradio as gr
4
  from langchain_openai import ChatOpenAI
5
  from langchain_community.vectorstores import Qdrant
 
11
  from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
12
  from langchain_community.chat_message_histories import ChatMessageHistory
13
  from langchain_core.runnables.history import RunnableWithMessageHistory
 
 
 
14
 
15
+ # --- 1. CONFIGURACIÓN Y VARIABLES DE ENTORNO ---
16
  QDRANT_URL = os.getenv("QDRANT_URL")
17
  QDRANT_API_KEY = os.getenv("QDRANT_API_KEY")
18
  OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
19
  COLLECTION_NAME = "dgt_documents_qdrant_memory_filter_fixed_2"
20
 
21
+ # Categorías disponibles
22
  OPCIONES_CATEGORIAS = [
23
  "Todas",
24
  "Documentos de la SUMA",
 
32
  # Cliente Qdrant
33
  client = QdrantClient(url=QDRANT_URL, api_key=QDRANT_API_KEY)
34
 
35
+ # Embeddings (CPU para Hugging Face Free Tier)
 
36
  embeddings_model = HuggingFaceEmbeddings(
37
  model_name="intfloat/e5-large-v2",
38
  model_kwargs={'device': 'cpu'},
39
  encode_kwargs={'normalize_embeddings': False}
40
  )
41
 
42
+ # LLM
43
  llm_openai = ChatOpenAI(
44
+ model="gpt-4o-mini",
45
  temperature=0.1,
46
  api_key=OPENAI_API_KEY
47
  )
48
 
49
+ # Conexión a la VectorDB
50
  vectordb = Qdrant(
51
  client=client,
52
  collection_name=COLLECTION_NAME,
 
54
  content_payload_key="content"
55
  )
56
 
57
+ # --- 3. PROMPTS ---
58
 
59
  contextualize_q_system_prompt = """Dado un historial de chat y la última pregunta del usuario \
60
  que podría hacer referencia al contexto en el historial de chat, formula una pregunta independiente \
 
72
  qa_system_prompt = """Eres un asistente especializado en los documentos sobre la Sociedad Musical de Alberic (SUMA). \
73
  Utiliza los siguientes fragmentos de contexto recuperado para responder a la pregunta. \
74
  Si no sabes la respuesta, di que no lo sabes. \
75
+ Menciona siempre de qué documentos has extraído la información (usando el metadato 'source'). \
76
  Profundiza en la respuesta.
77
 
78
  Contexto:
 
86
  ]
87
  )
88
 
89
+ # --- 4. GESTIÓN DE MEMORIA EN RAM ---
90
  store = {}
91
 
92
  def get_session_history(session_id: str):
 
94
  store[session_id] = ChatMessageHistory()
95
  return store[session_id]
96
 
97
+ # --- 5. LÓGICA DEL CHAT ---
98
 
99
  def build_qdrant_filter(category_name):
100
  if not category_name or category_name == "Todas":
 
108
  ]
109
  )
110
 
111
+ def chat_logic(message, history, selected_category, session_id):
112
+ # Seguridad: Si por error session_id viene vacío, generamos uno temporal
113
+ if not session_id:
114
+ session_id = str(uuid.uuid4())
115
+
116
  # 1. Construir filtro
117
  qdrant_filter = build_qdrant_filter(selected_category)
118
 
 
124
  }
125
  )
126
 
127
+ # 3. Cadenas LangChain
128
  history_aware_retriever = create_history_aware_retriever(
129
  llm_openai, dynamic_retriever, contextualize_q_prompt
130
  )
 
139
  output_messages_key="answer",
140
  )
141
 
142
+ # 4. Generar respuesta streaming usando el ID único del usuario
143
  full_response = ""
144
  for chunk in conversational_rag_chain.stream(
145
  {"input": message},
 
149
  full_response += chunk["answer"]
150
  yield full_response
151
 
152
+ # --- 6. INTERFAZ GRÁFICA ---
153
 
 
154
  custom_css = """
155
  footer {visibility: hidden}
156
  .gradio-container {background-color: #f9fafb}
 
160
 
161
  with gr.Blocks(theme=tema_musical, css=custom_css, title="Chatbot SUMA") as demo:
162
 
163
+ # ESTADO: Genera un ID único cada vez que se carga la página
164
+ session_state = gr.State(lambda: str(uuid.uuid4()))
165
+
166
  gr.Markdown("# 🎵 Asistente Virtual SUMA")
167
  gr.Markdown("Pregunta sobre normativas, manuales y documentos internos.")
168
 
 
169
  filtro_dropdown = gr.Dropdown(
170
  choices=OPCIONES_CATEGORIAS,
171
  value="Todas",
 
173
  info="Acota la búsqueda a un tipo de documento específico."
174
  )
175
 
 
176
  chat_interface = gr.ChatInterface(
177
  fn=chat_logic,
178
+ # Pasamos el session_state (el ID oculto) a la función lógica
179
+ additional_inputs=[filtro_dropdown, session_state],
180
  examples=[
181
  ["¿Cuáles son los requisitos para ser socio?"],
182
  ["Resumen del manual de procedimientos"],