Antoni341 commited on
Commit
01fe139
·
verified ·
1 Parent(s): b93a0e4

Upload 2 files

Browse files
Files changed (2) hide show
  1. app.py +187 -0
  2. requirements.txt +219 -0
app.py ADDED
@@ -0,0 +1,187 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import gradio as gr
3
+ from langchain_openai import ChatOpenAI
4
+ from langchain_community.vectorstores import Qdrant
5
+ from langchain_community.embeddings import HuggingFaceEmbeddings
6
+ from qdrant_client import QdrantClient, models
7
+ from qdrant_client.http import models as rest_models
8
+ from langchain.chains import create_history_aware_retriever, create_retrieval_chain
9
+ 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",
26
+ "Manuales Técnicos y Procedimientos",
27
+ "Inventarios y Activos SUMA",
28
+ "Otros"
29
+ ]
30
+
31
+ # --- 2. INICIALIZAR CLIENTES ---
32
+
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,
55
+ embeddings=embeddings_model,
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 \
63
+ que pueda entenderse sin el historial de chat. NO respondas a la pregunta, \
64
+ solo reformúlala si es necesario y, si no, devuélvela tal cual."""
65
+
66
+ contextualize_q_prompt = ChatPromptTemplate.from_messages(
67
+ [
68
+ ("system", contextualize_q_system_prompt),
69
+ MessagesPlaceholder("chat_history"),
70
+ ("human", "{input}"),
71
+ ]
72
+ )
73
+
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:
81
+ {context}"""
82
+
83
+ qa_prompt = ChatPromptTemplate.from_messages(
84
+ [
85
+ ("system", qa_system_prompt),
86
+ MessagesPlaceholder("chat_history"),
87
+ ("human", "{input}"),
88
+ ]
89
+ )
90
+
91
+ # --- 4. GESTIÓN DE MEMORIA ---
92
+ store = {}
93
+
94
+ def get_session_history(session_id: str):
95
+ if session_id not in store:
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":
103
+ return None
104
+ return rest_models.Filter(
105
+ must=[
106
+ rest_models.FieldCondition(
107
+ key="category",
108
+ match=rest_models.MatchValue(value=category_name)
109
+ )
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
+
120
+ # 2. Retriever dinámico
121
+ dynamic_retriever = vectordb.as_retriever(
122
+ search_kwargs={
123
+ "k": 4,
124
+ "filter": qdrant_filter
125
+ }
126
+ )
127
+
128
+ # 3. Cadenas
129
+ history_aware_retriever = create_history_aware_retriever(
130
+ llm_openai, dynamic_retriever, contextualize_q_prompt
131
+ )
132
+ question_answer_chain = create_stuff_documents_chain(llm_openai, qa_prompt)
133
+ rag_chain = create_retrieval_chain(history_aware_retriever, question_answer_chain)
134
+
135
+ conversational_rag_chain = RunnableWithMessageHistory(
136
+ rag_chain,
137
+ get_session_history,
138
+ input_messages_key="input",
139
+ history_messages_key="chat_history",
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},
147
+ config={"configurable": {"session_id": session_id}}
148
+ ):
149
+ if "answer" in chunk:
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}
159
+ """
160
+
161
+ 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",
172
+ label="📂 Filtrar por Categoría",
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"],
183
+ ]
184
+ )
185
+
186
+ if __name__ == "__main__":
187
+ demo.launch()
requirements.txt ADDED
@@ -0,0 +1,219 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ accelerate==1.10.1
2
+ aiofiles==24.1.0
3
+ aiohappyeyeballs==2.6.1
4
+ aiohttp==3.12.15
5
+ aiosignal==1.4.0
6
+ annotated-types==0.7.0
7
+ anyio==4.10.0
8
+ asttokens @ file:///home/conda/feedstock_root/build_artifacts/asttokens_1733250440834/work
9
+ attrs==25.3.0
10
+ backoff==2.2.1
11
+ beautifulsoup4==4.13.5
12
+ Brotli==1.1.0
13
+ certifi==2025.8.3
14
+ cffi==2.0.0
15
+ charset-normalizer==3.4.3
16
+ click==8.3.0
17
+ coloredlogs==15.0.1
18
+ comm @ file:///home/conda/feedstock_root/build_artifacts/bld/rattler-build_comm_1753453984/work
19
+ contourpy==1.3.3
20
+ cryptography==46.0.1
21
+ cycler==0.12.1
22
+ dataclasses-json==0.6.7
23
+ debugpy @ file:///home/task_175706710974480/conda-bld/debugpy_1757067125926/work
24
+ decorator @ file:///home/conda/feedstock_root/build_artifacts/decorator_1740384970518/work
25
+ distro==1.9.0
26
+ emoji==2.15.0
27
+ exceptiongroup @ file:///home/conda/feedstock_root/build_artifacts/exceptiongroup_1746947292760/work
28
+ executing @ file:///home/conda/feedstock_root/build_artifacts/executing_1756729339227/work
29
+ fastapi==0.117.1
30
+ fastembed==0.7.3
31
+ ffmpy==0.6.1
32
+ filelock==3.19.1
33
+ filetype==1.2.0
34
+ flatbuffers==25.9.23
35
+ fonttools==4.60.0
36
+ frozenlist==1.7.0
37
+ fsspec==2025.9.0
38
+ gradio==5.46.1
39
+ gradio_client==1.13.1
40
+ greenlet==3.2.4
41
+ groovy==0.1.2
42
+ grpcio==1.75.0
43
+ h11==0.16.0
44
+ h2==4.3.0
45
+ hf-xet==1.1.10
46
+ hpack==4.1.0
47
+ html5lib==1.1
48
+ httpcore==1.0.9
49
+ httpx==0.28.1
50
+ httpx-sse==0.4.1
51
+ huggingface-hub==0.35.0
52
+ humanfriendly==10.0
53
+ hyperframe==6.1.0
54
+ idna==3.10
55
+ importlib_metadata @ file:///home/conda/feedstock_root/build_artifacts/bld/rattler-build_importlib-metadata_1747934053/work
56
+ ipykernel @ file:///home/conda/feedstock_root/build_artifacts/ipykernel_1754352855579/work
57
+ ipython @ file:///home/conda/feedstock_root/build_artifacts/bld/rattler-build_ipython_1756474504/work
58
+ ipython_pygments_lexers @ file:///home/conda/feedstock_root/build_artifacts/ipython_pygments_lexers_1737123620466/work
59
+ ipywidgets==8.1.7
60
+ jedi @ file:///home/conda/feedstock_root/build_artifacts/jedi_1733300866624/work
61
+ Jinja2==3.1.6
62
+ jiter==0.11.0
63
+ joblib==1.5.2
64
+ jpype1==1.6.0
65
+ jsonpatch==1.33
66
+ jsonpointer==3.0.0
67
+ jupyter_client @ file:///home/conda/feedstock_root/build_artifacts/jupyter_client_1733440914442/work
68
+ jupyter_core @ file:///home/conda/feedstock_root/build_artifacts/jupyter_core_1748333051527/work
69
+ jupyterlab_widgets==3.0.15
70
+ kiwisolver==1.4.9
71
+ langchain==0.3.27
72
+ langchain-community==0.3.29
73
+ langchain-core==0.3.76
74
+ langchain-experimental==0.3.4
75
+ langchain-huggingface==0.3.1
76
+ langchain-ollama==0.3.8
77
+ langchain-openai==0.3.33
78
+ langchain-qdrant==0.2.1
79
+ langchain-text-splitters==0.3.11
80
+ langdetect==1.0.9
81
+ langsmith==0.4.28
82
+ loguru==0.7.3
83
+ lxml==6.0.2
84
+ Markdown==3.9
85
+ markdown-it-py==4.0.0
86
+ MarkupSafe==3.0.2
87
+ marshmallow==3.26.1
88
+ matplotlib==3.10.6
89
+ matplotlib-inline @ file:///home/conda/feedstock_root/build_artifacts/matplotlib-inline_1733416936468/work
90
+ mdurl==0.1.2
91
+ ml_dtypes==0.5.3
92
+ mmh3==5.2.0
93
+ mpmath==1.3.0
94
+ multidict==6.6.4
95
+ mypy_extensions==1.1.0
96
+ nest_asyncio @ file:///home/conda/feedstock_root/build_artifacts/nest-asyncio_1733325553580/work
97
+ networkx==3.5
98
+ nltk==3.9.2
99
+ numpy==2.2.6
100
+ nvidia-cublas-cu12==12.8.4.1
101
+ nvidia-cuda-cupti-cu12==12.8.90
102
+ nvidia-cuda-nvrtc-cu12==12.8.93
103
+ nvidia-cuda-runtime-cu12==12.8.90
104
+ nvidia-cudnn-cu12==9.10.2.21
105
+ nvidia-cufft-cu12==11.3.3.83
106
+ nvidia-cufile-cu12==1.13.1.3
107
+ nvidia-curand-cu12==10.3.9.90
108
+ nvidia-cusolver-cu12==11.7.3.90
109
+ nvidia-cusparse-cu12==12.5.8.93
110
+ nvidia-cusparselt-cu12==0.7.1
111
+ nvidia-nccl-cu12==2.27.3
112
+ nvidia-nvjitlink-cu12==12.8.93
113
+ nvidia-nvtx-cu12==12.8.90
114
+ olefile==0.47
115
+ ollama==0.5.4
116
+ onnx==1.19.0
117
+ onnxruntime==1.22.1
118
+ openai==1.108.0
119
+ opencv-python==4.12.0.88
120
+ orjson==3.11.3
121
+ packaging @ file:///home/conda/feedstock_root/build_artifacts/bld/rattler-build_packaging_1745345660/work
122
+ pandas==2.3.2
123
+ parso @ file:///home/conda/feedstock_root/build_artifacts/bld/rattler-build_parso_1755974222/work
124
+ pdf2image==1.17.0
125
+ pdfminer==20191125
126
+ pdfminer.six==20250506
127
+ pexpect @ file:///home/conda/feedstock_root/build_artifacts/pexpect_1733301927746/work
128
+ pi_heif==1.1.0
129
+ pickleshare @ file:///home/conda/feedstock_root/build_artifacts/pickleshare_1733327343728/work
130
+ pillow==11.3.0
131
+ platformdirs @ file:///home/conda/feedstock_root/build_artifacts/bld/rattler-build_platformdirs_1756227402/work
132
+ portalocker==3.2.0
133
+ prompt_toolkit @ file:///home/conda/feedstock_root/build_artifacts/prompt-toolkit_1756321756983/work
134
+ propcache==0.3.2
135
+ protobuf==6.32.1
136
+ psutil @ file:///home/task_175710406524895/conda-bld/psutil_1757104541967/work
137
+ ptyprocess @ file:///home/conda/feedstock_root/build_artifacts/ptyprocess_1733302279685/work/dist/ptyprocess-0.7.0-py2.py3-none-any.whl#sha256=92c32ff62b5fd8cf325bec5ab90d7be3d2a8ca8c8a3813ff487a8d2002630d1f
138
+ pure_eval @ file:///home/conda/feedstock_root/build_artifacts/pure_eval_1733569405015/work
139
+ py_rust_stemmers==0.1.5
140
+ pycparser==2.23
141
+ pycryptodome==3.23.0
142
+ pydantic==2.11.9
143
+ pydantic-settings==2.10.1
144
+ pydantic_core==2.33.2
145
+ pydub==0.25.1
146
+ Pygments @ file:///home/conda/feedstock_root/build_artifacts/pygments_1750615794071/work
147
+ PyMuPDF==1.26.4
148
+ pyparsing==3.2.5
149
+ pypdf==6.0.0
150
+ pypdfium2==4.30.0
151
+ pytesseract==0.3.13
152
+ python-dateutil @ file:///home/conda/feedstock_root/build_artifacts/bld/rattler-build_python-dateutil_1751104122/work
153
+ python-dotenv==1.1.1
154
+ python-iso639==2025.2.18
155
+ python-magic==0.4.27
156
+ python-multipart==0.0.20
157
+ python-oxmsg==0.0.2
158
+ pytz==2025.2
159
+ PyYAML==6.0.2
160
+ pyzmq @ file:///home/task_175810350411579/conda-bld/pyzmq_1758103935618/work
161
+ qdrant-client==1.15.1
162
+ rake-nltk==1.0.6
163
+ rank-bm25==0.2.2
164
+ RapidFuzz==3.14.1
165
+ regex==2025.9.1
166
+ requests==2.32.5
167
+ requests-toolbelt==1.0.0
168
+ rich==14.1.0
169
+ ruff==0.13.1
170
+ safehttpx==0.1.6
171
+ safetensors==0.6.2
172
+ scikit-learn==1.7.2
173
+ scipy==1.16.2
174
+ semantic-version==2.10.0
175
+ sentence-transformers==5.1.0
176
+ setuptools==78.1.1
177
+ shellingham==1.5.4
178
+ six @ file:///home/conda/feedstock_root/build_artifacts/bld/rattler-build_six_1753199211/work
179
+ sniffio==1.3.1
180
+ soupsieve==2.8
181
+ SQLAlchemy==2.0.43
182
+ stack_data @ file:///home/conda/feedstock_root/build_artifacts/stack_data_1733569443808/work
183
+ starlette==0.48.0
184
+ sympy==1.14.0
185
+ tabula-py==2.10.0
186
+ tabulate==0.9.0
187
+ tenacity==9.1.2
188
+ threadpoolctl==3.6.0
189
+ tiktoken==0.11.0
190
+ timm==1.0.20
191
+ tokenizers==0.22.0
192
+ tomlkit==0.13.3
193
+ torch==2.8.0
194
+ torchvision==0.23.0
195
+ tornado @ file:///croot/tornado_1748956929273/work
196
+ tqdm==4.67.1
197
+ traitlets @ file:///home/conda/feedstock_root/build_artifacts/traitlets_1733367359838/work
198
+ transformers==4.56.1
199
+ triton==3.4.0
200
+ typer==0.19.1
201
+ typing-inspect==0.9.0
202
+ typing-inspection==0.4.1
203
+ typing_extensions @ file:///home/conda/feedstock_root/build_artifacts/bld/rattler-build_typing_extensions_1756220668/work
204
+ tzdata==2025.2
205
+ unstructured==0.18.15
206
+ unstructured-client==0.42.3
207
+ unstructured-inference==1.0.5
208
+ unstructured.pytesseract==0.3.15
209
+ urllib3==2.5.0
210
+ uvicorn==0.36.0
211
+ wcwidth @ file:///home/conda/feedstock_root/build_artifacts/wcwidth_1733231326287/work
212
+ webencodings==0.5.1
213
+ websockets==15.0.1
214
+ wheel==0.45.1
215
+ widgetsnbextension==4.0.14
216
+ wrapt==1.17.3
217
+ yarl==1.20.1
218
+ zipp @ file:///home/conda/feedstock_root/build_artifacts/zipp_1749421620841/work
219
+ zstandard==0.25.0