From e5f364948f82294be40167c7ae9e9397a40d7e0d Mon Sep 17 00:00:00 2001 From: Jul1111 Date: Tue, 13 Jan 2026 17:03:34 +0100 Subject: [PATCH 1/3] Minor changes --- main.py | 12 +++- src/implementVectorDB.py | 41 ++++-------- src/loadingDataset.py | 138 +++++++++++++++++++++++++++++++++++++-- 3 files changed, 153 insertions(+), 38 deletions(-) diff --git a/main.py b/main.py index 994988f..a3d876b 100644 --- a/main.py +++ b/main.py @@ -1,13 +1,19 @@ from src.retrievalFunction import retrieve from src.implementVectorDB import LANGUAGE_MODEL, create_vector_db_from_dataset, VECTOR_DB, reset_vector_db -from src.loadingDataset import load_dataset +from src.loadingDataset import load_multiple_files import ollama # Réinitialiser au cas où reset_vector_db() # Load dataset and create vector database -dataset = load_dataset('tmp/cat-facts.txt') -create_vector_db_from_dataset(dataset) +# Charger depuis plusieurs sources +chunks = load_multiple_files([ + 'tmp/cat-facts.txt', + 'data/', + 'src/mon_module.py', + 'config.json', +]) +create_vector_db_from_dataset(chunks) print('\n' + '='*50) print('Chatbot is ready! Type "exit" to quit.') diff --git a/src/implementVectorDB.py b/src/implementVectorDB.py index 2fbafb0..496b0da 100644 --- a/src/implementVectorDB.py +++ b/src/implementVectorDB.py @@ -1,46 +1,27 @@ import ollama -from langchain_text_splitters import RecursiveCharacterTextSplitter -from src.loadingDataset import load_dataset +from src.loadingDataset import load_multiple_files EMBEDDING_MODEL = 'hf.co/CompendiumLabs/bge-base-en-v1.5-gguf' LANGUAGE_MODEL = 'hf.co/bartowski/Llama-3.2-1B-Instruct-GGUF' VECTOR_DB = [] + def add_chunk_to_database(chunk): embedding = ollama.embed(model=EMBEDDING_MODEL, input=chunk)['embeddings'][0] VECTOR_DB.append((chunk, embedding)) -#Configuration du découpage pour du texte -text_splitter = RecursiveCharacterTextSplitter( - chunk_size=500, # Taille maximale de chaque morceau (en caractères) - chunk_overlap=50, # Chevauchement pour garder le contexte - length_function=len, - is_separator_regex=False, -) - -#Configuration du découpage pour du code source -text_splitter_code = RecursiveCharacterTextSplitter( - chunk_size=500, # Taille maximale de chaque morceau (en caractères) - chunk_overlap=50, # Chevauchement pour garder le contexte - length_function=len, - is_separator_regex=False, - separators=["class", "def", "\n\n", "\n", " ", ""], -) - #Fonction pour créer la base de données vectorielle à partir du dataset chargé -def create_vector_db_from_dataset(dataset): +def create_vector_db_from_dataset(chunks): + """ + Ajoute les chunks déjà splitté à la base de données vectorielle. + Les chunks doivent être pré-splitté par load_multiple_files() + """ chunk_count = 0 - for data in dataset: - if data.strip().endswith(('.py', '.js', '.java', '.cpp', '.cs', '.rb', '.go')): - chunks = text_splitter_code.split_text(data) - else: - chunks = text_splitter.split_text(data) - - for chunk in chunks: - add_chunk_to_database(chunk) - chunk_count += 1 - #print(f'Added chunk {chunk_count}/{len(VECTOR_DB)} to the database') + for chunk in chunks: + add_chunk_to_database(chunk) + chunk_count += 1 + #print(f'Added chunk {chunk_count}/{len(VECTOR_DB)} to the database') def reset_vector_db(): global VECTOR_DB diff --git a/src/loadingDataset.py b/src/loadingDataset.py index 250caf7..2701f1a 100644 --- a/src/loadingDataset.py +++ b/src/loadingDataset.py @@ -1,10 +1,138 @@ -def load_dataset(filename): - with open(filename,'r') as file: - dataset = file.readlines() +import os +from langchain_text_splitters import RecursiveCharacterTextSplitter +from pypdf import PdfReader - #print(f'Loaded {len(dataset)} entries in the dataset.') +# Stratégies de splitting par type de fichier +SPLITTING_STRATEGIES = { + # Fichiers de code + 'code': RecursiveCharacterTextSplitter( + chunk_size=500, + chunk_overlap=50, + separators=["class", "def", "\n\n", "\n", " ", ""], + ), + # Fichiers JSON/YAML/Config + 'config': RecursiveCharacterTextSplitter( + chunk_size=300, + chunk_overlap=30, + separators=["\n", " ", ""], + ), + # Fichiers texte brut + 'text': RecursiveCharacterTextSplitter( + chunk_size=500, + chunk_overlap=50, + separators=["\n\n", "\n", " ", ""], + ), + 'pdf': RecursiveCharacterTextSplitter( + chunk_size=500, + chunk_overlap=50, + separators=["\n\n", "\n", " ", ""], + ), +} - return dataset +# Mapping extension -> type +FILE_TYPE_MAP = { + # Code + '.py': 'code', '.js': 'code', '.ts': 'code', '.jsx': 'code', '.tsx': 'code', + '.java': 'code', '.cpp': 'code', '.c': 'code', '.h': 'code', + '.cs': 'code', '.rb': 'code', '.go': 'code', '.rs': 'code', + '.php': 'code', '.swift': 'code', '.kt': 'code', '.sql':'code', + # Config + '.json': 'config', '.yaml': 'config', '.yml': 'config', + '.toml': 'config', '.ini': 'config', '.conf': 'config','.xml':'config', + # Texte + '.txt': 'text', '.md': 'text', + #PDF + 'pdf':'pdf', +} +def get_file_type(filepath): + """Détecte automatiquement le type de fichier.""" + _, ext = os.path.splitext(filepath) + return FILE_TYPE_MAP.get(ext.lower(), 'text') # Par défaut: texte +#Pour les fichiers PDF +def load_pdf(filepath): + """Extrait le texte d'un PDF.""" + text = "" + try: + reader = PdfReader(filepath) + for page_num, page in enumerate(reader.pages): + page_text = page.extract_text() + text += f"\n[Page {page_num + 1}]\n{page_text}" + return text + except Exception as e: + print(f"✗ Erreur en lisant le PDF {filepath}: {e}") + return "" +# Fonction principale pour charger et splitter un fichier 'txt' +def load_and_split_file(filepath): + """ + Charge et split un fichier automatiquement selon son type. + Retourne les chunks déjà splitté. + """ + file_type = get_file_type(filepath) + splitter = SPLITTING_STRATEGIES[file_type] + + try: + # Traitement spécial pour les PDFs + if file_type == 'pdf': + content = load_pdf(filepath) + else: + with open(filepath, 'r', encoding='utf-8') as f: + content = f.read() + + chunks = splitter.split_text(content) + print(f"✓ {filepath} ({file_type}) -> {len(chunks)} chunks") + return chunks + + except Exception as e: + print(f"✗ Erreur en lisant {filepath}: {e}") + return [] + +def load_multiple_files(paths): + """ + Charge plusieurs fichiers/dossiers et les split intelligemment. + Supporte: txt, code, json, yaml, pdf, et plus. + + Args: + paths (list): Liste de chemins (fichiers ou dossiers) + + Returns: + list: Tous les chunks extraits et splittés + """ + all_chunks = [] + total_files = 0 + total_chunks = 0 + + for path in paths: + if not os.path.exists(path): + print(f"⚠️ Le chemin n'existe pas: {path}") + continue + + try: + if os.path.isdir(path): + # Parcourir le dossier + print(f"\n📁 Traitement du dossier: {path}") + for filename in os.listdir(path): + filepath = os.path.join(path, filename) + if os.path.isfile(filepath): + chunks = load_and_split_file(filepath) + if chunks: + all_chunks.extend(chunks) + total_files += 1 + total_chunks += len(chunks) + else: + # Fichier unique + print(f"\n📄 Traitement du fichier: {path}") + chunks = load_and_split_file(path) + if chunks: + all_chunks.extend(chunks) + total_files += 1 + total_chunks += len(chunks) + + except Exception as e: + print(f"✗ Erreur lors du traitement de {path}: {e}") + continue + + print(f"\n✅ Résumé: {total_files} fichier(s) traité(s), {total_chunks} chunk(s) créé(s)") + return all_chunks \ No newline at end of file From 362635d4ff9ca6a57af9a7d330796793d6426de0 Mon Sep 17 00:00:00 2001 From: Jul1111 Date: Tue, 13 Jan 2026 21:19:05 +0100 Subject: [PATCH 2/3] Minor changes --- main.py | 79 ++++++++++++++++++++++++++++++++++++++++++------ requirements.txt | 11 +++---- 2 files changed, 74 insertions(+), 16 deletions(-) diff --git a/main.py b/main.py index a3d876b..cc6d1fe 100644 --- a/main.py +++ b/main.py @@ -2,17 +2,76 @@ from src.implementVectorDB import LANGUAGE_MODEL, create_vector_db_from_dataset, VECTOR_DB, reset_vector_db from src.loadingDataset import load_multiple_files import ollama +import os +import easygui as eg -# Réinitialiser au cas où +def select_knowledge_sources(): + """ + Menu interactif pour choisir les sources via interface graphique. + Fonctionne sur Windows, macOS et Linux. + """ + choice = eg.buttonbox( + msg='Configuration de la base de connaissance\n\nQue voulez-vous charger ?', + title='YourOwnRAG', + choices=['Fichiers', 'Dossiers', 'Défaut'] + ) + + if choice is None: + return ['tmp/cat-facts.txt'] + + sources = [] + + if choice == 'Fichiers': + # Sélectionner plusieurs fichiers + files = eg.fileopenbox( + msg='Sélectionnez les fichiers à charger', + title='Sélection de fichiers', + multiple=True, + filetypes=["*.txt", "*.pdf", "*.py", "*.json", "*.yaml", "*.*"] + ) + if files: + sources = files if isinstance(files, list) else [files] + print(f"\n✓ {len(sources)} fichier(s) sélectionné(s)") + for f in sources: + print(f" - {os.path.basename(f)}") + else: + print("\n✗ Aucun fichier sélectionné") + + elif choice == 'Dossiers': + # Sélectionner plusieurs dossiers + print("\nVous pouvez sélectionner plusieurs dossiers") + count = 0 + while True: + folder = eg.diropenbox( + msg='Sélectionnez un dossier (Annuler pour terminer)', + title='Sélection de dossier' + ) + if not folder: + break + sources.append(folder) + count += 1 + print(f"✓ Dossier {count} ajouté: {os.path.basename(folder)}") + + if sources: + print(f"\n✓ Total: {len(sources)} dossier(s) sélectionné(s)") + else: + print("\n✗ Aucun dossier sélectionné") + + else: # Défaut + sources = ['tmp/cat-facts.txt'] + print("✓ Sources par défaut chargées") + + return sources if sources else ['tmp/cat-facts.txt'] + +# Réinitialiser la base de vecteurs reset_vector_db() -# Load dataset and create vector database -# Charger depuis plusieurs sources -chunks = load_multiple_files([ - 'tmp/cat-facts.txt', - 'data/', - 'src/mon_module.py', - 'config.json', -]) + +# Sélectionner les sources de connaissance +sources = select_knowledge_sources() + +# Charger et créer la base de données vectorielle +print('\nChargement des sources...') +chunks = load_multiple_files(sources) create_vector_db_from_dataset(chunks) print('\n' + '='*50) @@ -22,7 +81,7 @@ # Main chatbot loop while True: # Get user input - input_query = input('Ask me a question: ') + input_query = input('You: ') # Check if user wants to exit if input_query.lower() == 'exit': diff --git a/requirements.txt b/requirements.txt index 945b037..d32b678 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,27 +2,26 @@ annotated-types==0.7.0 anyio==4.12.1 certifi==2026.1.4 charset-normalizer==3.4.4 -exceptiongroup==1.3.1 +easygui==0.98.3 h11==0.16.0 httpcore==1.0.9 httpx==0.28.1 idna==3.11 -init==0.1.0 jsonpatch==1.33 jsonpointer==3.0.0 -langchain-core==0.3.83 -langchain-text-splitters==0.3.11 -langsmith==0.4.37 +langchain-core==1.2.7 +langchain-text-splitters==1.1.0 +langsmith==0.6.2 ollama==0.6.1 orjson==3.11.5 packaging==25.0 pydantic==2.12.5 pydantic_core==2.41.5 +pypdf==6.6.0 PyYAML==6.0.3 requests==2.32.5 requests-toolbelt==1.0.0 tenacity==9.1.2 -terminal==0.4.0 typing-inspection==0.4.2 typing_extensions==4.15.0 urllib3==2.6.3 From fdbf969605e0646ae7759d4f56e19baa1bb86c27 Mon Sep 17 00:00:00 2001 From: Jul1111 Date: Tue, 13 Jan 2026 21:39:56 +0100 Subject: [PATCH 3/3] =?UTF-8?q?Changement=20de=20la=20fonctionnalit=C3=A9?= =?UTF-8?q?=20de=20choix=20de=20knowledge=20sources?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main.py | 52 +++++++++++++++++++++++++++++++++------------------- 1 file changed, 33 insertions(+), 19 deletions(-) diff --git a/main.py b/main.py index cc6d1fe..0afca70 100644 --- a/main.py +++ b/main.py @@ -7,22 +7,27 @@ def select_knowledge_sources(): """ - Menu interactif pour choisir les sources via interface graphique. - Fonctionne sur Windows, macOS et Linux. + Menu texte pour choisir le TYPE de source, puis utilise easygui pour sélectionner. """ - choice = eg.buttonbox( - msg='Configuration de la base de connaissance\n\nQue voulez-vous charger ?', - title='YourOwnRAG', - choices=['Fichiers', 'Dossiers', 'Défaut'] - ) + print('\n' + '='*60) + print('Configuration de la base de connaissance') + print('='*60) + print('\nOptions:') + print('1. Sélectionner des fichiers') + print('2. Sélectionner des dossiers') + print('3. Continuer sans charger (utiliser BDD existante)') + print('4. Utiliser les sources par défaut') - if choice is None: - return ['tmp/cat-facts.txt'] + while True: + choice = input('\nChoisissez une option (1-4): ').strip() + if choice in ['1', '2', '3', '4']: + break + print("✗ Entrée invalide, veuillez entrer 1, 2, 3 ou 4") sources = [] - if choice == 'Fichiers': - # Sélectionner plusieurs fichiers + if choice == '1': + # Sélectionner plusieurs fichiers via easygui files = eg.fileopenbox( msg='Sélectionnez les fichiers à charger', title='Sélection de fichiers', @@ -37,8 +42,8 @@ def select_knowledge_sources(): else: print("\n✗ Aucun fichier sélectionné") - elif choice == 'Dossiers': - # Sélectionner plusieurs dossiers + elif choice == '2': + # Sélectionner plusieurs dossiers via easygui print("\nVous pouvez sélectionner plusieurs dossiers") count = 0 while True: @@ -57,11 +62,16 @@ def select_knowledge_sources(): else: print("\n✗ Aucun dossier sélectionné") - else: # Défaut + elif choice == '3': + # Continuer sans charger + print("✓ Mode sans chargement - Utilisation de la BDD existante") + sources = None + + else: # choice == '4' sources = ['tmp/cat-facts.txt'] print("✓ Sources par défaut chargées") - return sources if sources else ['tmp/cat-facts.txt'] + return sources # Réinitialiser la base de vecteurs reset_vector_db() @@ -69,10 +79,14 @@ def select_knowledge_sources(): # Sélectionner les sources de connaissance sources = select_knowledge_sources() -# Charger et créer la base de données vectorielle -print('\nChargement des sources...') -chunks = load_multiple_files(sources) -create_vector_db_from_dataset(chunks) +# Charger et créer la base de données vectorielle si sources sont spécifiées +if sources is not None: + print('\nChargement des sources...') + chunks = load_multiple_files(sources) + create_vector_db_from_dataset(chunks) +else: + print('\n⚠️ Aucune source chargée - Utilisation de la BDD existante') + print('(Assurez-vous que la BDD contient déjà des données)') print('\n' + '='*50) print('Chatbot is ready! Type "exit" to quit.')