from __future__ import annotations from typing import Any from control_plane.authoring.services import DjangoStoryWorkflowServices from control_plane.authoring.state import StoryGraphState def build_story_workflow(services: DjangoStoryWorkflowServices, checkpointer: object): try: from langgraph.graph import END, StateGraph from langgraph.types import interrupt except ImportError as exc: raise RuntimeError("Story authoring requires LangGraph") from exc graph = StateGraph(StoryGraphState) def build_context(state: StoryGraphState) -> dict[str, Any]: return services.build_context(dict(state)) def plan_chapter(state: StoryGraphState) -> dict[str, Any]: return services.plan_chapter(dict(state)) def approve_plan(state: StoryGraphState) -> dict[str, Any]: approval = services.ensure_approval( dict(state), "STORY_PLAN_APPROVAL", { "type": "story_plan", "revision_id": state["revision_id"], "scene_plan": state.get("scene_plan", {}), "allowed_actions": ["approve", "request_revision", "reject"], }, ) decision = interrupt(approval.payload) services.decide_approval(approval.id, decision) return { "approval_action": str(decision.get("action") or "reject").lower(), "human_notes": str(decision.get("notes") or ""), } def draft_chapter(state: StoryGraphState) -> dict[str, Any]: return services.draft_chapter(dict(state)) def extract_continuity(state: StoryGraphState) -> dict[str, Any]: method = getattr(services, "extract_final_state", services.extract_continuity) return method(dict(state)) def quality_review(state: StoryGraphState) -> dict[str, Any]: method = getattr(services, "quality_review", None) if method is None: return {"editorial_finding_ids": []} return method(dict(state)) def judge_state_contract(state: StoryGraphState) -> dict[str, Any]: method = getattr(services, "finalize_combined_audit", None) if method is None: method = getattr(services, "judge_state_contract", None) if method is None: return {"state_judge_status": "pass"} return method(dict(state)) def decide_patch(state: StoryGraphState) -> dict[str, Any]: return services.decide_patch(dict(state)) def apply_patch(state: StoryGraphState) -> dict[str, Any]: return services.apply_automatic_patch(dict(state)) def verify_patch(state: StoryGraphState) -> dict[str, Any]: return services.verify_patch(dict(state)) def approve_chapter(state: StoryGraphState) -> dict[str, Any]: state_payload_method = getattr(services, "state_approval_payload", None) state_payload = state_payload_method(dict(state)) if state_payload_method else {} approval = services.ensure_approval( dict(state), "STORY_CHAPTER_APPROVAL", { "type": "story_chapter", "revision_id": state["revision_id"], "finding_ids": state.get("editorial_finding_ids", []), **state_payload, "allowed_actions": ["approve", "request_revision", "reject"], }, ) decision = interrupt(approval.payload) services.decide_approval(approval.id, decision) return { "approval_action": str(decision.get("action") or "reject").lower(), "human_notes": str(decision.get("notes") or ""), } def commit_chapter(state: StoryGraphState) -> dict[str, Any]: return services.commit_chapter(dict(state)) def publish_story(state: StoryGraphState) -> dict[str, Any]: return {"export_uri": services.publish_story(dict(state))} graph.add_node("build_context", build_context) graph.add_node("plan_chapter", plan_chapter) graph.add_node("approve_plan", approve_plan) graph.add_node("draft_chapter", draft_chapter) graph.add_node("quality_review", quality_review) graph.add_node("extract_continuity", extract_continuity) graph.add_node("review_draft", judge_state_contract) graph.add_node("decide_patch", decide_patch) graph.add_node("apply_patch", apply_patch) graph.add_node("extract_patched_continuity", extract_continuity) graph.add_node("verify_patch", verify_patch) graph.add_node("approve_chapter", approve_chapter) graph.add_node("commit_chapter", commit_chapter) graph.add_node("publish_story", publish_story) graph.add_node("manual_revision", lambda state: {}) graph.add_node("reject", lambda state: {}) graph.set_entry_point("build_context") graph.add_edge("build_context", "plan_chapter") graph.add_edge("plan_chapter", "approve_plan") graph.add_conditional_edges( "approve_plan", lambda state: state.get("approval_action", "reject"), {"approve": "draft_chapter", "request_revision": "plan_chapter", "reject": "reject"}, ) graph.add_edge("draft_chapter", "quality_review") graph.add_edge("quality_review", "decide_patch") graph.add_conditional_edges( "decide_patch", lambda state: state.get("patch_decision", "human_review"), {"patch": "apply_patch", "human_review": "extract_continuity"}, ) graph.add_conditional_edges( "apply_patch", lambda state: state.get("patch_status", "failed"), {"applied": "extract_patched_continuity", "failed": "extract_continuity"}, ) graph.add_edge("extract_continuity", "review_draft") graph.add_edge("extract_patched_continuity", "verify_patch") graph.add_edge("verify_patch", "review_draft") graph.add_edge("review_draft", "approve_chapter") graph.add_conditional_edges( "approve_chapter", lambda state: state.get("approval_action", "reject"), {"approve": "commit_chapter", "request_revision": "manual_revision", "reject": "reject"}, ) graph.add_edge("commit_chapter", "publish_story") graph.add_edge("publish_story", END) graph.add_edge("manual_revision", END) graph.add_edge("reject", END) return graph.compile(checkpointer=checkpointer)