Processus pour IA
This commit is contained in:
parent
b2c47048c7
commit
813fb5684e
90
IA/02 - injection_fiches/README.md
Normal file
90
IA/02 - injection_fiches/README.md
Normal file
@ -0,0 +1,90 @@
|
||||
# Script d'Injection Automatique pour PrivateGPT
|
||||
|
||||
Ce script permet d'automatiser l'injection de documents dans PrivateGPT à partir d'un répertoire local. Au lieu d'utiliser l'interface utilisateur pour télécharger les fichiers un par un, vous pouvez injecter un dossier entier en une seule commande.
|
||||
|
||||
## Prérequis
|
||||
|
||||
- Python 3.7 ou supérieur
|
||||
- PrivateGPT installé et fonctionnel sous Docker
|
||||
- Accès à l'API REST de PrivateGPT (port 8001 par défaut)
|
||||
|
||||
## Installation
|
||||
|
||||
1. Clonez ce dépôt ou téléchargez les fichiers dans un dossier
|
||||
|
||||
2. Installez les dépendances nécessaires :
|
||||
```bash
|
||||
pip install requests
|
||||
```
|
||||
|
||||
## Utilisation
|
||||
|
||||
Le script s'utilise en ligne de commande avec différentes options :
|
||||
|
||||
```bash
|
||||
python auto_ingest.py -d REPERTOIRE [-u URL] [-r] [-t THREADS] [--retry RETRY] [--retry-delay RETRY_DELAY] [--timeout TIMEOUT] [--extensions EXT1 EXT2 ...]
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
- `-d, --directory` : Chemin du répertoire contenant les fichiers à injecter (obligatoire)
|
||||
- `-u, --url` : URL de l'API PrivateGPT (défaut: http://localhost:8001)
|
||||
- `-r, --recursive` : Parcourir récursivement les sous-répertoires
|
||||
- `-t, --threads` : Nombre de threads pour les injections parallèles (défaut: 5)
|
||||
- `--retry` : Nombre de tentatives en cas d'échec (défaut: 3)
|
||||
- `--retry-delay` : Délai entre les tentatives en secondes (défaut: 5)
|
||||
- `--timeout` : Délai d'attente pour chaque requête en secondes (défaut: 300)
|
||||
- `--extensions` : Liste d'extensions spécifiques à injecter (ex: pdf txt)
|
||||
|
||||
## Exemples d'utilisation
|
||||
|
||||
### Injection simple d'un répertoire
|
||||
|
||||
```bash
|
||||
python auto_ingest.py -d /chemin/vers/documents
|
||||
```
|
||||
|
||||
### Injection récursive avec extensions spécifiques
|
||||
|
||||
```bash
|
||||
python auto_ingest.py -d /chemin/vers/documents -r --extensions pdf docx txt
|
||||
```
|
||||
|
||||
### Injection avec paramètres avancés
|
||||
|
||||
```bash
|
||||
python auto_ingest.py -d /chemin/vers/documents -r -t 10 --timeout 600 --retry 5
|
||||
```
|
||||
|
||||
## Formats de fichiers supportés
|
||||
|
||||
Par défaut, le script reconnaît et traite les formats suivants :
|
||||
- PDF (.pdf)
|
||||
- Documents texte (.txt, .md)
|
||||
- Documents Microsoft Office (.doc, .docx, .ppt, .pptx, .xls, .xlsx)
|
||||
- CSV (.csv)
|
||||
- EPUB (.epub)
|
||||
- HTML (.html, .htm)
|
||||
|
||||
## Résolution des problèmes
|
||||
|
||||
### Erreur de connexion
|
||||
|
||||
Si vous obtenez des erreurs de connexion, vérifiez que :
|
||||
1. PrivateGPT est bien en cours d'exécution
|
||||
2. L'URL est correcte (par défaut: http://localhost:8001)
|
||||
3. Le port 8001 est accessible et n'est pas bloqué par un pare-feu
|
||||
|
||||
### Erreurs d'injection
|
||||
|
||||
- Si un fichier spécifique ne peut pas être injecté, vérifiez qu'il est d'un format supporté par PrivateGPT
|
||||
- Pour les fichiers volumineux, vous pouvez augmenter la valeur de `--timeout`
|
||||
- En cas d'erreurs répétées, augmentez les valeurs de `--retry` et `--retry-delay`
|
||||
|
||||
## Logs
|
||||
|
||||
Le script génère des logs dans :
|
||||
- La console (stdout)
|
||||
- Un fichier pgpt_auto_ingest.log dans le répertoire courant
|
||||
|
||||
Ces logs contiennent des informations détaillées sur le processus d'injection.
|
||||
254
IA/02 - injection_fiches/auto_ingest.py
Executable file
254
IA/02 - injection_fiches/auto_ingest.py
Executable file
@ -0,0 +1,254 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Script d'injection automatique de documents pour PrivateGPT
|
||||
|
||||
Ce script parcourt un répertoire spécifié et injecte tous les fichiers
|
||||
compatibles dans PrivateGPT via son API REST.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import argparse
|
||||
import logging
|
||||
import requests
|
||||
from pathlib import Path
|
||||
from typing import List, Dict, Tuple, Set
|
||||
from concurrent.futures import ThreadPoolExecutor, as_completed
|
||||
|
||||
# Configuration du logging
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(levelname)s - %(message)s',
|
||||
handlers=[
|
||||
logging.StreamHandler(),
|
||||
logging.FileHandler("pgpt_auto_ingest.log")
|
||||
]
|
||||
)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Extensions de fichiers couramment supportées par PrivateGPT
|
||||
SUPPORTED_EXTENSIONS = {
|
||||
'.pdf', '.txt', '.md', '.doc', '.docx', '.ppt', '.pptx',
|
||||
'.xls', '.xlsx', '.csv', '.epub', '.html', '.htm'
|
||||
}
|
||||
|
||||
def parse_arguments():
|
||||
"""Parse les arguments de ligne de commande."""
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Injecte automatiquement tous les fichiers d'un répertoire dans PrivateGPT"
|
||||
)
|
||||
parser.add_argument(
|
||||
"-d", "--directory",
|
||||
type=str,
|
||||
required=True,
|
||||
help="Chemin du répertoire contenant les fichiers à injecter"
|
||||
)
|
||||
parser.add_argument(
|
||||
"-u", "--url",
|
||||
type=str,
|
||||
default="http://localhost:8001",
|
||||
help="URL de l'API PrivateGPT (défaut: http://localhost:8001)"
|
||||
)
|
||||
parser.add_argument(
|
||||
"-r", "--recursive",
|
||||
action="store_true",
|
||||
help="Parcourir récursivement les sous-répertoires"
|
||||
)
|
||||
parser.add_argument(
|
||||
"-t", "--threads",
|
||||
type=int,
|
||||
default=5,
|
||||
help="Nombre de threads pour les injections parallèles (défaut: 5)"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--retry",
|
||||
type=int,
|
||||
default=3,
|
||||
help="Nombre de tentatives en cas d'échec (défaut: 3)"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--retry-delay",
|
||||
type=int,
|
||||
default=5,
|
||||
help="Délai entre les tentatives en secondes (défaut: 5)"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--timeout",
|
||||
type=int,
|
||||
default=300,
|
||||
help="Délai d'attente pour chaque requête en secondes (défaut: 300)"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--extensions",
|
||||
nargs="+",
|
||||
help="Liste d'extensions spécifiques à injecter (ex: .pdf .txt)"
|
||||
)
|
||||
|
||||
return parser.parse_args()
|
||||
|
||||
def find_files(directory: str, recursive: bool = False,
|
||||
extensions: Set[str] = SUPPORTED_EXTENSIONS) -> List[Path]:
|
||||
"""
|
||||
Trouve tous les fichiers avec les extensions spécifiées dans le répertoire.
|
||||
|
||||
Args:
|
||||
directory: Répertoire à scanner
|
||||
recursive: Si True, parcourt aussi les sous-répertoires
|
||||
extensions: Ensemble d'extensions de fichier à inclure
|
||||
|
||||
Returns:
|
||||
Liste des chemins de fichiers trouvés
|
||||
"""
|
||||
directory_path = Path(directory)
|
||||
|
||||
if not directory_path.exists() or not directory_path.is_dir():
|
||||
logger.error(f"Le répertoire {directory} n'existe pas ou n'est pas un répertoire.")
|
||||
return []
|
||||
|
||||
files = []
|
||||
|
||||
if recursive:
|
||||
# Parcours récursif
|
||||
for root, _, filenames in os.walk(directory):
|
||||
for filename in filenames:
|
||||
file_path = Path(root) / filename
|
||||
if file_path.suffix.lower() in extensions:
|
||||
files.append(file_path)
|
||||
else:
|
||||
# Parcours non récursif
|
||||
for file_path in directory_path.iterdir():
|
||||
if file_path.is_file() and file_path.suffix.lower() in extensions:
|
||||
files.append(file_path)
|
||||
|
||||
return files
|
||||
|
||||
def ingest_file(file_path: Path, pgpt_url: str, timeout: int,
|
||||
retry_count: int, retry_delay: int) -> Tuple[Path, bool, str]:
|
||||
"""
|
||||
Injecte un fichier dans PrivateGPT.
|
||||
|
||||
Args:
|
||||
file_path: Chemin du fichier à injecter
|
||||
pgpt_url: URL de base de l'API PrivateGPT
|
||||
timeout: Délai d'attente pour la requête
|
||||
retry_count: Nombre de tentatives en cas d'échec
|
||||
retry_delay: Délai entre les tentatives en secondes
|
||||
|
||||
Returns:
|
||||
Tuple contenant (chemin_fichier, succès, message)
|
||||
"""
|
||||
ingest_url = f"{pgpt_url}/v1/ingest/file"
|
||||
|
||||
for attempt in range(retry_count):
|
||||
try:
|
||||
logger.info(f"Injection de {file_path} (tentative {attempt + 1}/{retry_count})")
|
||||
|
||||
with open(file_path, 'rb') as file:
|
||||
files = {'file': (file_path.name, file, 'application/octet-stream')}
|
||||
response = requests.post(ingest_url, files=files, timeout=timeout)
|
||||
|
||||
if response.status_code == 200:
|
||||
result = response.json()
|
||||
doc_ids = result.get('document_ids', [])
|
||||
logger.info(f"Succès! {file_path} -> {len(doc_ids)} documents créés")
|
||||
return file_path, True, f"{len(doc_ids)} documents créés"
|
||||
else:
|
||||
error_msg = f"Erreur HTTP {response.status_code}: {response.text}"
|
||||
logger.warning(error_msg)
|
||||
if attempt < retry_count - 1:
|
||||
logger.info(f"Nouvelle tentative dans {retry_delay} secondes...")
|
||||
time.sleep(retry_delay)
|
||||
else:
|
||||
return file_path, False, error_msg
|
||||
|
||||
except Exception as e:
|
||||
error_msg = f"Exception: {str(e)}"
|
||||
logger.warning(error_msg)
|
||||
if attempt < retry_count - 1:
|
||||
logger.info(f"Nouvelle tentative dans {retry_delay} secondes...")
|
||||
time.sleep(retry_delay)
|
||||
else:
|
||||
return file_path, False, error_msg
|
||||
|
||||
return file_path, False, "Nombre maximum de tentatives atteint"
|
||||
|
||||
def main():
|
||||
"""Fonction principale."""
|
||||
args = parse_arguments()
|
||||
|
||||
# Préparation des extensions si spécifiées
|
||||
extensions = set(args.extensions) if args.extensions else SUPPORTED_EXTENSIONS
|
||||
# Assurer que les extensions commencent par un point
|
||||
extensions = {ext if ext.startswith('.') else f'.{ext}' for ext in extensions}
|
||||
|
||||
logger.info(f"Démarrage de l'injection automatique depuis {args.directory}")
|
||||
logger.info(f"URL PrivateGPT: {args.url}")
|
||||
logger.info(f"Mode récursif: {args.recursive}")
|
||||
logger.info(f"Extensions: {', '.join(extensions)}")
|
||||
|
||||
# Trouver les fichiers
|
||||
files = find_files(args.directory, args.recursive, extensions)
|
||||
total_files = len(files)
|
||||
|
||||
if total_files == 0:
|
||||
logger.warning(f"Aucun fichier trouvé avec les extensions {', '.join(extensions)} dans {args.directory}")
|
||||
return
|
||||
|
||||
logger.info(f"Trouvé {total_files} fichiers à injecter")
|
||||
|
||||
# Statistiques
|
||||
successful = 0
|
||||
failed = 0
|
||||
failed_files = []
|
||||
|
||||
# Injection des fichiers en parallèle
|
||||
with ThreadPoolExecutor(max_workers=args.threads) as executor:
|
||||
futures = {
|
||||
executor.submit(
|
||||
ingest_file,
|
||||
file_path,
|
||||
args.url,
|
||||
args.timeout,
|
||||
args.retry,
|
||||
args.retry_delay
|
||||
): file_path for file_path in files
|
||||
}
|
||||
|
||||
for future in as_completed(futures):
|
||||
file_path, success, message = future.result()
|
||||
if success:
|
||||
successful += 1
|
||||
else:
|
||||
failed += 1
|
||||
failed_files.append((file_path, message))
|
||||
|
||||
# Afficher la progression
|
||||
progress = (successful + failed) / total_files * 100
|
||||
logger.info(f"Progression: {progress:.1f}% ({successful + failed}/{total_files})")
|
||||
|
||||
# Rapport final
|
||||
logger.info("="*50)
|
||||
logger.info("RAPPORT D'INJECTION")
|
||||
logger.info("="*50)
|
||||
logger.info(f"Total des fichiers: {total_files}")
|
||||
logger.info(f"Succès: {successful}")
|
||||
logger.info(f"Échecs: {failed}")
|
||||
|
||||
if failed > 0:
|
||||
logger.info("\nDétails des échecs:")
|
||||
for file_path, message in failed_files:
|
||||
logger.info(f"- {file_path}: {message}")
|
||||
|
||||
logger.info("="*50)
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
main()
|
||||
except KeyboardInterrupt:
|
||||
logger.info("\nInterruption par l'utilisateur. Arrêt du processus.")
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
logger.error(f"Erreur non gérée: {str(e)}", exc_info=True)
|
||||
sys.exit(1)
|
||||
90
IA/02 - injection_fiches/auto_ingest.sh
Executable file
90
IA/02 - injection_fiches/auto_ingest.sh
Executable file
@ -0,0 +1,90 @@
|
||||
#!/bin/bash
|
||||
# Script d'exécution pour auto_ingest.py
|
||||
|
||||
# Vérification des dépendances
|
||||
check_dependencies() {
|
||||
# Vérifier Python
|
||||
if ! command -v python3 &> /dev/null; then
|
||||
echo "Erreur: Python 3 n'est pas installé ou n'est pas dans le PATH"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Vérifier pip et requests
|
||||
if ! python3 -c "import requests" &> /dev/null; then
|
||||
echo "Installation de la bibliothèque requests..."
|
||||
pip3 install requests
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Erreur: Impossible d'installer la bibliothèque requests"
|
||||
echo "Exécutez manuellement: pip3 install requests"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# Fonction d'aide
|
||||
show_help() {
|
||||
echo "Script d'injection automatique pour PrivateGPT"
|
||||
echo ""
|
||||
echo "Usage: $0 [options] -d RÉPERTOIRE"
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " -d, --directory RÉPERTOIRE Répertoire contenant les fichiers à injecter (obligatoire)"
|
||||
echo " -u, --url URL URL de l'API PrivateGPT (défaut: http://localhost:8001)"
|
||||
echo " -r, --recursive Parcourir récursivement les sous-répertoires"
|
||||
echo " -t, --threads N Nombre de threads pour les injections parallèles (défaut: 5)"
|
||||
echo " --retry N Nombre de tentatives en cas d'échec (défaut: 3)"
|
||||
echo " --retry-delay N Délai entre les tentatives en secondes (défaut: 5)"
|
||||
echo " --timeout N Délai d'attente pour chaque requête en secondes (défaut: 300)"
|
||||
echo " --extensions EXT1 EXT2 ... Liste d'extensions spécifiques à injecter"
|
||||
echo " -h, --help Afficher cette aide"
|
||||
echo ""
|
||||
echo "Exemple: $0 -d /documents -r --extensions pdf docx"
|
||||
}
|
||||
|
||||
# Vérifier si aucun argument n'est fourni
|
||||
if [ $# -eq 0 ]; then
|
||||
show_help
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Vérifier l'argument d'aide
|
||||
for arg in "$@"; do
|
||||
if [ "$arg" = "-h" ] || [ "$arg" = "--help" ]; then
|
||||
show_help
|
||||
exit 0
|
||||
fi
|
||||
done
|
||||
|
||||
# Vérifier la présence de l'argument obligatoire (-d ou --directory)
|
||||
directory_specified=false
|
||||
for ((i=1; i<=$#; i++)); do
|
||||
if [ "${!i}" = "-d" ] || [ "${!i}" = "--directory" ]; then
|
||||
directory_specified=true
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$directory_specified" = false ]; then
|
||||
echo "Erreur: L'option -d/--directory est obligatoire"
|
||||
echo "Utilisez -h ou --help pour afficher l'aide"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Vérifier les dépendances
|
||||
check_dependencies
|
||||
|
||||
# Chemin au script Python
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PYTHON_SCRIPT="${SCRIPT_DIR}/auto_ingest.py"
|
||||
|
||||
# Vérifier que le script Python existe
|
||||
if [ ! -f "$PYTHON_SCRIPT" ]; then
|
||||
echo "Erreur: Le script auto_ingest.py n'existe pas dans $SCRIPT_DIR"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Rendre le script Python exécutable
|
||||
chmod +x "$PYTHON_SCRIPT"
|
||||
|
||||
# Exécuter le script Python avec tous les arguments
|
||||
python3 "$PYTHON_SCRIPT" "$@"
|
||||
40
IA/02 - injection_fiches/docker-compose.yml.example
Normal file
40
IA/02 - injection_fiches/docker-compose.yml.example
Normal file
@ -0,0 +1,40 @@
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
privategpt:
|
||||
image: ghcr.io/zylon-ai/private-gpt:latest
|
||||
container_name: privategpt
|
||||
ports:
|
||||
- "8001:8001"
|
||||
environment:
|
||||
- PGPT_PROFILES=local
|
||||
# Décommentez et modifiez ces variables si vous voulez utiliser un modèle différent
|
||||
# - PGPT_SETTINGS_LLMS_DEFAULT__MODEL=/models/custom-model.gguf
|
||||
# - PGPT_SETTINGS_EMBEDDING_DEFAULT__MODEL=/models/custom-embedding-model
|
||||
volumes:
|
||||
# Volume persistant pour les données
|
||||
- privategpt-data:/app/local_data
|
||||
# Montage du répertoire d'auto-injection
|
||||
- ./documents_to_ingest:/app/documents_to_ingest
|
||||
# Montage des modèles personnalisés (décommentez si nécessaire)
|
||||
# - ./custom_models:/app/models
|
||||
restart: unless-stopped
|
||||
# Décommentez ces lignes si vous avez un GPU NVIDIA
|
||||
# deploy:
|
||||
# resources:
|
||||
# reservations:
|
||||
# devices:
|
||||
# - driver: nvidia
|
||||
# count: 1
|
||||
# capabilities: [gpu]
|
||||
|
||||
volumes:
|
||||
privategpt-data:
|
||||
name: privategpt-data
|
||||
|
||||
# Instructions d'utilisation:
|
||||
# 1. Copiez ce fichier sous le nom "docker-compose.yml"
|
||||
# 2. Créez un répertoire "documents_to_ingest" à côté du fichier docker-compose.yml
|
||||
# 3. Placez vos documents à injecter dans ce répertoire
|
||||
# 4. Lancez avec la commande: docker-compose up -d
|
||||
# 5. Utilisez le script d'injection: ./auto_ingest.sh -d documents_to_ingest -u http://localhost:8001
|
||||
263
IA/02 - injection_fiches/watch_directory.py
Executable file
263
IA/02 - injection_fiches/watch_directory.py
Executable file
@ -0,0 +1,263 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Script de surveillance de répertoire pour l'injection automatique dans PrivateGPT
|
||||
|
||||
Ce script surveille un répertoire et injecte automatiquement les nouveaux fichiers
|
||||
dans PrivateGPT dès qu'ils sont ajoutés ou modifiés.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import logging
|
||||
import argparse
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
from typing import Set, Dict, List
|
||||
from datetime import datetime
|
||||
|
||||
try:
|
||||
from watchdog.observers import Observer
|
||||
from watchdog.events import FileSystemEventHandler, FileCreatedEvent, FileModifiedEvent
|
||||
except ImportError:
|
||||
print("Bibliothèque 'watchdog' non installée. Installation en cours...")
|
||||
subprocess.run([sys.executable, "-m", "pip", "install", "watchdog"])
|
||||
from watchdog.observers import Observer
|
||||
from watchdog.events import FileSystemEventHandler, FileCreatedEvent, FileModifiedEvent
|
||||
|
||||
# Configuration du logging
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(levelname)s - %(message)s',
|
||||
handlers=[
|
||||
logging.StreamHandler(),
|
||||
logging.FileHandler("pgpt_watch_directory.log")
|
||||
]
|
||||
)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Extensions de fichiers couramment supportées par PrivateGPT
|
||||
SUPPORTED_EXTENSIONS = {
|
||||
'.pdf', '.txt', '.md', '.doc', '.docx', '.ppt', '.pptx',
|
||||
'.xls', '.xlsx', '.csv', '.epub', '.html', '.htm'
|
||||
}
|
||||
|
||||
class DocumentHandler(FileSystemEventHandler):
|
||||
"""Gestionnaire d'événements pour les fichiers de documents."""
|
||||
|
||||
def __init__(self, watch_dir: str, ingest_script: str, pgpt_url: str,
|
||||
extensions: Set[str], delay: int = 5):
|
||||
"""
|
||||
Initialise le gestionnaire d'événements.
|
||||
|
||||
Args:
|
||||
watch_dir: Répertoire à surveiller
|
||||
ingest_script: Chemin vers le script d'injection
|
||||
pgpt_url: URL de l'API PrivateGPT
|
||||
extensions: Extensions de fichiers à traiter
|
||||
delay: Délai en secondes à attendre avant le traitement (évite de traiter des fichiers partiellement écrits)
|
||||
"""
|
||||
self.watch_dir = os.path.abspath(watch_dir)
|
||||
self.ingest_script = os.path.abspath(ingest_script)
|
||||
self.pgpt_url = pgpt_url
|
||||
self.extensions = extensions
|
||||
self.delay = delay
|
||||
|
||||
# Queue pour les fichiers en attente de traitement
|
||||
self.pending_files: Dict[str, float] = {}
|
||||
|
||||
# Vérifier que le script d'injection existe
|
||||
if not os.path.exists(self.ingest_script):
|
||||
logger.error(f"Le script d'injection {self.ingest_script} n'existe pas!")
|
||||
raise FileNotFoundError(f"Script d'injection introuvable: {self.ingest_script}")
|
||||
|
||||
def on_created(self, event):
|
||||
"""Appelé lorsqu'un fichier est créé."""
|
||||
if not event.is_directory:
|
||||
self._handle_file_event(event)
|
||||
|
||||
def on_modified(self, event):
|
||||
"""Appelé lorsqu'un fichier est modifié."""
|
||||
if not event.is_directory:
|
||||
self._handle_file_event(event)
|
||||
|
||||
def _handle_file_event(self, event):
|
||||
"""Traite un événement de fichier (création ou modification)."""
|
||||
file_path = event.src_path
|
||||
file_ext = os.path.splitext(file_path)[1].lower()
|
||||
|
||||
# Ignorer les fichiers non supportés
|
||||
if file_ext not in self.extensions:
|
||||
return
|
||||
|
||||
# Ignorer les fichiers temporaires et cachés
|
||||
file_name = os.path.basename(file_path)
|
||||
if file_name.startswith('.') or file_name.startswith('~') or file_name.endswith('.tmp'):
|
||||
return
|
||||
|
||||
# Ajouter à la queue avec l'horodatage actuel
|
||||
self.pending_files[file_path] = time.time()
|
||||
logger.info(f"Fichier détecté: {file_path} (en attente pendant {self.delay} secondes)")
|
||||
|
||||
def process_pending_files(self):
|
||||
"""Traite les fichiers en attente qui ont dépassé le délai d'attente."""
|
||||
current_time = time.time()
|
||||
files_to_process: List[str] = []
|
||||
|
||||
# Identifier les fichiers prêts à être traités
|
||||
for file_path, timestamp in list(self.pending_files.items()):
|
||||
if current_time - timestamp >= self.delay:
|
||||
if os.path.exists(file_path): # Vérifier que le fichier existe toujours
|
||||
files_to_process.append(file_path)
|
||||
self.pending_files.pop(file_path)
|
||||
|
||||
# Traiter les fichiers par lot
|
||||
if files_to_process:
|
||||
self._ingest_files(files_to_process)
|
||||
|
||||
def _ingest_files(self, files: List[str]):
|
||||
"""
|
||||
Injecte une liste de fichiers en utilisant le script d'injection.
|
||||
|
||||
Args:
|
||||
files: Liste des chemins de fichiers à injecter
|
||||
"""
|
||||
try:
|
||||
# Créer un répertoire temporaire pour stocker la liste des fichiers
|
||||
temp_dir = os.path.join(os.path.dirname(self.ingest_script), "temp")
|
||||
os.makedirs(temp_dir, exist_ok=True)
|
||||
|
||||
# Créer un fichier de liste
|
||||
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
list_file = os.path.join(temp_dir, f"files_to_ingest_{timestamp}.txt")
|
||||
|
||||
with open(list_file, "w") as f:
|
||||
for file_path in files:
|
||||
f.write(f"{file_path}\n")
|
||||
|
||||
# Construire la commande pour le script d'injection
|
||||
for file_path in files:
|
||||
file_dir = os.path.dirname(file_path)
|
||||
logger.info(f"Injection de {file_path}...")
|
||||
|
||||
cmd = [
|
||||
sys.executable,
|
||||
self.ingest_script,
|
||||
"-d", file_dir,
|
||||
"-u", self.pgpt_url,
|
||||
"--extensions", os.path.splitext(file_path)[1][1:] # Extension sans le point
|
||||
]
|
||||
|
||||
# Exécuter la commande
|
||||
process = subprocess.run(
|
||||
cmd,
|
||||
capture_output=True,
|
||||
text=True
|
||||
)
|
||||
|
||||
if process.returncode == 0:
|
||||
logger.info(f"Injection réussie de {file_path}")
|
||||
else:
|
||||
logger.error(f"Échec de l'injection de {file_path}: {process.stderr}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Erreur lors de l'injection des fichiers: {str(e)}")
|
||||
|
||||
def parse_arguments():
|
||||
"""Parse les arguments de ligne de commande."""
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Surveille un répertoire et injecte automatiquement les nouveaux fichiers dans PrivateGPT"
|
||||
)
|
||||
parser.add_argument(
|
||||
"-d", "--directory",
|
||||
type=str,
|
||||
required=True,
|
||||
help="Chemin du répertoire à surveiller"
|
||||
)
|
||||
parser.add_argument(
|
||||
"-s", "--script",
|
||||
type=str,
|
||||
default=None,
|
||||
help="Chemin vers le script auto_ingest.py (par défaut: détection automatique)"
|
||||
)
|
||||
parser.add_argument(
|
||||
"-u", "--url",
|
||||
type=str,
|
||||
default="http://localhost:8001",
|
||||
help="URL de l'API PrivateGPT (défaut: http://localhost:8001)"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--delay",
|
||||
type=int,
|
||||
default=5,
|
||||
help="Délai en secondes avant de traiter un nouveau fichier (défaut: 5)"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--extensions",
|
||||
nargs="+",
|
||||
help="Liste d'extensions spécifiques à surveiller (ex: pdf txt)"
|
||||
)
|
||||
|
||||
return parser.parse_args()
|
||||
|
||||
def main():
|
||||
"""Fonction principale."""
|
||||
args = parse_arguments()
|
||||
|
||||
# Préparation des extensions si spécifiées
|
||||
extensions = set(args.extensions) if args.extensions else SUPPORTED_EXTENSIONS
|
||||
# Assurer que les extensions commencent par un point
|
||||
extensions = {ext if ext.startswith('.') else f'.{ext}' for ext in extensions}
|
||||
|
||||
# Déterminer le chemin du script d'injection
|
||||
if args.script:
|
||||
ingest_script = args.script
|
||||
else:
|
||||
# Utiliser le script auto_ingest.py dans le même répertoire que ce script
|
||||
ingest_script = os.path.join(os.path.dirname(os.path.abspath(__file__)), "auto_ingest.py")
|
||||
|
||||
# Créer le répertoire de surveillance s'il n'existe pas
|
||||
watch_dir = os.path.abspath(args.directory)
|
||||
if not os.path.exists(watch_dir):
|
||||
logger.info(f"Création du répertoire de surveillance: {watch_dir}")
|
||||
os.makedirs(watch_dir, exist_ok=True)
|
||||
|
||||
logger.info(f"Démarrage de la surveillance de {watch_dir}")
|
||||
logger.info(f"URL PrivateGPT: {args.url}")
|
||||
logger.info(f"Extensions surveillées: {', '.join(extensions)}")
|
||||
logger.info(f"Délai de traitement: {args.delay} secondes")
|
||||
|
||||
# Initialiser le gestionnaire et l'observateur
|
||||
event_handler = DocumentHandler(
|
||||
watch_dir=watch_dir,
|
||||
ingest_script=ingest_script,
|
||||
pgpt_url=args.url,
|
||||
extensions=extensions,
|
||||
delay=args.delay
|
||||
)
|
||||
|
||||
observer = Observer()
|
||||
observer.schedule(event_handler, path=watch_dir, recursive=True)
|
||||
observer.start()
|
||||
|
||||
try:
|
||||
logger.info("Surveillance en cours... (Ctrl+C pour quitter)")
|
||||
|
||||
while True:
|
||||
# Traiter les fichiers en attente
|
||||
event_handler.process_pending_files()
|
||||
time.sleep(1)
|
||||
|
||||
except KeyboardInterrupt:
|
||||
logger.info("\nInterruption par l'utilisateur. Arrêt de la surveillance.")
|
||||
observer.stop()
|
||||
|
||||
observer.join()
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
main()
|
||||
except Exception as e:
|
||||
logger.error(f"Erreur non gérée: {str(e)}", exc_info=True)
|
||||
sys.exit(1)
|
||||
94
IA/02 - injection_fiches/watch_directory.sh
Executable file
94
IA/02 - injection_fiches/watch_directory.sh
Executable file
@ -0,0 +1,94 @@
|
||||
#!/bin/bash
|
||||
# Script d'exécution pour watch_directory.py
|
||||
|
||||
# Vérification des dépendances
|
||||
check_dependencies() {
|
||||
# Vérifier Python
|
||||
if ! command -v python3 &> /dev/null; then
|
||||
echo "Erreur: Python 3 n'est pas installé ou n'est pas dans le PATH"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Vérifier les bibliothèques Python requises
|
||||
python3 -c "
|
||||
try:
|
||||
import watchdog
|
||||
except ImportError:
|
||||
print('Installation de la bibliothèque watchdog...')
|
||||
import subprocess, sys
|
||||
subprocess.run([sys.executable, '-m', 'pip', 'install', 'watchdog'])
|
||||
" || {
|
||||
echo "Erreur: Impossible d'installer les dépendances"
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
# Fonction d'aide
|
||||
show_help() {
|
||||
echo "Script de surveillance de répertoire pour PrivateGPT"
|
||||
echo ""
|
||||
echo "Usage: $0 [options] -d RÉPERTOIRE"
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " -d, --directory RÉPERTOIRE Répertoire à surveiller (obligatoire)"
|
||||
echo " -s, --script CHEMIN Chemin vers le script auto_ingest.py (facultatif)"
|
||||
echo " -u, --url URL URL de l'API PrivateGPT (défaut: http://localhost:8001)"
|
||||
echo " --delay N Délai en secondes avant de traiter un nouveau fichier (défaut: 5)"
|
||||
echo " --extensions EXT1 EXT2 ... Liste d'extensions spécifiques à surveiller"
|
||||
echo " -h, --help Afficher cette aide"
|
||||
echo ""
|
||||
echo "Exemple: $0 -d /documents/à/surveiller --extensions pdf docx"
|
||||
}
|
||||
|
||||
# Vérifier si aucun argument n'est fourni
|
||||
if [ $# -eq 0 ]; then
|
||||
show_help
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Vérifier l'argument d'aide
|
||||
for arg in "$@"; do
|
||||
if [ "$arg" = "-h" ] || [ "$arg" = "--help" ]; then
|
||||
show_help
|
||||
exit 0
|
||||
fi
|
||||
done
|
||||
|
||||
# Vérifier la présence de l'argument obligatoire (-d ou --directory)
|
||||
directory_specified=false
|
||||
for ((i=1; i<=$#; i++)); do
|
||||
if [ "${!i}" = "-d" ] || [ "${!i}" = "--directory" ]; then
|
||||
directory_specified=true
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$directory_specified" = false ]; then
|
||||
echo "Erreur: L'option -d/--directory est obligatoire"
|
||||
echo "Utilisez -h ou --help pour afficher l'aide"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Vérifier les dépendances
|
||||
check_dependencies
|
||||
|
||||
# Chemin au script Python
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PYTHON_SCRIPT="${SCRIPT_DIR}/watch_directory.py"
|
||||
|
||||
# Vérifier que le script Python existe
|
||||
if [ ! -f "$PYTHON_SCRIPT" ]; then
|
||||
echo "Erreur: Le script watch_directory.py n'existe pas dans $SCRIPT_DIR"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Rendre le script Python exécutable
|
||||
chmod +x "$PYTHON_SCRIPT"
|
||||
|
||||
# Message d'information sur l'arrêt
|
||||
echo "Démarrage de la surveillance..."
|
||||
echo "Appuyez sur Ctrl+C pour arrêter la surveillance"
|
||||
echo ""
|
||||
|
||||
# Exécuter le script Python avec tous les arguments
|
||||
python3 "$PYTHON_SCRIPT" "$@"
|
||||
Loading…
x
Reference in New Issue
Block a user