Etape 1 OK, sans optionnel
This commit is contained in:
@@ -1,18 +1,34 @@
|
||||
#nodes.py
|
||||
from app.agents.pm_agent import run_pm_agent
|
||||
from app.agents.dev_agent import run_dev_agent
|
||||
from app.agents.qa_agent import run_qa_agent
|
||||
from app.services.retrieval_service import find_existing_project
|
||||
from app.graph.state import WorkflowState
|
||||
|
||||
async def pm_node(state: WorkflowState):
|
||||
prompt = state["user_input"]
|
||||
if state.get("user_feedback"):
|
||||
prompt += f"\nRetour utilisateur pour correction : {state['user_feedback']}"
|
||||
async def pm_node(state: WorkflowState):
|
||||
history = state.get("chat_history", []) or []
|
||||
|
||||
if state.get("status") == "spec_incomplete" and state.get("user_feedback"):
|
||||
current_input = state["user_feedback"]
|
||||
full_user_input = f"{state['user_input']}\n{current_input}"
|
||||
else:
|
||||
current_input = state["user_input"]
|
||||
full_user_input = current_input
|
||||
|
||||
spec = await run_pm_agent(user_input=current_input, history=history)
|
||||
|
||||
updated_history = list(history)
|
||||
updated_history.append({"role": "user", "content": current_input})
|
||||
|
||||
if not spec.is_complete and spec.clarifying_question:
|
||||
updated_history.append({"role": "assistant", "content": spec.clarifying_question})
|
||||
|
||||
spec = await run_pm_agent(prompt)
|
||||
return {
|
||||
"spec": spec.model_dump(),
|
||||
"status": "spec_ready",
|
||||
"status": "spec_ready" if spec.is_complete else "spec_incomplete",
|
||||
"chat_history": updated_history,
|
||||
"user_input": full_user_input,
|
||||
"user_feedback": None,
|
||||
"loop_count": 0,
|
||||
}
|
||||
|
||||
@@ -53,4 +69,45 @@ async def human_review_node(state: WorkflowState):
|
||||
"existing_project_approved": True,
|
||||
"is_completed": True,
|
||||
"status": "approved_by_human"
|
||||
}
|
||||
}
|
||||
|
||||
# def inspect_specifications_gaps(spec_dict: dict) -> list[str]:
|
||||
# missing_fields = []
|
||||
|
||||
# # 1. Vérifications globales (clés alignées sur ProjectSpec)
|
||||
# if not spec_dict.get("title"): missing_fields.append("title")
|
||||
# if not spec_dict.get("description"): missing_fields.append("description")
|
||||
# if not spec_dict.get("requirements"): missing_fields.append("requirements")
|
||||
|
||||
# # 2. Entrées/Sorties
|
||||
# io = spec_dict.get("io_config", {})
|
||||
# if io.get("has_inputs") is True and not io.get("input_type"):
|
||||
# missing_fields.append("io_config.input_type")
|
||||
# if io.get("has_outputs") is True and not io.get("output_type"):
|
||||
# missing_fields.append("io_config.output_type")
|
||||
|
||||
# # 3. Authentification
|
||||
# auth = spec_dict.get("auth_config", {})
|
||||
# if auth.get("requires_auth") is True and not auth.get("auth_method"):
|
||||
# missing_fields.append("auth_config.auth_method")
|
||||
|
||||
# return missing_fields
|
||||
|
||||
# def generate_clarifying_questions_prompt(missing_fields: list[str]) -> str:
|
||||
# # Le mapping utilise désormais les EXACTES mêmes clés
|
||||
# mapping_instructions = {
|
||||
# "title": "- Préciser un titre pour le projet.",
|
||||
# "description": "- Expliquer le but global du script.",
|
||||
# "requirements": "- Lister les fonctionnalités attendues étape par étape.",
|
||||
# "io_config.input_type": "- Préciser le type et le format des fichiers/données d'entrée (CSV, dossier, etc.).",
|
||||
# "io_config.output_type": "- Préciser le type et le format attendus en sortie (Excel, PDF, log, etc.).",
|
||||
# "auth_config.auth_method": "- Clarifier la méthode d'accès ou d'authentification exigée pour l'outil tiers."
|
||||
# }
|
||||
|
||||
# bullet_points = "\n".join([mapping_instructions[field] for field in missing_fields if field in mapping_instructions])
|
||||
|
||||
# return f"""
|
||||
# ATTENTION : Les données suivantes sont obligatoires mais absentes.
|
||||
# Vous devez formuler une question naturelle et polie pour demander à l'utilisateur de préciser :
|
||||
# {bullet_points}
|
||||
# """
|
||||
@@ -1,4 +1,4 @@
|
||||
from typing import TypedDict, Optional, Any, Dict
|
||||
from typing import TypedDict, Optional, Any, Dict, List
|
||||
|
||||
class WorkflowState(TypedDict, total=False):
|
||||
user_input: str
|
||||
@@ -10,4 +10,5 @@ class WorkflowState(TypedDict, total=False):
|
||||
loop_count: int # Compteur pour la Loop 1 (Dev <-> QA)
|
||||
user_feedback: Optional[str] # Retours si l'utilisateur refuse le code final
|
||||
is_completed: bool # Statut de livraison finale
|
||||
status: str
|
||||
status: str
|
||||
chat_history: List[Dict[str, str]]
|
||||
@@ -12,7 +12,17 @@ from app.graph.nodes import (
|
||||
human_review_node,
|
||||
)
|
||||
|
||||
def route_entry_point(state: WorkflowState):
|
||||
if state.get("status") == "spec_approved":
|
||||
return "retrieval"
|
||||
return "pm"
|
||||
|
||||
# --- Fonctions de Routage (Conditional Edges) ---
|
||||
def route_after_pm(state: WorkflowState):
|
||||
current_status = state.get("status")
|
||||
if current_status in ["spec_incomplete", "spec_ready"]:
|
||||
return END
|
||||
return "retrieval"
|
||||
|
||||
def route_after_retrieval(state: WorkflowState):
|
||||
# Si un projet existe, on demande d'abord à l'humain (via le nœud de review)
|
||||
@@ -55,8 +65,22 @@ graph.add_node("dev", dev_node)
|
||||
graph.add_node("qa", qa_node)
|
||||
graph.add_node("human_review", human_review_node)
|
||||
|
||||
graph.set_entry_point("pm")
|
||||
graph.add_edge("pm", "retrieval")
|
||||
graph.set_conditional_entry_point(
|
||||
route_entry_point,
|
||||
{
|
||||
"pm": "pm",
|
||||
"retrieval": "retrieval",
|
||||
},
|
||||
)
|
||||
|
||||
graph.add_conditional_edges(
|
||||
"pm",
|
||||
route_after_pm,
|
||||
{
|
||||
END: END,
|
||||
"retrieval": "retrieval",
|
||||
},
|
||||
)
|
||||
|
||||
# Étape 1 : Choix après recherche vectorielle
|
||||
graph.add_conditional_edges(
|
||||
|
||||
Reference in New Issue
Block a user