Subgraphs et streaming d'exécution
Subgraph : le graphe comme node
Un graphe compilĂ© est un node comme un autre â c'est la composition du projet :
orchestrator = StateGraph(UnifiedState)
orchestrator.add_node("extraction", create_extraction_graph_with_matching()) # â un GRAPHE
orchestrator.add_node("post_extraction", post_extraction_node)
orchestrator.add_node("persist_document", persist_node)
orchestrator.add_edge(START, "extraction")
orchestrator.add_edge("extraction", "post_extraction")
L'orchestrateur reste lisible en trois lignes ; le dĂ©tail (router â extractor â structurer â validator â matchers) vit dans le subgraph. MĂȘme state partagĂ© (le UnifiedState plat, section par section) â pas de mapping d'entrĂ©es/sorties entre niveaux. Le projet a aussi un subgraph multi_request (splitter + fan-out) quand un mĂȘme document contient plusieurs commandes â la composition permet de brancher ce cas sans toucher au chemin principal.
astream : l'exécution comme flux d'événements
async for chunk in graph.astream(initial_state, stream_mode="updates"):
# chunk = {"matcher_fast": {"match_results": [...]}} â delta par node terminĂ©
node_name, delta = next(iter(chunk.items()))
yield sse_event("node_completed", {"node": node_name, **summary(delta)})
stream_mode="updates" Ă©met un Ă©vĂ©nement par node terminĂ©, avec son delta â exactement la granularitĂ© d'une UI de progression. C'est le pont direct vers le gĂ©nĂ©rateur SSE du module 2 : le front affiche « extraction â â matching en cours⊠» et mĂȘme les lignes matchĂ©es en live. (Les autres modes existent : values = le state complet Ă chaque Ă©tape, messages = les tokens LLM.)
La conséquence : les noms de nodes sont une API
Puisque le front se cĂąble sur {"node": "persist_document"}, renommer un node est un breaking change. Le projet en fait un contrat documentĂ©. Corollaire de design : nomme tes nodes comme tu nommes tes routes â pour le consommateur, pas pour toi.
Ajouter un subgraph compilĂ© avec add_node(...) permetâŠ