JoseAntonioBarrancoBernabe commited on
Commit
0b7778d
1 Parent(s): 701b939

Primera subida ficheros de la app

Browse files
Files changed (6) hide show
  1. 11312ENERO.pdf +0 -0
  2. 11312FEBRERO.pdf +0 -0
  3. app.py +254 -0
  4. ficheros.txt +1 -0
  5. requirements.txt +8 -0
  6. utils.py +35 -0
11312ENERO.pdf ADDED
Binary file (71.2 kB). View file
 
11312FEBRERO.pdf ADDED
Binary file (71.2 kB). View file
 
app.py ADDED
@@ -0,0 +1,254 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ ##Instalación de paquetes necesarios
3
+ import streamlit as st
4
+ import os
5
+ import time
6
+ import torch
7
+ from utils import *
8
+ from dotenv import load_dotenv
9
+ load_dotenv()
10
+
11
+ ##import nest_asyncio
12
+ ##nest_asyncio.apply()
13
+ from llama_parse import LlamaParse
14
+
15
+ from llama_index.llms.openai import OpenAI
16
+ from llama_index.embeddings.openai import OpenAIEmbedding
17
+ from llama_index.core import VectorStoreIndex, ServiceContext
18
+ from llama_index.core import SimpleDirectoryReader
19
+ from llama_index.core import Settings
20
+ print(torch.cuda.is_available())
21
+ ######
22
+
23
+ ## titulos y cabeceras
24
+ st.set_page_config('compare PDF por LLM')
25
+ st.title("Comparar PDFs mediante LLM")
26
+ st.subheader("Campos a comparar en tu PDF",divider='rainbow')
27
+
28
+ #### Inicializar mensajes de chat
29
+ if "messages" not in st.session_state.keys():
30
+ st.session_state.messages = [
31
+ {"role": "assistant", "content": "Ask me a question about PDFs files you provided me"}
32
+ ]
33
+
34
+ @st.cache_resource(show_spinner=False) # Añade decorador de caché
35
+ def cargar_embedmodel_y_llmmodel():
36
+ return True
37
+
38
+ #esta variable es para tener aqui un listado de aquellos ficheros que se han ido subiendo
39
+ archivos = []
40
+
41
+ ## carga y almacenamiento de ficheros almacenada, acepta varios.
42
+ with st.sidebar:
43
+ archivos = load_name_files(FILE_LIST)
44
+ files_uploaded = st.file_uploader(
45
+ "Carga tus ficheros PDF",
46
+ type="pdf",
47
+ accept_multiple_files=True,
48
+ on_change=st.cache_resource.clear
49
+ )
50
+
51
+ if st.button("Guardar y procesar por LLM", type="secondary",help="donde buscará lo que comparará"):
52
+ for pdf in files_uploaded:
53
+ if pdf is not None and pdf.name not in archivos:
54
+ archivos.append(pdf.name)
55
+
56
+ archivos = save_name_files(FILE_LIST, archivos)
57
+
58
+
59
+ if len(archivos)>0:
60
+ st.write('Los archivos PDF se han cargados:')
61
+ lista_documentos = st.empty()
62
+ with lista_documentos.container():
63
+ for arch in archivos:
64
+ st.write(arch)
65
+ if st.button('Borrar ficheros'):
66
+ archivos = []
67
+ clean_files(FILE_LIST)
68
+ lista_documentos.empty()
69
+
70
+
71
+ # comprueba que hay archivos a ser tratados
72
+ if len(archivos)>0:
73
+ # comprueba que hay consulta a responder
74
+ if user_question := st.chat_input("Realizar consulta:"):
75
+ st.session_state.messages.append({"role": "user", "content": user_question})
76
+
77
+ if user_question:
78
+ for message in st.session_state.messages: # Muestra anteriores mensajes
79
+ with st.chat_message(message["role"]):
80
+ st.write(message["content"])
81
+
82
+ alert = st.warning("Sea paciente") # Mensaje de aviso o warning al usuario
83
+ time.sleep(3) # establece tiempo espera en 3 segundos
84
+ alert.empty() # borra el aviso
85
+
86
+ # se define el analizador-parser de los documentos.
87
+ parser = LlamaParse(
88
+ api_key=os.environ["LLAMA_CLOUD_API_KEY"], ##API de acceso a Cloud de LlamaIndex
89
+ result_type="markdown", # se toma "markdown", tambien hay text disponible
90
+ verbose=True,
91
+ )
92
+
93
+ cargar_embedmodel_y_llmmodel()
94
+
95
+ #se parametrizan los modelos de embedding y LLM
96
+ embed_model=OpenAIEmbedding(model="text-embedding-3-small") #embeddings para base de conocimiento
97
+ llm = OpenAI(model="gpt-3.5-turbo-0125") #modelo LLM usado
98
+
99
+ Settings.llm = llm
100
+ Settings.embed_model = embed_model
101
+
102
+
103
+ tratar = load_name_files(FILE_LIST) ##variable que tomará los ficheros a tratar recuperados de funcion
104
+ # st.write(tratar[0]) # se puede desasteriscar en desarrollo para apoyo
105
+ # st.write(tratar[1]) # se puede desasteriscar en desarrollo para apoyo
106
+
107
+
108
+ # Carga de los ficheros mediante LlamaParse, se ejecutará job para cada analizador-parser de los mismos
109
+ docs_202401 = parser.load_data( f'{tratar[0]}')
110
+ docs_202402 = parser.load_data( f'{tratar[1]}')
111
+
112
+ #uso de MarkdownElementNodeParser para analizar la salida de LlamaParse mediante un motor de consultas de recuperación(recursivo)
113
+ from llama_index.core.node_parser import MarkdownElementNodeParser
114
+ node_parser = MarkdownElementNodeParser(llm=OpenAI(model="gpt-3.5-turbo-0125"), num_workers=8)
115
+
116
+ import pickle
117
+ from llama_index.postprocessor.flag_embedding_reranker import FlagEmbeddingReranker
118
+ # se parametriza el modelo reranker
119
+ reranker = FlagEmbeddingReranker(
120
+ top_n=5,
121
+ model="BAAI/bge-reranker-large",
122
+ )
123
+ #funcion para Facilitar el motor de consultas sobre el almacén de vectores, y poderse realizar la recuperación.
124
+ def create_query_engine_over_doc(docs, nodes_save_path=None):
125
+ """Big function to go from document path -> recursive retriever."""
126
+ if nodes_save_path is not None and os.path.exists(nodes_save_path):
127
+ raw_nodes = pickle.load(open(nodes_save_path, "rb"))
128
+ else:
129
+ raw_nodes = node_parser.get_nodes_from_documents(docs)
130
+ if nodes_save_path is not None:
131
+ pickle.dump(raw_nodes, open(nodes_save_path, "wb"))
132
+
133
+ base_nodes, objects = node_parser.get_nodes_and_objects(
134
+ raw_nodes
135
+ )
136
+
137
+ ### Recuperador-retriever
138
+ # indice y motor
139
+ vector_index = VectorStoreIndex(nodes=base_nodes+objects)
140
+ query_engine = vector_index.as_query_engine(
141
+ similarity_top_k=15,
142
+ node_postprocessors=[reranker]
143
+ )
144
+ return query_engine, base_nodes, vector_index ###devuelve motor de consultas y nodos
145
+
146
+ ## motores de consulta y nodos para cada documento usando la función anterior.
147
+ ## En los ficheros .pkl se puede ver la estructura de los documentos que ha conformado o analizado y será con la que trabajará.
148
+ query_engine_202401, nodes_202401,vindex1 = create_query_engine_over_doc(
149
+ docs_202401, nodes_save_path="202401_nodes.pkl"
150
+ )
151
+ query_engine_202402, nodes_202402,vindex2 = create_query_engine_over_doc(
152
+ docs_202402, nodes_save_path="202402_nodes.pkl"
153
+ )
154
+
155
+ from llama_index.core.tools import QueryEngineTool, ToolMetadata
156
+ from llama_index.core.query_engine import SubQuestionQueryEngine
157
+
158
+ from llama_index.core.llms import ChatMessage
159
+
160
+ # motor de consulta como tool, configuración y contexto de los datos que deberá proveer por los que será consultado
161
+ # debajo se usa como motor de subconsultas SubQuestionQueryEngine
162
+ query_engine_tools = [
163
+ QueryEngineTool(
164
+ query_engine=query_engine_202401,
165
+ metadata=ToolMetadata(
166
+ name="pdf_ENERO",
167
+ description=(
168
+ # "Provides information about Datos del Producto for ENERO"
169
+ # "Provides information about values of fields of Datos del Producto, Titular, Fabricante,Composicion,Envases, Usos y Dosis Autorizados,Plazos de Seguridad"
170
+ """\
171
+ The documents provided are plant protection product data sheets in PDF format.
172
+ Provides information about values of fields of Datos del Producto, Titular, Fabricante,Composicion,Envases,
173
+ Usos y Dosis Autorizados,Plazos de Seguridad:
174
+ # Datos del Producto
175
+ |Numero de Registro|
176
+ |Estado|
177
+ |Fechas Inscripción|
178
+ |Renovación|
179
+ |Caducidad|
180
+ |Nombre Comercial|
181
+
182
+ # Titular
183
+
184
+ # Fabricante
185
+
186
+ # Composición
187
+
188
+ # Envases
189
+
190
+ # Usos y Dosis Autorizados
191
+ |USO|
192
+ |AGENTE|
193
+ |Dosis|
194
+ |Condic. Especifico|
195
+
196
+ """
197
+ ),
198
+ ),
199
+ ),
200
+ QueryEngineTool(
201
+ query_engine=query_engine_202402,
202
+ metadata=ToolMetadata(
203
+ name="pdf_FEBRERO",
204
+ description=(
205
+ # "Provides information about Datos del Producto for FEBRERO"
206
+ # "Provides information about values of fields of Datos del Producto, Titular, Fabricante,Composicion,Envases, Usos y Dosis Autorizados,Plazos de Seguridad"
207
+ """\
208
+ The documents provided are plant protection product data sheets in PDF format.
209
+ Provides information about values of fields of Datos del Producto, Titular, Fabricante,Composicion,Envases,
210
+ Usos y Dosis Autorizados,Plazos de Seguridad:
211
+ # Datos del Producto
212
+ |Numero de Registro|
213
+ |Estado|
214
+ |Fechas Inscripción|
215
+ |Renovación|
216
+ |Caducidad|
217
+ |Nombre Comercial|
218
+
219
+ # Titular
220
+
221
+ # Fabricante
222
+
223
+ # Composición
224
+
225
+ # Envases
226
+
227
+ # Usos y Dosis Autorizados
228
+ |USO|
229
+ |AGENTE|
230
+ |Dosis|
231
+ |Condic. Especifico|
232
+
233
+ """
234
+ ),
235
+ ),
236
+ ),
237
+ ]
238
+ # subconsultas con tool creada a través de SubQuestionQueryEngine
239
+ sub_query_engine = SubQuestionQueryEngine.from_defaults(
240
+ query_engine_tools=query_engine_tools,
241
+ llm=llm
242
+ )
243
+
244
+ if "chat_engine" not in st.session_state.keys(): # Initializa motor chat
245
+ # para que generen las subconsultas con la consulta-query del usuario
246
+ streaming_response = sub_query_engine.query(user_question)
247
+
248
+ ## If last message is not from assistant, generate a new response
249
+ if st.session_state.messages[-1]["role"] != "assistant":
250
+ with st.chat_message("assistant"):
251
+ with st.spinner("Thinking..."): #figura del spinner de streamlit mientras se ejecuta bloque
252
+ response = st.write(streaming_response.response) #respuesta entregada a la query-consulta del usuario
253
+ st.session_state.messages.append({"role": "assistant", "content": response})
254
+
ficheros.txt ADDED
@@ -0,0 +1 @@
 
 
1
+
requirements.txt ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ llama-index
2
+ llama-index-embeddings-openai
3
+ llama-index-core
4
+ llama-index-llms-openai
5
+ llama-index-question-gen-openai
6
+ llama-index-postprocessor-flag-embedding-reranker
7
+ git+https://github.com/FlagOpen/FlagEmbedding.git
8
+ llama-parse
utils.py ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ #import streamlit as st
3
+ import tempfile
4
+
5
+ #donde se almacenan los ficheros subidos por usuario
6
+ FILE_LIST = "ficheros.txt"
7
+
8
+ #funcion para cargar ficheros
9
+ def load_name_files(path):
10
+
11
+ archivos = []
12
+ with open(path, "r") as file:
13
+ for line in file:
14
+ archivos.append(line.strip())
15
+
16
+ return archivos
17
+
18
+ #informa nombres de ficheros a ser almacenados
19
+ def save_name_files(path, new_files):
20
+
21
+ old_files = load_name_files(path)
22
+
23
+ with open(path, "a") as file:
24
+ for item in new_files:
25
+ if item not in old_files:
26
+ file.write(item + "\n")
27
+ old_files.append(item)
28
+
29
+ return old_files
30
+
31
+ #limpieza de los ficheros
32
+ def clean_files(path):
33
+ with open(path, "w") as file:
34
+ pass
35
+ return True