#spec.py from pydantic import BaseModel, Field, model_validator from typing import List, Optional, Literal class InputOutputConfig(BaseModel): has_inputs: bool = Field(default=False, description="Le script prend-il des éléments en entrée ?") input_type: Optional[Literal["file", "directory", "api", "database", "none"]] = Field(None, description="Type d'entrée principale") input_paths_or_sources: List[str] = Field(default_factory=list, description="Chemins, tables ou endpoints sources") has_outputs: bool = Field(default=False, description="Le script génère-t-style des éléments en sortie ?") output_type: Optional[Literal["file", "directory", "database", "api_response", "log_only"]] = Field(None, description="Type de sortie principale") output_formats: List[str] = Field(default_factory=list, description="Formats attendus (ex: CSV, JSON, XLSX)") class AuthenticationConfig(BaseModel): requires_auth: bool = Field(default=False, description="Le script nécessite-t-il des accès sécurisés ?") auth_method: Optional[Literal["env_variables", "service_account_json", "oauth2", "api_key", "none"]] = Field(None, description="Méthode d'authentification") target_tools_and_apis: List[str] = Field(default_factory=list, description="Logiciels ou API avec lesquels interagir (ex: Sharepoint, JIRA)") class EnvironmentConfig(BaseModel): target_os: Literal["linux", "windows", "macos", "cross_platform"] = Field(default="cross_platform", description="Système d'exploitation cible") language_version: str = Field(default="^3.11", description="Contrainte de version du langage") critical_dependencies: List[str] = Field(default_factory=list, description="Librairies tierces indispensables") class ProjectSpec(BaseModel): title: Optional[str] = Field(None, description="Nom clair et concis du script") description: Optional[str] = Field(None, description="Description macro de l'objectif du script") requirements: List[str] = Field(default_factory=list, description="Liste des fonctionnalités pas-à-pas attendues") constraints: List[str] = Field(default_factory=list, description="Normes, formats de code et conventions de nommage imposés") language: str = Field(default="Python", description="Langage de programmation principal") target_stack: str = Field(None, description="Stack technique ou framework attendu (ex: Pandas, FastAPI, Flask, Django, etc.)") # Sous-configurations détaillées (permettent la détection des champs manquants) io_config: InputOutputConfig = Field(default_factory=InputOutputConfig) auth_config: AuthenticationConfig = Field(default_factory=AuthenticationConfig) env_config: EnvironmentConfig = Field(default_factory=EnvironmentConfig) error_handling_strategy: Literal["abort_on_error", "log_and_continue", "retry_policy"] = Field( default="log_and_continue", description="Comportement du script face à une anomalie" ) is_complete: bool = Field(default=False, description="Passez à True UNIQUEMENT si vous avez TOUTES les infos pour coder (titre, desc, reqs, io_config, auth_config si besoin).") clarifying_question: Optional[str] = Field(None, description="Si is_complete est False, écrivez ici une question claire et polie pour Chainlit pour demander les détails manquants.") @model_validator(mode="after") def validate_conditional_fields(self) -> 'ProjectSpec': """Validation des dépendances logiques pour marquer le cahier des charges comme valide.""" # Si les champs majeurs sont remplis, on valide la cohérence interne if self.title and self.description and len(self.requirements) > 0: if self.io_config.has_inputs and not self.io_config.input_type: raise ValueError("input_type manquant alors que has_inputs est True") if self.io_config.has_outputs and not self.io_config.output_type: raise ValueError("output_type manquant alors que has_outputs est True") if self.auth_config.requires_auth and (not self.auth_config.auth_method or self.auth_config.auth_method == "none"): raise ValueError("auth_method manquante alors que requires_auth est True") return self