in-memory MCP resuming an interrupted deployment

This commit is contained in:
lda
2026-05-26 01:54:04 +07:00 Verified
parent 95532b1cf9
commit 9d11f78111
14 changed files with 585 additions and 91 deletions
+13
View File
@@ -79,3 +79,16 @@ def register_artifact_tools(server: FastMCP, service: WfMcpService) -> None:
deployment_id=deployment_id,
workflow_input=workflow_input,
)
@server.tool()
async def resume_workflow_run(
run_id: str,
resume_payload: dict[str, Any],
resume_outcome: str = "submitted",
) -> dict[str, Any]:
"""Resume a process-local interrupted deployment run."""
return await handlers.resume_run(
run_id=run_id,
resume_payload=resume_payload,
resume_outcome=resume_outcome,
)
+85 -15
View File
@@ -18,7 +18,13 @@ from wf_artifacts import (
artifact_catalog_entry,
)
from wf_authoring import NodeReturn, NodeSpec
from wf_core import NodeUse, Workflow, execute_workflow_async
from wf_core import (
NodeUse,
RunState,
Workflow,
execute_workflow_async,
resume_workflow_async,
)
from wf_platform import (
CapabilityBuckets,
@@ -664,6 +670,7 @@ class WfMcpService:
"state_schema": plan.state_schema,
"output_schema": plan.output_schema,
"output": [binding.model_dump(mode="json") for binding in plan.output],
"outcomes": plan.outcomes,
"start": plan.start,
"node_defs": [node.model_dump() for node in node_defs.values()],
"nodes": nodes,
@@ -671,20 +678,19 @@ class WfMcpService:
}
return Workflow.model_validate(payload)
async def run_workflow_from_plan(
def _prepare_workflow_runtime(
self,
plan: RawWorkflowPlan,
workflow_input: dict[str, Any],
deployment: WorkflowDeployment | None = None,
artifact: WorkflowArtifact | None = None,
):
self._record_event(
make_event(
"workflow_run_started",
workflow_name=plan.name,
payload={"input_keys": sorted(workflow_input.keys())},
)
)
*,
deployment: WorkflowDeployment | None,
artifact: WorkflowArtifact | None,
) -> tuple[Workflow, dict[str, Any], dict[str, Any], dict[str, Any]]:
"""Resolve bindings once into the executable pieces core expects.
Saved-run resume must rebuild prepared dependencies from the current
in-memory service state. Durable resume will need a stricter snapshot,
but this keeps the current platform boundary explicit.
"""
plan_node_names = [
node.node for node in plan.nodes if isinstance(node, NodeUse)
]
@@ -716,11 +722,39 @@ class WfMcpService:
compile_plan=self.compile_plan,
)
workflow = self.compile_plan(plan, dependencies.node_name_bindings)
return (
workflow,
dependencies.node_registry,
dependencies.reducers,
prepared_subgraphs,
)
async def run_workflow_from_plan(
self,
plan: RawWorkflowPlan,
workflow_input: dict[str, Any],
deployment: WorkflowDeployment | None = None,
artifact: WorkflowArtifact | None = None,
):
self._record_event(
make_event(
"workflow_run_started",
workflow_name=plan.name,
payload={"input_keys": sorted(workflow_input.keys())},
)
)
workflow, registry, reducers, prepared_subgraphs = (
self._prepare_workflow_runtime(
plan,
deployment=deployment,
artifact=artifact,
)
)
run = await execute_workflow_async(
workflow,
workflow_input,
dependencies.node_registry,
reducers=dependencies.reducers,
registry,
reducers=reducers,
subgraphs=prepared_subgraphs,
)
self._record_event(
@@ -732,6 +766,42 @@ class WfMcpService:
)
return run
async def resume_workflow_from_plan(
self,
plan: RawWorkflowPlan,
run: RunState,
*,
resume_payload: dict[str, Any],
resume_outcome: str = "submitted",
deployment: WorkflowDeployment | None = None,
artifact: WorkflowArtifact | None = None,
) -> RunState:
"""Resume one in-memory run using freshly resolved runtime dependencies."""
workflow, registry, reducers, prepared_subgraphs = (
self._prepare_workflow_runtime(
plan,
deployment=deployment,
artifact=artifact,
)
)
resumed = await resume_workflow_async(
workflow,
run,
registry,
resume_payload=resume_payload,
resume_outcome=resume_outcome,
reducers=reducers,
subgraphs=prepared_subgraphs,
)
self._record_event(
make_event(
"workflow_run_resumed",
workflow_name=plan.name,
payload={"status": resumed.status.value},
)
)
return resumed
def list_events(self) -> list[McpEvent]:
return self.event_bus.list_events()