- Frontend v4 accessible sur réseau local (192.168.1.40) - Ports ouverts: 3002 (frontend), 5001 (backend), 5004 (dashboard) - Ollama GPU fonctionnel - Self-healing interactif - Dashboard confiance Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
653 lines
24 KiB
Python
653 lines
24 KiB
Python
"""
|
|
Configuration centralisée pour RPA Vision V3
|
|
|
|
Gestionnaire de configuration unifié qui élimine les incohérences entre composants.
|
|
Utilise les variables d'environnement avec des valeurs par défaut sensées.
|
|
En production, définir ENVIRONMENT=production pour forcer la configuration.
|
|
"""
|
|
|
|
import os
|
|
import logging
|
|
from pathlib import Path
|
|
from dataclasses import dataclass, field
|
|
from typing import Optional, List, Dict, Any, Callable
|
|
import json
|
|
from datetime import datetime
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
@dataclass
|
|
class ValidationError:
|
|
"""Erreur de validation de configuration"""
|
|
field: str
|
|
value: Any
|
|
message: str
|
|
severity: str = "error" # error, warning
|
|
|
|
|
|
@dataclass
|
|
class SystemConfig:
|
|
"""Configuration système unifiée - Point central de toute configuration"""
|
|
# Chemins unifiés
|
|
base_path: Path = field(default_factory=lambda: Path.cwd())
|
|
data_path: Path = field(default_factory=lambda: Path("data"))
|
|
logs_path: Path = field(default_factory=lambda: Path("logs"))
|
|
|
|
# Services
|
|
api_host: str = "0.0.0.0"
|
|
api_port: int = 8000
|
|
dashboard_host: str = "0.0.0.0"
|
|
dashboard_port: int = 5001
|
|
worker_threads: int = 4
|
|
|
|
# Base de données
|
|
sessions_path: Path = field(default_factory=lambda: Path("data/sessions"))
|
|
workflows_path: Path = field(default_factory=lambda: Path("data/workflows"))
|
|
embeddings_path: Path = field(default_factory=lambda: Path("data/embeddings"))
|
|
faiss_index_path: Path = field(default_factory=lambda: Path("data/faiss_index"))
|
|
screenshots_path: Path = field(default_factory=lambda: Path("data/screenshots"))
|
|
training_path: Path = field(default_factory=lambda: Path("data/training"))
|
|
uploads_path: Path = field(default_factory=lambda: Path("data/training/uploads"))
|
|
|
|
# Sécurité
|
|
secret_key: str = "dev_secret_key_not_for_production"
|
|
encryption_password: str = "dev_default_key_not_for_production"
|
|
auth_enabled: bool = True
|
|
allowed_origins: List[str] = field(default_factory=lambda: ["*"])
|
|
|
|
# Monitoring
|
|
health_check_interval: int = 30
|
|
metrics_enabled: bool = True
|
|
|
|
# Environment
|
|
environment: str = "development"
|
|
debug: bool = False
|
|
|
|
# Models
|
|
clip_model: str = "ViT-B-32"
|
|
clip_pretrained: str = "openai"
|
|
clip_device: str = "cpu"
|
|
vlm_model: str = "qwen3-vl:8b"
|
|
vlm_endpoint: str = "http://localhost:11434"
|
|
owl_model: str = "google/owlv2-base-patch16-ensemble"
|
|
owl_confidence_threshold: float = 0.1
|
|
|
|
# FAISS
|
|
faiss_dimensions: int = 512
|
|
faiss_index_type: str = "Flat"
|
|
faiss_metric: str = "cosine"
|
|
faiss_nprobe: int = 8
|
|
faiss_auto_optimize: bool = True
|
|
faiss_migration_threshold: int = 10000
|
|
|
|
# GPU
|
|
gpu_idle_timeout_seconds: int = 300
|
|
gpu_vram_threshold_mb: int = 1024
|
|
gpu_max_load_retries: int = 3
|
|
gpu_load_timeout_seconds: int = 30
|
|
gpu_unload_timeout_seconds: int = 5
|
|
|
|
def __post_init__(self):
|
|
"""Normalise les chemins après initialisation"""
|
|
# Convertir tous les chemins relatifs en absolus basés sur base_path
|
|
for field_name in self.__dataclass_fields__:
|
|
value = getattr(self, field_name)
|
|
if isinstance(value, Path) and not value.is_absolute():
|
|
if field_name != 'base_path':
|
|
setattr(self, field_name, self.base_path / value)
|
|
|
|
def ensure_directories(self):
|
|
"""Crée tous les répertoires nécessaires avec les bonnes permissions"""
|
|
directories = [
|
|
self.data_path, self.logs_path, self.sessions_path,
|
|
self.workflows_path, self.embeddings_path, self.faiss_index_path,
|
|
self.screenshots_path, self.training_path, self.uploads_path
|
|
]
|
|
|
|
for directory in directories:
|
|
try:
|
|
directory.mkdir(parents=True, exist_ok=True)
|
|
# Vérifier les permissions d'écriture
|
|
test_file = directory / ".write_test"
|
|
test_file.touch()
|
|
test_file.unlink()
|
|
logger.debug(f"Directory created/verified: {directory}")
|
|
except Exception as e:
|
|
logger.error(f"Failed to create/verify directory {directory}: {e}")
|
|
raise
|
|
|
|
|
|
class ConfigurationManager:
|
|
"""Gestionnaire centralisé de configuration - Point unique de vérité"""
|
|
|
|
def __init__(self):
|
|
self._config: Optional[SystemConfig] = None
|
|
self._config_watchers: List[Callable[[SystemConfig], None]] = []
|
|
self._last_loaded: Optional[datetime] = None
|
|
|
|
def load_config(self) -> SystemConfig:
|
|
"""Charge la configuration depuis les variables d'environnement et fichiers"""
|
|
try:
|
|
# Charger depuis les variables d'environnement
|
|
config = self._load_from_env()
|
|
|
|
# Charger depuis le fichier de config si présent
|
|
config_file = Path(".env")
|
|
if config_file.exists():
|
|
config = self._merge_from_file(config, config_file)
|
|
|
|
# Valider la configuration
|
|
validation_errors = self.validate_config(config)
|
|
if any(error.severity == "error" for error in validation_errors):
|
|
error_messages = [f"{error.field}: {error.message}"
|
|
for error in validation_errors if error.severity == "error"]
|
|
raise ValueError(f"Configuration validation failed: {'; '.join(error_messages)}")
|
|
|
|
# Afficher les warnings
|
|
warnings = [error for error in validation_errors if error.severity == "warning"]
|
|
for warning in warnings:
|
|
logger.warning(f"Configuration warning - {warning.field}: {warning.message}")
|
|
|
|
# Créer les répertoires
|
|
config.ensure_directories()
|
|
|
|
self._config = config
|
|
self._last_loaded = datetime.now()
|
|
|
|
# Notifier les watchers
|
|
for watcher in self._config_watchers:
|
|
try:
|
|
watcher(config)
|
|
except Exception as e:
|
|
logger.error(f"Configuration watcher failed: {e}")
|
|
|
|
logger.info(f"Configuration loaded successfully (environment: {config.environment})")
|
|
return config
|
|
|
|
except Exception as e:
|
|
logger.error(f"Failed to load configuration: {e}")
|
|
raise
|
|
|
|
def _load_from_env(self) -> SystemConfig:
|
|
"""Charge la configuration depuis les variables d'environnement"""
|
|
base_path = Path(os.getenv("BASE_PATH", Path.cwd()))
|
|
|
|
return SystemConfig(
|
|
# Chemins
|
|
base_path=base_path,
|
|
data_path=Path(os.getenv("DATA_PATH", "data")),
|
|
logs_path=Path(os.getenv("LOGS_PATH", "logs")),
|
|
sessions_path=Path(os.getenv("SESSIONS_PATH", "data/sessions")),
|
|
workflows_path=Path(os.getenv("WORKFLOWS_PATH", "data/workflows")),
|
|
embeddings_path=Path(os.getenv("EMBEDDINGS_PATH", "data/embeddings")),
|
|
faiss_index_path=Path(os.getenv("FAISS_INDEX_PATH", "data/faiss_index")),
|
|
screenshots_path=Path(os.getenv("SCREENSHOTS_PATH", "data/screenshots")),
|
|
training_path=Path(os.getenv("TRAINING_PATH", "data/training")),
|
|
uploads_path=Path(os.getenv("UPLOADS_PATH", "data/training/uploads")),
|
|
|
|
# Services
|
|
api_host=os.getenv("API_HOST", "0.0.0.0"),
|
|
api_port=int(os.getenv("API_PORT", "8000")),
|
|
dashboard_host=os.getenv("DASHBOARD_HOST", "0.0.0.0"),
|
|
dashboard_port=int(os.getenv("DASHBOARD_PORT", "5001")),
|
|
worker_threads=int(os.getenv("WORKER_THREADS", "4")),
|
|
|
|
# Sécurité
|
|
secret_key=os.getenv("SECRET_KEY", "dev_secret_key_not_for_production"),
|
|
encryption_password=os.getenv("ENCRYPTION_PASSWORD", "dev_default_key_not_for_production"),
|
|
auth_enabled=os.getenv("AUTH_ENABLED", "true").lower() == "true",
|
|
allowed_origins=os.getenv("ALLOWED_ORIGINS", "*").split(","),
|
|
|
|
# Monitoring
|
|
health_check_interval=int(os.getenv("HEALTH_CHECK_INTERVAL", "30")),
|
|
metrics_enabled=os.getenv("METRICS_ENABLED", "true").lower() == "true",
|
|
|
|
# Environment
|
|
environment=os.getenv("ENVIRONMENT", "development"),
|
|
debug=os.getenv("DEBUG", "false").lower() == "true",
|
|
|
|
# Models
|
|
clip_model=os.getenv("CLIP_MODEL", "ViT-B-32"),
|
|
clip_pretrained=os.getenv("CLIP_PRETRAINED", "openai"),
|
|
clip_device=os.getenv("CLIP_DEVICE", "cpu"),
|
|
vlm_model=os.getenv("VLM_MODEL", "qwen3-vl:8b"),
|
|
vlm_endpoint=os.getenv("VLM_ENDPOINT", "http://localhost:11434"),
|
|
owl_model=os.getenv("OWL_MODEL", "google/owlv2-base-patch16-ensemble"),
|
|
owl_confidence_threshold=float(os.getenv("OWL_CONFIDENCE_THRESHOLD", "0.1")),
|
|
|
|
# FAISS
|
|
faiss_dimensions=int(os.getenv("FAISS_DIMENSIONS", "512")),
|
|
faiss_index_type=os.getenv("FAISS_INDEX_TYPE", "Flat"),
|
|
faiss_metric=os.getenv("FAISS_METRIC", "cosine"),
|
|
faiss_nprobe=int(os.getenv("FAISS_NPROBE", "8")),
|
|
faiss_auto_optimize=os.getenv("FAISS_AUTO_OPTIMIZE", "true").lower() == "true",
|
|
faiss_migration_threshold=int(os.getenv("FAISS_MIGRATION_THRESHOLD", "10000")),
|
|
|
|
# GPU
|
|
gpu_idle_timeout_seconds=int(os.getenv("GPU_IDLE_TIMEOUT", "300")),
|
|
gpu_vram_threshold_mb=int(os.getenv("GPU_VRAM_THRESHOLD_MB", "1024")),
|
|
gpu_max_load_retries=int(os.getenv("GPU_MAX_LOAD_RETRIES", "3")),
|
|
gpu_load_timeout_seconds=int(os.getenv("GPU_LOAD_TIMEOUT", "30")),
|
|
gpu_unload_timeout_seconds=int(os.getenv("GPU_UNLOAD_TIMEOUT", "5"))
|
|
)
|
|
|
|
def _merge_from_file(self, config: SystemConfig, config_file: Path) -> SystemConfig:
|
|
"""Merge configuration from file (future enhancement)"""
|
|
# Pour l'instant, on utilise seulement les variables d'environnement
|
|
# Cette méthode peut être étendue pour supporter les fichiers JSON/YAML
|
|
return config
|
|
|
|
def validate_config(self, config: SystemConfig) -> List[ValidationError]:
|
|
"""Valide la cohérence de la configuration"""
|
|
errors = []
|
|
|
|
# Validation de l'environnement de production
|
|
if config.environment == "production":
|
|
if config.secret_key == "dev_secret_key_not_for_production":
|
|
errors.append(ValidationError(
|
|
"secret_key", config.secret_key,
|
|
"SECRET_KEY must be set in production environment"
|
|
))
|
|
|
|
if config.encryption_password == "dev_default_key_not_for_production":
|
|
errors.append(ValidationError(
|
|
"encryption_password", config.encryption_password,
|
|
"ENCRYPTION_PASSWORD must be set in production environment"
|
|
))
|
|
|
|
if config.debug:
|
|
errors.append(ValidationError(
|
|
"debug", config.debug,
|
|
"DEBUG should be false in production environment",
|
|
"warning"
|
|
))
|
|
|
|
# Validation des ports
|
|
if config.api_port == config.dashboard_port:
|
|
errors.append(ValidationError(
|
|
"ports", f"api:{config.api_port}, dashboard:{config.dashboard_port}",
|
|
"API and Dashboard ports must be different"
|
|
))
|
|
|
|
if not (1024 <= config.api_port <= 65535):
|
|
errors.append(ValidationError(
|
|
"api_port", config.api_port,
|
|
"API port must be between 1024 and 65535"
|
|
))
|
|
|
|
if not (1024 <= config.dashboard_port <= 65535):
|
|
errors.append(ValidationError(
|
|
"dashboard_port", config.dashboard_port,
|
|
"Dashboard port must be between 1024 and 65535"
|
|
))
|
|
|
|
# Validation des chemins
|
|
try:
|
|
config.base_path.resolve()
|
|
except Exception as e:
|
|
errors.append(ValidationError(
|
|
"base_path", config.base_path,
|
|
f"Invalid base path: {e}"
|
|
))
|
|
|
|
# Validation des modèles
|
|
if config.faiss_dimensions <= 0:
|
|
errors.append(ValidationError(
|
|
"faiss_dimensions", config.faiss_dimensions,
|
|
"FAISS dimensions must be positive"
|
|
))
|
|
|
|
if config.worker_threads <= 0:
|
|
errors.append(ValidationError(
|
|
"worker_threads", config.worker_threads,
|
|
"Worker threads must be positive"
|
|
))
|
|
|
|
# Validation des timeouts
|
|
if config.health_check_interval <= 0:
|
|
errors.append(ValidationError(
|
|
"health_check_interval", config.health_check_interval,
|
|
"Health check interval must be positive"
|
|
))
|
|
|
|
return errors
|
|
|
|
def apply_config(self, config: SystemConfig):
|
|
"""Applique une nouvelle configuration"""
|
|
# Valider d'abord
|
|
validation_errors = self.validate_config(config)
|
|
if any(error.severity == "error" for error in validation_errors):
|
|
error_messages = [f"{error.field}: {error.message}"
|
|
for error in validation_errors if error.severity == "error"]
|
|
raise ValueError(f"Configuration validation failed: {'; '.join(error_messages)}")
|
|
|
|
# Créer les répertoires
|
|
config.ensure_directories()
|
|
|
|
# Appliquer la configuration
|
|
old_config = self._config
|
|
self._config = config
|
|
self._last_loaded = datetime.now()
|
|
|
|
# Notifier les watchers du changement
|
|
for watcher in self._config_watchers:
|
|
try:
|
|
watcher(config)
|
|
except Exception as e:
|
|
logger.error(f"Configuration watcher failed during apply: {e}")
|
|
# En cas d'erreur, restaurer l'ancienne configuration
|
|
self._config = old_config
|
|
raise
|
|
|
|
logger.info("Configuration applied successfully")
|
|
|
|
def watch_config_changes(self, callback: Callable[[SystemConfig], None]):
|
|
"""Enregistre un callback pour les changements de configuration"""
|
|
self._config_watchers.append(callback)
|
|
|
|
# Si on a déjà une configuration, appeler immédiatement le callback
|
|
if self._config:
|
|
try:
|
|
callback(self._config)
|
|
except Exception as e:
|
|
logger.error(f"Configuration watcher failed during registration: {e}")
|
|
|
|
def get_config(self) -> SystemConfig:
|
|
"""Récupère la configuration actuelle"""
|
|
if self._config is None:
|
|
return self.load_config()
|
|
return self._config
|
|
|
|
def reload_config(self) -> SystemConfig:
|
|
"""Recharge la configuration depuis les sources"""
|
|
return self.load_config()
|
|
|
|
|
|
# Instance globale du gestionnaire de configuration
|
|
_config_manager: Optional[ConfigurationManager] = None
|
|
|
|
|
|
def get_configuration_manager() -> ConfigurationManager:
|
|
"""Récupère le gestionnaire de configuration global (singleton)"""
|
|
global _config_manager
|
|
if _config_manager is None:
|
|
_config_manager = ConfigurationManager()
|
|
return _config_manager
|
|
|
|
|
|
def get_config() -> SystemConfig:
|
|
"""Récupère la configuration système unifiée"""
|
|
return get_configuration_manager().get_config()
|
|
|
|
|
|
def reload_config() -> SystemConfig:
|
|
"""Recharge la configuration système"""
|
|
return get_configuration_manager().reload_config()
|
|
|
|
|
|
# Backward compatibility - Keep old classes for gradual migration
|
|
@dataclass
|
|
class ServerConfig:
|
|
"""Configuration du serveur - DEPRECATED: Use SystemConfig instead"""
|
|
api_host: str = "0.0.0.0"
|
|
api_port: int = 8000
|
|
dashboard_host: str = "0.0.0.0"
|
|
dashboard_port: int = 5001
|
|
environment: str = "development"
|
|
debug: bool = False
|
|
|
|
@classmethod
|
|
def from_env(cls) -> 'ServerConfig':
|
|
logger.warning("ServerConfig is deprecated. Use SystemConfig via get_config() instead.")
|
|
config = get_config()
|
|
return cls(
|
|
api_host=config.api_host,
|
|
api_port=config.api_port,
|
|
dashboard_host=config.dashboard_host,
|
|
dashboard_port=config.dashboard_port,
|
|
environment=config.environment,
|
|
debug=config.debug
|
|
)
|
|
|
|
|
|
@dataclass
|
|
class SecurityConfig:
|
|
"""Configuration de sécurité - DEPRECATED: Use SystemConfig instead"""
|
|
encryption_password: Optional[str] = None
|
|
secret_key: Optional[str] = None
|
|
allowed_origins: List[str] = field(default_factory=lambda: ["*"])
|
|
|
|
@classmethod
|
|
def from_env(cls, environment: str = "development") -> 'SecurityConfig':
|
|
logger.warning("SecurityConfig is deprecated. Use SystemConfig via get_config() instead.")
|
|
config = get_config()
|
|
return cls(
|
|
encryption_password=config.encryption_password,
|
|
secret_key=config.secret_key,
|
|
allowed_origins=config.allowed_origins
|
|
)
|
|
|
|
|
|
@dataclass
|
|
class ModelConfig:
|
|
"""Configuration des modèles ML - DEPRECATED: Use SystemConfig instead"""
|
|
clip_model: str = "ViT-B-32"
|
|
clip_pretrained: str = "openai"
|
|
clip_device: str = "cpu"
|
|
vlm_model: str = "qwen3-vl:8b"
|
|
vlm_endpoint: str = "http://localhost:11434"
|
|
owl_model: str = "google/owlv2-base-patch16-ensemble"
|
|
owl_confidence_threshold: float = 0.1
|
|
|
|
@classmethod
|
|
def from_env(cls) -> 'ModelConfig':
|
|
logger.warning("ModelConfig is deprecated. Use SystemConfig via get_config() instead.")
|
|
config = get_config()
|
|
return cls(
|
|
clip_model=config.clip_model,
|
|
clip_pretrained=config.clip_pretrained,
|
|
clip_device=config.clip_device,
|
|
vlm_model=config.vlm_model,
|
|
vlm_endpoint=config.vlm_endpoint,
|
|
owl_model=config.owl_model,
|
|
owl_confidence_threshold=config.owl_confidence_threshold
|
|
)
|
|
|
|
|
|
@dataclass
|
|
class PathConfig:
|
|
"""Configuration des chemins - DEPRECATED: Use SystemConfig instead"""
|
|
data_path: Path = field(default_factory=lambda: Path("data"))
|
|
models_path: Path = field(default_factory=lambda: Path("models"))
|
|
logs_path: Path = field(default_factory=lambda: Path("logs"))
|
|
uploads_path: Path = field(default_factory=lambda: Path("data/training/uploads"))
|
|
sessions_path: Path = field(default_factory=lambda: Path("data/training/sessions"))
|
|
|
|
@classmethod
|
|
def from_env(cls) -> 'PathConfig':
|
|
logger.warning("PathConfig is deprecated. Use SystemConfig via get_config() instead.")
|
|
config = get_config()
|
|
return cls(
|
|
data_path=config.data_path,
|
|
models_path=Path("models"), # Not in SystemConfig yet
|
|
logs_path=config.logs_path,
|
|
uploads_path=config.uploads_path,
|
|
sessions_path=config.sessions_path
|
|
)
|
|
|
|
def ensure_directories(self):
|
|
"""Crée tous les répertoires nécessaires"""
|
|
config = get_config()
|
|
config.ensure_directories()
|
|
|
|
|
|
@dataclass
|
|
class FAISSConfig:
|
|
"""Configuration FAISS - DEPRECATED: Use SystemConfig instead"""
|
|
dimensions: int = 512
|
|
index_type: str = "Flat"
|
|
metric: str = "cosine"
|
|
nprobe: int = 8
|
|
auto_optimize: bool = True
|
|
migration_threshold: int = 10000
|
|
|
|
@classmethod
|
|
def from_env(cls) -> 'FAISSConfig':
|
|
logger.warning("FAISSConfig is deprecated. Use SystemConfig via get_config() instead.")
|
|
config = get_config()
|
|
return cls(
|
|
dimensions=config.faiss_dimensions,
|
|
index_type=config.faiss_index_type,
|
|
metric=config.faiss_metric,
|
|
nprobe=config.faiss_nprobe,
|
|
auto_optimize=config.faiss_auto_optimize,
|
|
migration_threshold=config.faiss_migration_threshold
|
|
)
|
|
|
|
|
|
@dataclass
|
|
class GPUResourceConfig:
|
|
"""Configuration for GPU resource management - DEPRECATED: Use SystemConfig instead"""
|
|
ollama_endpoint: str = "http://localhost:11434"
|
|
vlm_model: str = "qwen3-vl:8b"
|
|
clip_model: str = "ViT-B-32"
|
|
idle_timeout_seconds: int = 300
|
|
vram_threshold_for_clip_gpu_mb: int = 1024
|
|
max_load_retries: int = 3
|
|
load_timeout_seconds: int = 30
|
|
unload_timeout_seconds: int = 5
|
|
|
|
@classmethod
|
|
def from_env(cls) -> 'GPUResourceConfig':
|
|
logger.warning("GPUResourceConfig is deprecated. Use SystemConfig via get_config() instead.")
|
|
config = get_config()
|
|
return cls(
|
|
ollama_endpoint=config.vlm_endpoint,
|
|
vlm_model=config.vlm_model,
|
|
clip_model=config.clip_model,
|
|
idle_timeout_seconds=config.gpu_idle_timeout_seconds,
|
|
vram_threshold_for_clip_gpu_mb=config.gpu_vram_threshold_mb,
|
|
max_load_retries=config.gpu_max_load_retries,
|
|
load_timeout_seconds=config.gpu_load_timeout_seconds,
|
|
unload_timeout_seconds=config.gpu_unload_timeout_seconds
|
|
)
|
|
|
|
|
|
@dataclass
|
|
class AppConfig:
|
|
"""Configuration globale de l'application - DEPRECATED: Use SystemConfig instead"""
|
|
server: ServerConfig
|
|
security: SecurityConfig
|
|
models: ModelConfig
|
|
paths: PathConfig
|
|
faiss: FAISSConfig
|
|
gpu: GPUResourceConfig = field(default_factory=GPUResourceConfig)
|
|
|
|
@classmethod
|
|
def from_env(cls) -> 'AppConfig':
|
|
"""Charge la configuration depuis les variables d'environnement"""
|
|
logger.warning("AppConfig is deprecated. Use SystemConfig via get_config() instead.")
|
|
return cls(
|
|
server=ServerConfig.from_env(),
|
|
security=SecurityConfig.from_env(),
|
|
models=ModelConfig.from_env(),
|
|
paths=PathConfig.from_env(),
|
|
faiss=FAISSConfig.from_env(),
|
|
gpu=GPUResourceConfig.from_env()
|
|
)
|
|
|
|
|
|
# Exemple de fichier .env
|
|
# Exemple de fichier .env
|
|
ENV_TEMPLATE = """
|
|
# RPA Vision V3 Configuration Unifiée
|
|
# Copier ce fichier en .env et modifier les valeurs
|
|
|
|
# Environment
|
|
ENVIRONMENT=development # development, staging, production
|
|
DEBUG=false
|
|
|
|
# Base Path
|
|
BASE_PATH=/opt/rpa_vision_v3 # Production path, use . for development
|
|
|
|
# Server
|
|
API_HOST=0.0.0.0
|
|
API_PORT=8000
|
|
DASHBOARD_HOST=0.0.0.0
|
|
DASHBOARD_PORT=5001
|
|
WORKER_THREADS=4
|
|
|
|
# Security (REQUIRED in production)
|
|
# SECRET_KEY=your_secure_secret_key_here
|
|
# ENCRYPTION_PASSWORD=your_secure_password_here
|
|
AUTH_ENABLED=true
|
|
# ALLOWED_ORIGINS=https://yourdomain.com,https://api.yourdomain.com
|
|
|
|
# Data Paths (relative to BASE_PATH)
|
|
DATA_PATH=data
|
|
LOGS_PATH=logs
|
|
SESSIONS_PATH=data/sessions
|
|
WORKFLOWS_PATH=data/workflows
|
|
EMBEDDINGS_PATH=data/embeddings
|
|
FAISS_INDEX_PATH=data/faiss_index
|
|
SCREENSHOTS_PATH=data/screenshots
|
|
TRAINING_PATH=data/training
|
|
UPLOADS_PATH=data/training/uploads
|
|
|
|
# Models
|
|
CLIP_MODEL=ViT-B-32
|
|
CLIP_PRETRAINED=openai
|
|
CLIP_DEVICE=cpu
|
|
VLM_MODEL=qwen3-vl:8b
|
|
VLM_ENDPOINT=http://localhost:11434
|
|
OWL_MODEL=google/owlv2-base-patch16-ensemble
|
|
OWL_CONFIDENCE_THRESHOLD=0.1
|
|
|
|
# FAISS
|
|
FAISS_DIMENSIONS=512
|
|
FAISS_INDEX_TYPE=Flat
|
|
FAISS_METRIC=cosine
|
|
FAISS_NPROBE=8
|
|
FAISS_AUTO_OPTIMIZE=true
|
|
FAISS_MIGRATION_THRESHOLD=10000
|
|
|
|
# GPU
|
|
GPU_IDLE_TIMEOUT=300
|
|
GPU_VRAM_THRESHOLD_MB=1024
|
|
GPU_MAX_LOAD_RETRIES=3
|
|
GPU_LOAD_TIMEOUT=30
|
|
GPU_UNLOAD_TIMEOUT=5
|
|
|
|
# Monitoring
|
|
HEALTH_CHECK_INTERVAL=30
|
|
METRICS_ENABLED=true
|
|
"""
|
|
|
|
|
|
if __name__ == "__main__":
|
|
# Test de la configuration unifiée
|
|
config_manager = get_configuration_manager()
|
|
config = config_manager.load_config()
|
|
|
|
print("=== Configuration Système Unifiée ===")
|
|
print(f"Environment: {config.environment}")
|
|
print(f"Base Path: {config.base_path}")
|
|
print(f"Data Path: {config.data_path}")
|
|
print(f"API Port: {config.api_port}")
|
|
print(f"Dashboard Port: {config.dashboard_port}")
|
|
print(f"Sessions Path: {config.sessions_path}")
|
|
print(f"CLIP Model: {config.clip_model}")
|
|
print(f"Auth Enabled: {config.auth_enabled}")
|
|
|
|
# Test de validation
|
|
validation_errors = config_manager.validate_config(config)
|
|
if validation_errors:
|
|
print("\n=== Validation Errors/Warnings ===")
|
|
for error in validation_errors:
|
|
print(f"[{error.severity.upper()}] {error.field}: {error.message}")
|
|
else:
|
|
print("\n✅ Configuration validation passed")
|
|
|
|
print(f"\n✅ Configuration Manager initialized successfully")
|