2026-06-17 10:18:55 +02:00
import json
import logging
import httpx
from langchain_openai import ChatOpenAI
from langchain_core . messages import SystemMessage , HumanMessage
from app . core . config import settings
from app . schemas . code_output import ProjectCodeOutput , GeneratedFile
from app . llm . prompts import DEV_AGENT_PROMPT
logger = logging . getLogger ( __name__ )
# Clients HTTPX configurés pour ignorer les blocages de certificats/révocation du lab
sync_client = httpx . Client ( verify = False )
async_client = httpx . AsyncClient ( verify = False )
2026-06-12 18:16:58 +02:00
async def run_dev_agent ( spec : dict , qa_feedback : list = None ) - > dict :
"""
2026-06-17 10:18:55 +02:00
Agent Dev : prend un état / spec validé , génère l ' arborescence et le code,
et valide techniquement la sortie avant de la transmettre à la QA .
2026-06-12 18:16:58 +02:00
"""
2026-06-17 10:18:55 +02:00
logger . info ( f " [Dev Agent] Début de la génération pour : { spec . get ( ' title ' , ' Sans titre ' ) } " )
llm = ChatOpenAI (
base_url = settings . llm_base_url ,
api_key = settings . llm_api_key ,
model = settings . llm_model_dev ,
temperature = 0.2 ,
max_retries = 2 ,
http_client = sync_client ,
http_async_client = async_client ,
model_kwargs = { " response_format " : { " type " : " json_object " } }
)
structured_llm = llm . with_structured_output ( ProjectCodeOutput , strict = True )
messages = [
SystemMessage ( content = DEV_AGENT_PROMPT ) ,
]
user_content = f " CAHIER DES CHARGES (ProjectSpec) : \n { json . dumps ( spec , indent = 2 , ensure_ascii = False ) } \n \n "
if qa_feedback :
user_content + = f " ⚠️ RETOURS DE VALIDATION QA (Corrections à appliquer impérativement) : \n { json . dumps ( qa_feedback , indent = 2 , ensure_ascii = False ) } \n \n "
user_content + = " Génère maintenant le JSON complet contenant l ' arborescence ( ' tree ' ) et tous les fichiers ( ' files ' ) décrits. "
messages . append ( HumanMessage ( content = user_content ) )
try :
validated_code = await structured_llm . ainvoke ( messages )
logger . info ( f " [Dev Agent] ✅ Code généré et validé avec succès ( { len ( validated_code . files ) } fichiers) " )
return validated_code . model_dump ( )
except Exception as e :
logger . error ( f " [Dev Agent] ❌ Échec de la génération/validation : { type ( e ) . __name__ } : { str ( e ) } " )
return _generate_fallback_code_output ( spec )
def _generate_fallback_code_output ( spec : dict ) - > dict :
""" Génère un livrable minimal de secours en cas de crash du LLM. """
logger . warning ( " [Dev Agent] Génération du package de secours (Fallback) " )
title = spec . get ( " title " , " automation_script " )
fallback = ProjectCodeOutput (
spec_title = title ,
tree = [ " main.py " , " README.md " , " requirements.txt " ] ,
files = [
GeneratedFile ( path = " main.py " , content = " import logging \n logging.basicConfig(level=logging.INFO) \n \n def main(): \n logging.error( ' Le Dev Agent a rencontré une erreur de génération. ' ) \n \n if __name__ == ' __main__ ' : \n main() " ) ,
GeneratedFile ( path = " README.md " , content = f " # { title } \n Génération en mode fallback suite à une erreur technique. " ) ,
GeneratedFile ( path = " requirements.txt " , content = " # Aucune dépendance externe définie (Fallback) \n " )
]
)
return fallback . model_dump ( )