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