feat: complete production workflow migration

This commit is contained in:
wangyunlong committed 2026-08-06 14:29:57 +08:00
1 parent 7f215e79c4
commit 8df5266abb
448 files changed
+56937 -14619

No files matched your search

+119
View File
@@ -0,0 +1,119 @@
"""LangGraph compilation for deterministic workflow definitions."""
from __future__ import annotations
from typing import Annotated, Callable, Literal, TypedDict
from langgraph.graph import END, START, StateGraph
from langgraph.graph.state import CompiledStateGraph
from gyxx_flow.core.context import RunContext
from .model import StepDefinition, WorkflowDefinition
GraphStepStatus = Literal["success", "failed", "skipped"]
class GraphAttemptResult(TypedDict):
"""JSON-serializable attempt result stored in graph state."""
attempt: int
status: GraphStepStatus
exit_code: int | None
error: str | None
reason: str | None
class GraphStepResult(TypedDict):
"""JSON-serializable step result stored in graph state."""
step_id: str
status: GraphStepStatus
attempts: list[GraphAttemptResult]
final_exit_code: int | None
critical: bool
reason: str | None
class WorkflowGraphState(TypedDict):
"""Serializable state passed between one workflow's LangGraph nodes."""
workflow_id: str
run_id: str
business_date: str
started_at: str
shadow: bool
dry_run: bool
step_results: Annotated[
dict[str, GraphStepResult],
_merge_step_results,
]
GraphStepRunner = Callable[
[StepDefinition, WorkflowGraphState],
GraphStepResult,
]
def initial_workflow_graph_state(
context: RunContext,
*,
dry_run: bool,
) -> WorkflowGraphState:
"""Create a JSON-serializable initial state from the public run context."""
return {
"workflow_id": context.workflow_id,
"run_id": context.run_id,
"business_date": context.business_date.isoformat(),
"started_at": context.started_at.isoformat(),
"shadow": context.shadow,
"dry_run": dry_run,
"step_results": {},
}
def compile_workflow_graph(
workflow: WorkflowDefinition,
*,
run_step: GraphStepRunner,
) -> CompiledStateGraph:
"""Compile dependencies into a real LangGraph DAG."""
builder = StateGraph(WorkflowGraphState)
ordered_steps = workflow.ordered_steps()
dependent_ids = {
dependency
for step in ordered_steps
for dependency in step.depends_on
}
for step in ordered_steps:
builder.add_node(step.step_id, _step_node(step, run_step))
if step.depends_on:
builder.add_edge(list(step.depends_on), step.step_id)
else:
builder.add_edge(START, step.step_id)
if step.step_id not in dependent_ids:
builder.add_edge(step.step_id, END)
return builder.compile()
def _step_node(
step: StepDefinition,
run_step: GraphStepRunner,
) -> Callable[[WorkflowGraphState], dict[str, object]]:
def execute(state: WorkflowGraphState) -> dict[str, object]:
result = run_step(step, state)
return {"step_results": {step.step_id: result}}
return execute
def _merge_step_results(
current: dict[str, GraphStepResult],
update: dict[str, GraphStepResult],
) -> dict[str, GraphStepResult]:
"""Merge branch-local results emitted in the same LangGraph superstep."""
return {**current, **update}