Etape 4 finie, TODO : correction QA, gestion retour au PM après refus utilisateur
This commit is contained in:
@@ -1,3 +1,7 @@
|
||||
import os
|
||||
import json
|
||||
import time
|
||||
from pathlib import Path
|
||||
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
|
||||
@@ -106,10 +110,59 @@ async def qa_node(state: WorkflowState):
|
||||
}
|
||||
|
||||
async def human_review_node(state: WorkflowState):
|
||||
print("[Human Review] Passage en mode automatique (Mock)...")
|
||||
"""
|
||||
Nœud pivot. Si le statut vient du QA, il bascule en attente de validation humaine.
|
||||
Si Chainlit a déjà collecté la décision, il laisse passer le flux vers le routage.
|
||||
"""
|
||||
current_status = state.get("status")
|
||||
|
||||
if current_status in ["qa_done", "existing_found"]:
|
||||
return {
|
||||
"status": "wait_human_review"
|
||||
}
|
||||
|
||||
return {"status": current_status}
|
||||
|
||||
async def delivery_node(state: WorkflowState, config: RunnableConfig):
|
||||
"""
|
||||
Nœud final de livraison : Archivage ZIP en mémoire et réindexation Qdrant.
|
||||
"""
|
||||
dev_data = state.get("generated_code", {})
|
||||
project_title = dev_data.get("spec_title", f"project_{int(time.time())}")
|
||||
|
||||
raw_files = dev_data.get("files", {})
|
||||
files = {}
|
||||
if isinstance(raw_files, list):
|
||||
for f in raw_files:
|
||||
if isinstance(f, dict):
|
||||
path = f.get("path") or f.get("filename") or f.get("name")
|
||||
content = f.get("content") or f.get("code") or ""
|
||||
if path:
|
||||
files[path] = content
|
||||
elif isinstance(raw_files, dict):
|
||||
files = raw_files
|
||||
|
||||
# 3. Réindexation dans Qdrant (en utilisant le payload mémoire)
|
||||
# qdrant_repo = config.get("configurable", {}).get("qdrant_repo")
|
||||
# if qdrant_repo:
|
||||
# payload_text = f"Title: {project_title}\nDescription: {state.get('spec', {}).get('description')}"
|
||||
|
||||
# # /!\ À remplacer par ton vrai modèle d'embedding (ex: await qdrant_repo.embed(payload_text))
|
||||
# dummy_vector = [0.1] * 1536
|
||||
|
||||
# qdrant_repo.client.upsert(
|
||||
# collection_name="arc_projects",
|
||||
# points=[
|
||||
# PointStruct(
|
||||
# id=str(Path(project_title).name),
|
||||
# vector=dummy_vector,
|
||||
# payload=metadata
|
||||
# )
|
||||
# ]
|
||||
# )
|
||||
|
||||
return {
|
||||
"existing_project_approved": True,
|
||||
"is_completed": True,
|
||||
"status": "approved_by_human"
|
||||
"status": "delivered",
|
||||
"user_input": f"{state.get('user_input')}\n\n[System] Projet {project_title} validé et package ZIP disponible."
|
||||
}
|
||||
@@ -10,10 +10,14 @@ from app.graph.nodes import (
|
||||
dev_node,
|
||||
qa_node,
|
||||
human_review_node,
|
||||
delivery_node
|
||||
)
|
||||
|
||||
def route_entry_point(state: WorkflowState):
|
||||
if state.get("status") == "spec_approved":
|
||||
current_status = state.get("status")
|
||||
if current_status in ["human_approved", "human_refused"]:
|
||||
return "human_review"
|
||||
if current_status == "spec_approved":
|
||||
return "retrieval"
|
||||
return "pm"
|
||||
|
||||
@@ -40,18 +44,23 @@ def route_after_qa(state: WorkflowState):
|
||||
return "human_review"
|
||||
|
||||
def route_after_human(state: WorkflowState):
|
||||
# Cas d'un projet existant proposé
|
||||
if state.get("existing_project") and not state.get("generated_code"):
|
||||
if state.get("existing_project_approved") == True:
|
||||
return END # L'utilisateur est satisfait du projet existant
|
||||
return "dev" # L'utilisateur refuse l'existant, on génère du neuf
|
||||
|
||||
# Cas du code généré
|
||||
if state.get("is_completed") == True:
|
||||
return END
|
||||
current_status = state.get("status")
|
||||
|
||||
# Si l'utilisateur a refusé le code -> Retour à la case PM avec ses commentaires
|
||||
return "pm"
|
||||
if current_status == "wait_human_review":
|
||||
return END
|
||||
|
||||
if state.get("existing_project") and not state.get("generated_code"):
|
||||
if state.get("existing_project_approved") is True:
|
||||
return END
|
||||
return "dev"
|
||||
|
||||
if current_status == "human_approved":
|
||||
return "delivery"
|
||||
|
||||
if current_status == "human_refused":
|
||||
return "pm"
|
||||
|
||||
return END
|
||||
|
||||
# --- Assemblage du Graphe ---
|
||||
|
||||
@@ -62,12 +71,14 @@ graph.add_node("retrieval", retrieval_node)
|
||||
graph.add_node("dev", dev_node)
|
||||
graph.add_node("qa", qa_node)
|
||||
graph.add_node("human_review", human_review_node)
|
||||
graph.add_node("delivery", delivery_node)
|
||||
|
||||
graph.set_conditional_entry_point(
|
||||
route_entry_point,
|
||||
{
|
||||
"pm": "pm",
|
||||
"retrieval": "retrieval",
|
||||
"human_review": "human_review"
|
||||
},
|
||||
)
|
||||
|
||||
@@ -108,7 +119,12 @@ graph.add_conditional_edges(
|
||||
{
|
||||
"pm": "pm",
|
||||
"dev": "dev",
|
||||
"delivery": "delivery",
|
||||
END: END,
|
||||
},
|
||||
)
|
||||
|
||||
# Maillon final : Après Delivery, c'est la fin du cycle
|
||||
graph.add_edge("delivery", END)
|
||||
|
||||
compiled_graph = graph.compile()
|
||||
Reference in New Issue
Block a user