#!/usr/bin/env python3 """Construye el indice del RAG sin abrir la interfaz. flatpak run --command=python3 io.github.qwersyk.Newelle//master \ ~/COFRE/CODERS/JARVIS/config/construir_indice.py En la app esto es el boton "Update Index" de los ajustes del RAG, que lanza un hilo y va enseñando el progreso en una barra. Desde aqui se llama a lo mismo pero en primer plano, para poder dejarlo corriendo en una terminal y ver si falla, que con 20 000 fragmentos conviene. Antes hay que haber preparado el corpus con indexar.py: este script indexa lo que haya en la carpeta de documentos, sin filtrar nada. """ import os import sys import time sys.path.insert(0, "/app/share/newelle") sys.path.insert(0, os.path.expanduser( "~/.var/app/io.github.qwersyk.Newelle/config/pip")) from gi.repository import Gio # noqa: E402 from newelle.handlers.rag.llamaindex_handler import LlamaIndexHanlder # noqa: E402 from newelle.handlers.embeddings.model2vec import Model2VecHandler # noqa: E402 CONFIG = os.path.expanduser("~/.var/app/io.github.qwersyk.Newelle/config") MODELOS = os.path.join(CONFIG, "models") DOCUMENTOS = os.path.join(MODELOS, "documents") def main(): if not os.path.isdir(DOCUMENTOS): print(f"no existe {DOCUMENTOS}") print("prepara antes el corpus: python3 config/indexar.py --hacerlo") return 1 ficheros = [f for f in os.listdir(DOCUMENTOS) if not f.startswith(".")] if not ficheros: print("la carpeta de documentos esta vacia") return 1 tam = sum(os.path.getsize(os.path.join(DOCUMENTOS, f)) for f in ficheros) / 1048576 print(f"{len(ficheros)} documentos · {tam:.1f} MB") settings = Gio.Settings.new("io.github.qwersyk.Newelle") rag = LlamaIndexHanlder(settings, MODELOS) if not rag.is_installed(): print("faltan dependencias del RAG (llama-index, faiss, tiktoken)") return 1 # El RAG necesita el modelo de embeddings para vectorizar, y opcionalmente # un LLM secundario para reescribir la consulta. Aqui solo hace falta el # primero: se indexa, no se pregunta. rag.embedding = Model2VecHandler(settings, MODELOS) if not rag.embedding.is_installed(): print("instalando model2vec...") rag.embedding.install() # create_index solo le pide load_model() al LLM —para indexar no hace falta # generar nada— asi que un cascaron sirve y evita cargar 3,7 GB de modelo # para vectorizar documentos. class SinModelo: def load_model(self, *a, **k): return True rag.llm = SinModelo() rag.secondary_llm = SinModelo() trozo = rag.get_setting("chunk_size") print(f"fragmentos de {trozo} tokens · esto tarda, no lo cortes\n") ini = time.time() try: rag.create_index() except Exception as e: print(f"\nfallo al indexar: {type(e).__name__}: {e}") return 1 minutos = (time.time() - ini) / 60 _, data_path = rag.get_paths() peso = sum( os.path.getsize(os.path.join(dp, f)) for dp, _, fs in os.walk(data_path) for f in fs) / 1048576 # create_index se traga sus propias excepciones e imprime el error, asi que # terminar sin fallo no significa que haya indice. Se comprueba el disco. if peso < 0.1: print(f"\nno se creo indice ({peso:.1f} MB en {data_path})") print("mira el error de arriba") return 1 print(f"\nindice construido en {minutos:.1f} min · {peso:.1f} MB") print(f"en {data_path}") return 0 if __name__ == "__main__": sys.exit(main())