feat: complete draft step model parity
This commit is contained in:
@@ -14,6 +14,7 @@ from .models import (
|
||||
DraftJoinStep,
|
||||
DraftMatchCase,
|
||||
DraftMatchStep,
|
||||
DraftSubgraphStep,
|
||||
DraftUseStep,
|
||||
DraftWhenStep,
|
||||
WorkflowDraft,
|
||||
@@ -29,6 +30,7 @@ __all__ = [
|
||||
"DraftJoinStep",
|
||||
"DraftMatchCase",
|
||||
"DraftMatchStep",
|
||||
"DraftSubgraphStep",
|
||||
"DraftWhenStep",
|
||||
"DraftUseStep",
|
||||
"WorkflowDraft",
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from wf_authoring import WorkflowBuilder
|
||||
from wf_authoring.dsl import PathExpr
|
||||
from wf_core import JoinNode, Workflow
|
||||
from wf_core import JoinNode, SubgraphNode, Workflow
|
||||
from wf_core.paths import GraphSourcePath
|
||||
|
||||
from .models import (
|
||||
@@ -13,6 +15,7 @@ from .models import (
|
||||
DraftJoinStep,
|
||||
DraftMatchStep,
|
||||
DraftStep,
|
||||
DraftSubgraphStep,
|
||||
DraftUseStep,
|
||||
DraftWhenStep,
|
||||
WorkflowDraft,
|
||||
@@ -63,12 +66,23 @@ def _add_step(builder: WorkflowBuilder, step_id: str, step: DraftStep):
|
||||
concurrent=step.foreach.concurrent,
|
||||
)
|
||||
if isinstance(step, DraftInterruptStep):
|
||||
interrupt_kwargs: dict[str, Any] = {
|
||||
"id": step_id,
|
||||
"kind": step.interrupt.kind,
|
||||
"request": step.interrupt.request,
|
||||
"resume": step.interrupt.resume,
|
||||
"outcomes": step.interrupt.outcomes,
|
||||
}
|
||||
if step.interrupt.request_schema is not None:
|
||||
interrupt_kwargs["request_schema"] = step.interrupt.request_schema.model_dump(
|
||||
mode="json", exclude_none=True
|
||||
)
|
||||
if step.interrupt.resume_schema is not None:
|
||||
interrupt_kwargs["resume_schema"] = step.interrupt.resume_schema.model_dump(
|
||||
mode="json", exclude_none=True
|
||||
)
|
||||
return builder.interrupt(
|
||||
id=step_id,
|
||||
kind=step.interrupt.kind,
|
||||
request=step.interrupt.request,
|
||||
resume=step.interrupt.resume,
|
||||
outcomes=step.interrupt.outcomes,
|
||||
**interrupt_kwargs,
|
||||
)
|
||||
if isinstance(step, DraftJoinStep):
|
||||
node = JoinNode(id=step_id, type="join")
|
||||
@@ -96,4 +110,12 @@ def _add_step(builder: WorkflowBuilder, step_id: str, step: DraftStep):
|
||||
id=step_id,
|
||||
default=step.match.default,
|
||||
).entry
|
||||
if isinstance(step, DraftSubgraphStep):
|
||||
node = SubgraphNode(
|
||||
id=step_id,
|
||||
type="subgraph",
|
||||
**step.subgraph.model_dump(),
|
||||
)
|
||||
builder.nodes.append(node)
|
||||
return node
|
||||
raise TypeError(f"unsupported draft step {type(step)!r}")
|
||||
|
||||
@@ -2,15 +2,17 @@ from __future__ import annotations
|
||||
|
||||
from typing import Any, Literal
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field, model_validator
|
||||
from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator
|
||||
|
||||
from wf_core.models.conditions import Condition
|
||||
from wf_core.models.schemas import SchemaRef
|
||||
from wf_core.models.steps import (
|
||||
ForeachConcurrentPolicy,
|
||||
ForeachItemErrorPolicy,
|
||||
InputBinding,
|
||||
OutputBinding,
|
||||
)
|
||||
from wf_core.models.workflow_refs import WorkflowRef
|
||||
from wf_core.paths import GraphSourcePath
|
||||
|
||||
JsonObject = dict[str, Any]
|
||||
@@ -24,6 +26,7 @@ STEP_KIND_KEYS = frozenset(
|
||||
"when",
|
||||
"choose",
|
||||
"match",
|
||||
"subgraph",
|
||||
}
|
||||
)
|
||||
|
||||
@@ -153,8 +156,18 @@ class DraftInterruptPayload(BaseModel):
|
||||
kind: str
|
||||
request: list[InputBinding] = Field(default_factory=list)
|
||||
resume: list[OutputBinding] = Field(default_factory=list)
|
||||
request_schema: SchemaRef | None = None
|
||||
resume_schema: SchemaRef | None = None
|
||||
outcomes: list[str] = Field(default_factory=lambda: ["submitted"])
|
||||
|
||||
@field_validator("request_schema", "resume_schema")
|
||||
@classmethod
|
||||
def _require_object_schema(cls, value: SchemaRef | None) -> SchemaRef | None:
|
||||
"""Keep explicit interrupt contracts distinct from untyped interrupts."""
|
||||
if value is not None and value.type != "object":
|
||||
raise ValueError("interrupt schema must describe a JSON object")
|
||||
return value
|
||||
|
||||
@model_validator(mode="before")
|
||||
@classmethod
|
||||
def _coerce_legacy_maps(cls, data: object) -> object:
|
||||
@@ -184,6 +197,30 @@ class DraftInterruptStep(BaseModel):
|
||||
interrupt: DraftInterruptPayload
|
||||
|
||||
|
||||
class DraftSubgraphPayload(BaseModel):
|
||||
"""Declarative boundary contract for a referenced child workflow."""
|
||||
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
workflow: WorkflowRef
|
||||
desc: str | None = None
|
||||
input_schema: SchemaRef = Field(default_factory=lambda: SchemaRef(type="object"))
|
||||
output_schema: SchemaRef = Field(
|
||||
default_factory=lambda: SchemaRef(type="object")
|
||||
)
|
||||
input: list[InputBinding] = Field(default_factory=list)
|
||||
output: list[OutputBinding] = Field(default_factory=list)
|
||||
outcomes: list[str] = Field(default_factory=lambda: ["ok"], min_length=1)
|
||||
|
||||
|
||||
class DraftSubgraphStep(BaseModel):
|
||||
"""Draft step that lowers to a native `SubgraphNode` boundary."""
|
||||
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
subgraph: DraftSubgraphPayload
|
||||
|
||||
|
||||
class DraftJoinStep(BaseModel):
|
||||
"""Draft step that emits the current core join node."""
|
||||
|
||||
@@ -292,6 +329,7 @@ DraftStep = (
|
||||
| DraftWhenStep
|
||||
| DraftChooseStep
|
||||
| DraftMatchStep
|
||||
| DraftSubgraphStep
|
||||
)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user