Spaces:
Build error
Build error
| from fastapi import FastAPI, HTTPException, UploadFile, File | |
| from pydantic import BaseModel | |
| import os | |
| import uuid | |
| import shutil | |
| import certifi | |
| from langchain_community.vectorstores import Chroma | |
| from langchain.storage import InMemoryStore | |
| from langchain.retrievers.multi_vector import MultiVectorRetriever | |
| from langchain_huggingface.embeddings import HuggingFaceEmbeddings | |
| from langchain_core.documents import Document | |
| from langchain_core.runnables import RunnablePassthrough, RunnableLambda | |
| from langchain_core.messages import HumanMessage | |
| from langchain_community.llms import HuggingFaceHub | |
| from langchain_core.prompts import ChatPromptTemplate | |
| from langchain_core.output_parsers import StrOutputParser | |
| from unstructured.partition.pdf import partition_pdf | |
| from base64 import b64decode | |
| from datetime import datetime | |
| from werkzeug.utils import secure_filename | |
| from fastapi.middleware.cors import CORSMiddleware | |
| from pymongo import MongoClient | |
| from bson.objectid import ObjectId | |
| from dotenv import load_dotenv | |
| load_dotenv() | |
| app = FastAPI() | |
| # Ruta para la ra铆z | |
| # Configuraci贸n de CORS | |
| app.add_middleware( | |
| CORSMiddleware, | |
| allow_origins=["*"], # Permite todos los or铆genes (cambiar en producci贸n) | |
| allow_credentials=True, | |
| allow_methods=["*"], | |
| allow_headers=["*"], | |
| ) | |
| # Configuraci贸n de las claves de API | |
| os.environ["GROQ_API_KEY"] = os.getenv("GROQ_API_KEY") | |
| os.environ["LANGCHAIN_API_KEY"] = os.getenv("LANGCHAIN_API_KEY") | |
| os.environ["LANGCHAIN_TRACING_V2"] = os.getenv("LANGCHAIN_TRACING_V2") | |
| os.environ["HUGGINGFACEHUB_API_TOKEN"] = os.getenv("HUGGINGFACEHUB_API_TOKEN") | |
| os.environ['SSL_CERT_FILE'] = certifi.where() | |
| # Configuraci贸n de la carpeta temporal | |
| TEMP_FOLDER = os.getenv('TEMP_FOLDER', './_temp') | |
| os.makedirs(TEMP_FOLDER, exist_ok=True) | |
| # Configuraci贸n de MongoDB | |
| MONGO_URI = os.getenv("MONGO_URI") | |
| client = MongoClient(MONGO_URI, tls=True, tlsCAFile=certifi.where()) | |
| db = client.chatbotlaguna | |
| conversations_collection = db.conversations | |
| documents_collection = db.documents | |
| # Configuraci贸n del vectorstore | |
| vectorstore = Chroma( | |
| collection_name="multi_modal_rag", | |
| embedding_function=HuggingFaceEmbeddings(model_name="sentence-transformers/all-mpnet-base-v2") | |
| ) | |
| store = InMemoryStore() | |
| id_key = "doc_id" | |
| retriever = MultiVectorRetriever( | |
| vectorstore=vectorstore, | |
| docstore=store, | |
| id_key=id_key, | |
| ) | |
| # Configuraci贸n del modelo de Hugging Face | |
| llm = HuggingFaceHub( | |
| repo_id="mistralai/Mistral-7B-Instruct-v0.2", | |
| model_kwargs={"temperature": 0.5, "max_length": 512} | |
| ) | |
| # Funci贸n para parsear documentos | |
| def parse_docs(docs): | |
| b64 = [] | |
| text = [] | |
| for doc in docs: | |
| try: | |
| b64decode(doc) | |
| b64.append(doc) | |
| except Exception: | |
| text.append(doc) | |
| return {"images": b64, "texts": text} | |
| # Funci贸n para construir el prompt | |
| def build_prompt(kwargs): | |
| docs_by_type = kwargs["context"] | |
| user_question = kwargs["question"] | |
| context_text = "".join(text_element.text for text_element in docs_by_type["texts"]) | |
| prompt_template = f""" | |
| Answer the question based only on the following context, which can include text, tables, and images. | |
| Context: {context_text} | |
| Question: {user_question} | |
| """ | |
| prompt_content = [{"type": "text", "text": prompt_template}] | |
| if len(docs_by_type["images"]) > 0: | |
| for image in docs_by_type["images"]: | |
| prompt_content.append( | |
| { | |
| "type": "image_url", | |
| "image_url": {"url": f"data:image/jpeg;base64,{image}"}, | |
| } | |
| ) | |
| return ChatPromptTemplate.from_messages( | |
| [ | |
| HumanMessage(content=prompt_content), | |
| ] | |
| ) | |
| # Cadena de procesamiento | |
| chain = ( | |
| { | |
| "context": retriever | RunnableLambda(parse_docs), | |
| "question": RunnablePassthrough(), | |
| } | |
| | RunnableLambda(build_prompt) | |
| | llm | |
| | StrOutputParser() | |
| ) | |
| chain_with_sources = { | |
| "context": retriever | RunnableLambda(parse_docs), | |
| "question": RunnablePassthrough(), | |
| } | RunnablePassthrough().assign( | |
| response=( | |
| RunnableLambda(build_prompt) | |
| | llm | |
| | StrOutputParser() | |
| ) | |
| ) | |
| # Modelo de solicitud | |
| class QueryRequest(BaseModel): | |
| question: str | |
| conversation_name: str = "Default Conversation" | |
| # Ruta para incrustar archivos PDF | |
| async def embed_pdf(file: UploadFile = File(...)): | |
| if not file.filename.endswith(".pdf"): | |
| raise HTTPException(status_code=400, detail="Solo se permiten archivos PDF") | |
| # Guardar el archivo en la carpeta de documentos | |
| file_path = f"./documents/{secure_filename(file.filename)}" | |
| with open(file_path, "wb") as f: | |
| shutil.copyfileobj(file.file, f) | |
| # Procesar el PDF | |
| chunks = partition_pdf( | |
| filename=file_path, | |
| infer_table_structure=True, | |
| strategy="hi_res", | |
| extract_image_block_types=["Image"], | |
| extract_image_block_to_payload=True, | |
| chunking_strategy="by_title", | |
| max_characters=10000, | |
| combine_text_under_n_chars=2000, | |
| new_after_n_chars=6000, | |
| ) | |
| # Separar textos, tablas e im谩genes | |
| texts = [chunk for chunk in chunks if "CompositeElement" in str(type(chunk))] | |
| tables = [chunk for chunk in chunks if "Table" in str(type(chunk))] | |
| images = get_images_base64(chunks) | |
| # Agregar textos al vectorstore | |
| doc_ids = [str(uuid.uuid4()) for _ in texts] | |
| summary_texts = [ | |
| Document(page_content=chunk.text, metadata={id_key: doc_ids[i]}) for i, chunk in enumerate(texts) | |
| ] | |
| retriever.vectorstore.add_documents(summary_texts) | |
| retriever.docstore.mset(list(zip(doc_ids, texts))) | |
| # Guardar la informaci贸n del documento en MongoDB | |
| document_data = { | |
| "file_path": file_path, | |
| "doc_ids": doc_ids, | |
| "texts": [chunk.text for chunk in texts], | |
| "tables": [chunk.text for chunk in tables], | |
| "images": images, | |
| "upload_date": datetime.now() | |
| } | |
| documents_collection.insert_one(document_data) | |
| return {"message": "PDF procesado y datos incrustados correctamente"} | |
| # Ruta para realizar consultas | |
| async def query(request: QueryRequest): | |
| try: | |
| response = chain.invoke(request.question) | |
| # Guardar la conversaci贸n en MongoDB | |
| conversation_data = { | |
| "conversation_name": request.conversation_name, | |
| "question": request.question, | |
| "response": response, | |
| "timestamp": datetime.now() | |
| } | |
| conversations_collection.insert_one(conversation_data) | |
| return {"response": response} | |
| except Exception as e: | |
| raise HTTPException(status_code=500, detail=str(e)) | |
| # Funci贸n para obtener im谩genes en base64 | |
| def get_images_base64(chunks): | |
| images_b64 = [] | |
| for chunk in chunks: | |
| if "CompositeElement" in str(type(chunk)): | |
| chunk_els = chunk.metadata.orig_elements | |
| for el in chunk_els: | |
| if "Image" in str(type(el)): | |
| images_b64.append(el.metadata.image_base64) | |
| return images_b64 | |
| # Crear la carpeta de documentos si no existe | |
| if not os.path.exists("./documents"): | |
| os.makedirs("./documents") | |
| # Iniciar la aplicaci贸n | |
| if __name__ == "__main__": | |
| import uvicorn | |
| uvicorn.run(app, host="0.0.0.0", port=8000) | |