Spaces:
Sleeping
Sleeping
File size: 11,018 Bytes
d5f44d7 |
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 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 |
##Instalación de paquetes necesarios
import streamlit as st
import os
import time
import torch
from utils import *
from dotenv import load_dotenv
load_dotenv()
##import nest_asyncio
##nest_asyncio.apply()
from llama_parse import LlamaParse
from llama_index.llms.openai import OpenAI
from llama_index.embeddings.openai import OpenAIEmbedding
from llama_index.core import VectorStoreIndex, ServiceContext
from llama_index.core import SimpleDirectoryReader
from llama_index.core import Settings
######
## titulos y cabeceras
st.set_page_config('compare PDF por LLM')
st.title("Comparar PDFs mediante LLM")
st.subheader("Campos a comparar en tu PDF",divider='rainbow')
OPENAI_API_KEY = st.text_input('OpenAI API Key', type='password')
LLAMA_CLOUD_API_KEY = st.text_input('LLAMA API Key', type='password')
#### Inicializar mensajes de chat
if "messages" not in st.session_state.keys():
st.session_state.messages = [
{"role": "assistant", "content": "Ask me a question about PDFs files you provided me"}
]
@st.cache_resource(show_spinner=False) # Añade decorador de caché
def cargar_embedmodel_y_llmmodel():
return True
#esta variable es para tener aqui un listado de aquellos ficheros que se han ido subiendo
archivos = []
## carga y almacenamiento de ficheros almacenada, acepta varios.
with st.sidebar:
archivos = load_name_files(FILE_LIST)
files_uploaded = st.file_uploader(
"Carga tus ficheros PDF",
type="pdf",
accept_multiple_files=True,
on_change=st.cache_resource.clear
)
if st.button("Guardar y procesar por LLM", type="secondary",help="donde buscará lo que comparará"):
for pdf in files_uploaded:
if pdf is not None and pdf.name not in archivos:
archivos.append(pdf.name)
archivos = save_name_files(FILE_LIST, archivos)
if len(archivos)>0:
st.write('Los archivos PDF se han cargados:')
lista_documentos = st.empty()
with lista_documentos.container():
for arch in archivos:
st.write(arch)
if st.button('Borrar ficheros'):
archivos = []
clean_files(FILE_LIST)
lista_documentos.empty()
# comprueba que hay archivos a ser tratados
if len(archivos)>0:
# comprueba que hay consulta a responder
if user_question := st.chat_input("Realizar consulta:"):
st.session_state.messages.append({"role": "user", "content": user_question})
if user_question:
for message in st.session_state.messages: # Muestra anteriores mensajes
with st.chat_message(message["role"]):
st.write(message["content"])
alert = st.warning("Sea paciente") # Mensaje de aviso o warning al usuario
time.sleep(3) # establece tiempo espera en 3 segundos
alert.empty() # borra el aviso
# se define el analizador-parser de los documentos.
os.environ["OPENAI_API_KEY"] = OPENAI_API_KEY
parser = LlamaParse(
## api_key=os.environ["LLAMA_CLOUD_API_KEY"], ##API de acceso a Cloud de LlamaIndex
api_key=LLAMA_CLOUD_API_KEY,
result_type="markdown", # se toma "markdown", tambien hay text disponible
verbose=True,
)
cargar_embedmodel_y_llmmodel()
#se parametrizan los modelos de embedding y LLM
embed_model=OpenAIEmbedding(model="text-embedding-3-small") #embeddings para base de conocimiento
llm = OpenAI(model="gpt-3.5-turbo-0125") #modelo LLM usado
Settings.llm = llm
Settings.embed_model = embed_model
tratar = load_name_files(FILE_LIST) ##variable que tomará los ficheros a tratar recuperados de funcion
# st.write(tratar[0]) # se puede desasteriscar en desarrollo para apoyo
# st.write(tratar[1]) # se puede desasteriscar en desarrollo para apoyo
# Carga de los ficheros mediante LlamaParse, se ejecutará job para cada analizador-parser de los mismos
docs_202401 = parser.load_data( f'{tratar[0]}')
docs_202402 = parser.load_data( f'{tratar[1]}')
#uso de MarkdownElementNodeParser para analizar la salida de LlamaParse mediante un motor de consultas de recuperación(recursivo)
from llama_index.core.node_parser import MarkdownElementNodeParser
node_parser = MarkdownElementNodeParser(llm=OpenAI(model="gpt-3.5-turbo-0125"), num_workers=8)
import pickle
from llama_index.postprocessor.flag_embedding_reranker import FlagEmbeddingReranker
# se parametriza el modelo reranker
reranker = FlagEmbeddingReranker(
top_n=5,
model="BAAI/bge-reranker-large",
)
#funcion para Facilitar el motor de consultas sobre el almacén de vectores, y poderse realizar la recuperación.
def create_query_engine_over_doc(docs, nodes_save_path=None):
"""Big function to go from document path -> recursive retriever."""
if nodes_save_path is not None and os.path.exists(nodes_save_path):
raw_nodes = pickle.load(open(nodes_save_path, "rb"))
else:
raw_nodes = node_parser.get_nodes_from_documents(docs)
if nodes_save_path is not None:
pickle.dump(raw_nodes, open(nodes_save_path, "wb"))
base_nodes, objects = node_parser.get_nodes_and_objects(
raw_nodes
)
### Recuperador-retriever
# indice y motor
vector_index = VectorStoreIndex(nodes=base_nodes+objects)
query_engine = vector_index.as_query_engine(
similarity_top_k=15,
node_postprocessors=[reranker]
)
return query_engine, base_nodes, vector_index ###devuelve motor de consultas y nodos
## motores de consulta y nodos para cada documento usando la función anterior.
## En los ficheros .pkl se puede ver la estructura de los documentos que ha conformado o analizado y será con la que trabajará.
query_engine_202401, nodes_202401,vindex1 = create_query_engine_over_doc(
docs_202401, nodes_save_path="202401_nodes.pkl"
)
query_engine_202402, nodes_202402,vindex2 = create_query_engine_over_doc(
docs_202402, nodes_save_path="202402_nodes.pkl"
)
from llama_index.core.tools import QueryEngineTool, ToolMetadata
from llama_index.core.query_engine import SubQuestionQueryEngine
from llama_index.core.llms import ChatMessage
# motor de consulta como tool, configuración y contexto de los datos que deberá proveer por los que será consultado
# debajo se usa como motor de subconsultas SubQuestionQueryEngine
query_engine_tools = [
QueryEngineTool(
query_engine=query_engine_202401,
metadata=ToolMetadata(
name="pdf_ENERO",
description=(
# "Provides information about Datos del Producto for ENERO"
# "Provides information about values of fields of Datos del Producto, Titular, Fabricante,Composicion,Envases, Usos y Dosis Autorizados,Plazos de Seguridad"
"""\
The documents provided are plant protection product data sheets in PDF format.
Provides information about values of fields of Datos del Producto, Titular, Fabricante,Composicion,Envases,
Usos y Dosis Autorizados,Plazos de Seguridad:
# Datos del Producto
|Numero de Registro|
|Estado|
|Fechas Inscripción|
|Renovación|
|Caducidad|
|Nombre Comercial|
# Titular
# Fabricante
# Composición
# Envases
# Usos y Dosis Autorizados
|USO|
|AGENTE|
|Dosis|
|Condic. Especifico|
"""
),
),
),
QueryEngineTool(
query_engine=query_engine_202402,
metadata=ToolMetadata(
name="pdf_FEBRERO",
description=(
# "Provides information about Datos del Producto for FEBRERO"
# "Provides information about values of fields of Datos del Producto, Titular, Fabricante,Composicion,Envases, Usos y Dosis Autorizados,Plazos de Seguridad"
"""\
The documents provided are plant protection product data sheets in PDF format.
Provides information about values of fields of Datos del Producto, Titular, Fabricante,Composicion,Envases,
Usos y Dosis Autorizados,Plazos de Seguridad:
# Datos del Producto
|Numero de Registro|
|Estado|
|Fechas Inscripción|
|Renovación|
|Caducidad|
|Nombre Comercial|
# Titular
# Fabricante
# Composición
# Envases
# Usos y Dosis Autorizados
|USO|
|AGENTE|
|Dosis|
|Condic. Especifico|
"""
),
),
),
]
# subconsultas con tool creada a través de SubQuestionQueryEngine
sub_query_engine = SubQuestionQueryEngine.from_defaults(
query_engine_tools=query_engine_tools,
llm=llm
)
if "chat_engine" not in st.session_state.keys(): # Initializa motor chat
# para que generen las subconsultas con la consulta-query del usuario
streaming_response = sub_query_engine.query(user_question)
## If last message is not from assistant, generate a new response
if st.session_state.messages[-1]["role"] != "assistant":
with st.chat_message("assistant"):
with st.spinner("Thinking..."): #figura del spinner de streamlit mientras se ejecuta bloque
response = st.write(streaming_response.response) #respuesta entregada a la query-consulta del usuario
st.session_state.messages.append({"role": "assistant", "content": response})
|