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
+26 -6
View File
@@ -415,7 +415,7 @@ def test_broker_run_deployment_returns_unrunnable_for_dependency_errors() -> Non
assert payload["diagnostics"][0]["code"] == "source_missing"
def test_broker_run_deployment_rejects_interrupting_artifacts() -> None:
def test_broker_run_deployment_pauses_and_resumes_interrupting_artifacts() -> None:
artifact_store = FileWorkflowArtifactStore(
local_temp_root() / "broker_run_interrupt_artifacts"
)
@@ -445,9 +445,25 @@ def test_broker_run_deployment_rejects_interrupting_artifacts() -> None:
)
payload = cast(dict[str, Any], cast(object, structured))
assert payload["status"] == "unsupported"
assert payload["output"] is None
assert payload["diagnostics"][0]["code"] == "interrupting_artifact_unsupported"
assert payload["status"] == "interrupted"
assert payload["output"] == {}
assert isinstance(payload["run_id"], str)
assert payload["interrupt"]["payload"]["message"] == "send?"
_content, structured = asyncio.run(
server.call_tool(
"resume_workflow_run",
{
"run_id": payload["run_id"],
"resume_payload": {},
},
)
)
resumed = cast(dict[str, Any], cast(object, structured))
assert resumed["status"] == "completed"
assert resumed["outcome"] == "submitted"
assert resumed["run_id"] is None
def test_build_service_from_config_uses_store_root_for_artifacts() -> None:
@@ -556,6 +572,7 @@ def _interrupt_artifact() -> WorkflowArtifact:
},
"state_schema": {"fields": {}},
"output_schema": {"type": "object", "properties": {}},
"outcomes": ["submitted"],
"start": "approval",
"nodes": [
{
@@ -565,8 +582,11 @@ def _interrupt_artifact() -> WorkflowArtifact:
"request": [input_binding("input.message", "message")],
"resume": [],
"outcomes": ["submitted"],
}
},
{"id": "end_submitted", "type": "end", "outcome": "submitted"},
],
"edges": [
{"from": "approval", "outcome": "submitted", "to": "end_submitted"}
],
"edges": [{"from": "approval", "outcome": "submitted", "to": "__end__"}],
},
)
+31 -5
View File
@@ -94,18 +94,44 @@ def test_saved_child_missing_parent_binding_is_unrunnable() -> None:
assert result["diagnostics"][0]["logical_ref"] == "demo.echo_tool"
def test_interrupting_saved_child_remains_unrunnable_on_deployment_surface() -> None:
def test_interrupting_saved_child_pauses_and_resumes_through_deployment_surface() -> (
None
):
store = FileWorkflowArtifactStore(local_temp_root() / "saved_subgraph_interrupt")
store.save_artifact(_parent_artifact())
store.save_artifact(_interrupting_child_artifact())
store.save_deployment(_deployment())
handlers = _handlers(store)
result = asyncio.run(handlers.validate_deployment(deployment_id="parent.personal"))
validation = asyncio.run(
handlers.validate_deployment(deployment_id="parent.personal")
)
assert result["status"] == "unrunnable"
assert result["diagnostics"][0]["code"] == "interrupting_artifact_unsupported"
assert result["diagnostics"][0]["logical_ref"] == "workflow.child.v1"
assert validation["status"] == "runnable"
assert validation["diagnostics"] == []
paused = asyncio.run(
handlers.run_deployment(
deployment_id="parent.personal",
workflow_input={"text": "hello"},
)
)
assert paused["status"] == "interrupted"
assert isinstance(paused["run_id"], str)
assert paused["interrupt"]["node_id"] == "child_step"
assert paused["interrupt"]["payload"]["question"] == "hello"
resumed = asyncio.run(
handlers.resume_run(
run_id=paused["run_id"],
resume_payload={"answer": "world"},
)
)
assert resumed["status"] == "completed"
assert resumed["outcome"] == "ok"
assert resumed["output"]["echoed"] == "world"
def test_missing_saved_child_is_unrunnable_on_deployment_surface() -> None: