feat: expose composite step inputs
This commit is contained in:
@@ -11,7 +11,7 @@ from wf_artifacts import (
|
||||
)
|
||||
from wf_authoring import build_async_registry
|
||||
from wf_core import RuntimeContext
|
||||
from wf_core.models.steps import InputBinding, OutputBinding
|
||||
from wf_core.models.steps import OutputBinding, StepInputBinding
|
||||
from wf_core.paths import GraphSourcePath
|
||||
from wf_platform import CapabilitySource
|
||||
|
||||
@@ -389,7 +389,7 @@ class WorkflowCapabilityApi:
|
||||
input_schema: dict[str, Any] | None = None,
|
||||
state_schema: dict[str, Any] | None = None,
|
||||
output_schema: dict[str, Any] | None = None,
|
||||
input: Sequence[InputBinding] | None = None,
|
||||
input: Sequence[StepInputBinding] | None = None,
|
||||
output: Sequence[OutputBinding] | None = None,
|
||||
input_map: dict[str, str] | None = None,
|
||||
output_map: dict[str, str] | None = None,
|
||||
|
||||
@@ -30,6 +30,7 @@ from wf_core.models.steps import (
|
||||
InputPathBinding,
|
||||
InputValueBinding,
|
||||
OutputBinding,
|
||||
StepInputBinding,
|
||||
)
|
||||
from wf_core.paths import (
|
||||
GraphSourcePath,
|
||||
@@ -54,11 +55,7 @@ from .draft_payloads import (
|
||||
state_root_field,
|
||||
)
|
||||
from .draft_updates import CapabilityStepUpdate
|
||||
from .drafts import (
|
||||
WorkflowDraftApi,
|
||||
_draft_input_maps,
|
||||
_draft_output_map,
|
||||
)
|
||||
from .drafts import WorkflowDraftApi, _draft_output_map
|
||||
from .input_expressions import (
|
||||
validate_and_project_input_expression,
|
||||
validate_schema_references,
|
||||
@@ -101,7 +98,7 @@ def _upsert_input_path_binding(
|
||||
step_id: str,
|
||||
) -> list[dict[str, Any]]:
|
||||
"""Update one graph source without lowering unrelated canonical bindings."""
|
||||
bindings = TypeAdapter(list[InputBinding]).validate_python(payload)
|
||||
bindings = TypeAdapter(list[StepInputBinding]).validate_python(payload)
|
||||
matching = [
|
||||
index
|
||||
for index, existing in enumerate(bindings)
|
||||
@@ -208,7 +205,7 @@ def _rebind_workflow_output(
|
||||
|
||||
|
||||
def _overlapping_input_binding_targets_error(
|
||||
bindings: Sequence[InputBinding],
|
||||
bindings: Sequence[StepInputBinding],
|
||||
) -> ValueError:
|
||||
"""Describe the first overlapping input-shaped target pair."""
|
||||
for left_index, left in enumerate(bindings):
|
||||
@@ -473,7 +470,7 @@ class WorkflowDraftAuthoringApi:
|
||||
input_schema: dict[str, Any],
|
||||
state_schema: dict[str, Any],
|
||||
output_schema: dict[str, Any],
|
||||
input: Sequence[InputBinding] | None = None,
|
||||
input: Sequence[StepInputBinding] | None = None,
|
||||
output: Sequence[OutputBinding] | None = None,
|
||||
input_map: dict[str, str] | None = None,
|
||||
output_map: dict[str, str] | None = None,
|
||||
@@ -481,9 +478,12 @@ class WorkflowDraftAuthoringApi:
|
||||
title: str | None = None,
|
||||
) -> DraftWorkspaceResult:
|
||||
"""Bootstrap the smallest patchable draft around one workflow capability."""
|
||||
draft_input, draft_with = _draft_input_maps(
|
||||
input=input,
|
||||
input_map=input_map,
|
||||
if input is not None and input_map is not None:
|
||||
raise ValueError("cannot mix canonical input bindings with input_map")
|
||||
input_payload = (
|
||||
[binding.model_dump(mode="json") for binding in input]
|
||||
if input is not None
|
||||
else input_bindings_payload(input_map or {}, {})
|
||||
)
|
||||
draft_output = _draft_output_map(output=output, output_map=output_map)
|
||||
outcomes = self._outcomes_for_capability(capability_name) or (
|
||||
@@ -492,7 +492,7 @@ class WorkflowDraftAuthoringApi:
|
||||
steps: dict[str, Any] = {
|
||||
DEFAULT_CALL_STEP_ID: {
|
||||
"use": capability_name,
|
||||
"input": input_bindings_payload(draft_input, draft_with),
|
||||
"input": input_payload,
|
||||
"output": output_bindings_payload(draft_output),
|
||||
}
|
||||
}
|
||||
@@ -537,7 +537,7 @@ class WorkflowDraftAuthoringApi:
|
||||
workspace_id: str,
|
||||
revision: int,
|
||||
step_id: str,
|
||||
bindings: Sequence[InputBinding],
|
||||
bindings: Sequence[StepInputBinding],
|
||||
) -> DraftWorkspaceResult:
|
||||
"""Replace one capability step's canonical input bindings atomically."""
|
||||
checked = self._workspace_if_revision_matches(
|
||||
@@ -604,7 +604,7 @@ class WorkflowDraftAuthoringApi:
|
||||
*,
|
||||
workspace: WorkflowDraftWorkspace,
|
||||
capability_name: str,
|
||||
bindings: Sequence[InputBinding],
|
||||
bindings: Sequence[StepInputBinding],
|
||||
) -> _ProjectedStepInputBindings:
|
||||
"""Validate canonical inputs and project missing workflow source schemas."""
|
||||
spec = self.context.specs.get_qualified_spec(capability_name)
|
||||
@@ -1259,7 +1259,7 @@ class WorkflowDraftAuthoringApi:
|
||||
route_from_outcome: str = DEFAULT_OK_OUTCOME,
|
||||
routes: dict[str, str] | None = None,
|
||||
input_map: dict[str, str] | None = None,
|
||||
input_bindings: Sequence[InputBinding] | None = None,
|
||||
input_bindings: Sequence[StepInputBinding] | None = None,
|
||||
bind_outputs: dict[str, str] | None = None,
|
||||
desc: str | None = None,
|
||||
retry: int | None = None,
|
||||
@@ -1349,7 +1349,7 @@ class WorkflowDraftAuthoringApi:
|
||||
)
|
||||
|
||||
if input_bindings is None:
|
||||
canonical_inputs = TypeAdapter(list[InputBinding]).validate_python(
|
||||
canonical_inputs = TypeAdapter(list[StepInputBinding]).validate_python(
|
||||
input_bindings_payload(input_map or {}, {})
|
||||
)
|
||||
else:
|
||||
|
||||
@@ -4,7 +4,7 @@ from typing import Self
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field, model_validator
|
||||
|
||||
from wf_core.models.steps import InputBinding
|
||||
from wf_core.models.steps import StepInputBinding
|
||||
|
||||
|
||||
class CapabilityStepUpdate(BaseModel):
|
||||
@@ -15,7 +15,7 @@ class CapabilityStepUpdate(BaseModel):
|
||||
desc: str | None = Field(default=None, min_length=1)
|
||||
retry: int | None = Field(default=None, ge=0)
|
||||
timeout_seconds: int | None = Field(default=None, gt=0)
|
||||
input: list[InputBinding] | None = None
|
||||
input: list[StepInputBinding] | None = None
|
||||
|
||||
@model_validator(mode="after")
|
||||
def validate_patch_shape(self) -> Self:
|
||||
|
||||
@@ -5,7 +5,7 @@ from typing import Any
|
||||
|
||||
from wf_artifacts import ArtifactKind
|
||||
from wf_artifacts.drafts.models import DraftStep
|
||||
from wf_core.models.steps import InputBinding, OutputBinding
|
||||
from wf_core.models.steps import InputBinding, OutputBinding, StepInputBinding
|
||||
|
||||
from .artifacts import WorkflowArtifactApi
|
||||
from .capabilities import WorkflowCapabilityApi
|
||||
@@ -457,7 +457,7 @@ class WorkflowApi:
|
||||
workspace_id: str,
|
||||
revision: int,
|
||||
step_id: str,
|
||||
bindings: Sequence[InputBinding],
|
||||
bindings: Sequence[StepInputBinding],
|
||||
) -> DraftWorkspaceResult:
|
||||
return await self.draft_authoring.set_step_input_bindings(
|
||||
workspace_id=workspace_id,
|
||||
@@ -570,7 +570,7 @@ class WorkflowApi:
|
||||
route_from_outcome: str = "ok",
|
||||
routes: dict[str, str] | None = None,
|
||||
input_map: dict[str, str] | None = None,
|
||||
input_bindings: Sequence[InputBinding] | None = None,
|
||||
input_bindings: Sequence[StepInputBinding] | None = None,
|
||||
bind_outputs: dict[str, str] | None = None,
|
||||
desc: str | None = None,
|
||||
retry: int | None = None,
|
||||
@@ -653,8 +653,8 @@ class WorkflowApi:
|
||||
input_schema: dict[str, Any],
|
||||
state_schema: dict[str, Any],
|
||||
output_schema: dict[str, Any],
|
||||
input: Sequence[Any] | None = None,
|
||||
output: Sequence[Any] | None = None,
|
||||
input: Sequence[StepInputBinding] | None = None,
|
||||
output: Sequence[OutputBinding] | None = None,
|
||||
input_map: dict[str, str] | None = None,
|
||||
output_map: dict[str, str] | None = None,
|
||||
error_message_source: Any | None = None,
|
||||
@@ -730,8 +730,8 @@ class WorkflowApi:
|
||||
input_schema: dict[str, Any] | None = None,
|
||||
state_schema: dict[str, Any] | None = None,
|
||||
output_schema: dict[str, Any] | None = None,
|
||||
input: Sequence[Any] | None = None,
|
||||
output: Sequence[Any] | None = None,
|
||||
input: Sequence[StepInputBinding] | None = None,
|
||||
output: Sequence[OutputBinding] | None = None,
|
||||
input_map: dict[str, str] | None = None,
|
||||
output_map: dict[str, str] | None = None,
|
||||
error_message_source: Any | None = None,
|
||||
|
||||
@@ -5,7 +5,7 @@ from typing import Any, Protocol
|
||||
|
||||
from wf_artifacts import ArtifactKind
|
||||
from wf_artifacts.drafts.models import DraftStep
|
||||
from wf_core.models.steps import InputBinding, OutputBinding
|
||||
from wf_core.models.steps import InputBinding, OutputBinding, StepInputBinding
|
||||
|
||||
from .draft_authoring import RouteSource
|
||||
from .draft_updates import CapabilityStepUpdate
|
||||
@@ -117,8 +117,8 @@ class WorkflowDraftSurface(Protocol):
|
||||
input_schema: dict[str, Any] | None = None,
|
||||
state_schema: dict[str, Any] | None = None,
|
||||
output_schema: dict[str, Any] | None = None,
|
||||
input: Sequence[Any] | None = None,
|
||||
output: Sequence[Any] | None = None,
|
||||
input: Sequence[StepInputBinding] | None = None,
|
||||
output: Sequence[OutputBinding] | None = None,
|
||||
input_map: dict[str, str] | None = None,
|
||||
output_map: dict[str, str] | None = None,
|
||||
error_message_source: Any | None = None,
|
||||
@@ -207,7 +207,7 @@ class WorkflowDraftSurface(Protocol):
|
||||
workspace_id: str,
|
||||
revision: int,
|
||||
step_id: str,
|
||||
bindings: Sequence[InputBinding],
|
||||
bindings: Sequence[StepInputBinding],
|
||||
) -> DraftWorkspaceResult: ...
|
||||
|
||||
async def set_step_output_bindings(
|
||||
@@ -276,7 +276,7 @@ class WorkflowDraftSurface(Protocol):
|
||||
route_from_outcome: str = "ok",
|
||||
routes: dict[str, str] | None = None,
|
||||
input_map: dict[str, str] | None = None,
|
||||
input_bindings: Sequence[InputBinding] | None = None,
|
||||
input_bindings: Sequence[StepInputBinding] | None = None,
|
||||
bind_outputs: dict[str, str] | None = None,
|
||||
desc: str | None = None,
|
||||
retry: int | None = None,
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Sequence
|
||||
from typing import Any, cast
|
||||
from typing import Any
|
||||
|
||||
from wf_authoring import WorkflowBuilder
|
||||
from wf_authoring.builder.mapping import InputBindingArg
|
||||
from wf_authoring.dsl import PathExpr
|
||||
from wf_core import JoinNode, SubgraphNode, Workflow
|
||||
from wf_core.paths import GraphSourcePath
|
||||
@@ -54,9 +52,7 @@ def _add_step(builder: WorkflowBuilder, step_id: str, step: DraftStep):
|
||||
return builder.use_ref(
|
||||
step.use,
|
||||
id=step_id,
|
||||
# Task 1 persists composite inputs; the builder input union is
|
||||
# widened in the later Python API carry-through task.
|
||||
input=cast(Sequence[InputBindingArg], step.input),
|
||||
input=step.input,
|
||||
output=step.output,
|
||||
desc=step.desc,
|
||||
)
|
||||
|
||||
@@ -31,11 +31,11 @@ from wf_core.errors import WorkflowExecutionError
|
||||
from wf_core.models.conditions import BinaryCondition, ExistsCondition, PathOperand
|
||||
from wf_core.models.conditions import Condition as CoreCondition
|
||||
from wf_core.models.steps import (
|
||||
InputBinding,
|
||||
InputPathBinding,
|
||||
InputValueBinding,
|
||||
OutputBinding,
|
||||
Step,
|
||||
StepInputBinding,
|
||||
)
|
||||
from wf_core.paths import GraphSourcePath, LocalPath, StatePath
|
||||
from wf_core.runtime.ops.merges import ReducerDefinition
|
||||
@@ -50,17 +50,17 @@ from ..schemas import SchemaLike, StateSchemaLike, schema_ref_from, state_schema
|
||||
from ..subgraph import subgraph_ref
|
||||
from .ids import next_step_id, slug_id
|
||||
from .mapping import (
|
||||
InputBindingArg,
|
||||
MapArg,
|
||||
OutputBindingArg,
|
||||
StepInputBindingArg,
|
||||
auto_input_map,
|
||||
auto_output_map,
|
||||
coerce_path,
|
||||
normalize_input_bindings,
|
||||
normalize_input_mapping,
|
||||
normalize_input_values,
|
||||
normalize_output_bindings,
|
||||
normalize_output_mapping,
|
||||
normalize_step_input_bindings,
|
||||
)
|
||||
from .refs import (
|
||||
BranchRef,
|
||||
@@ -110,7 +110,7 @@ def _capability_ref_name(name: str | CapabilityRef) -> str:
|
||||
def _canonical_input_bindings(
|
||||
in_map: Mapping[GraphSourcePath, LocalPath],
|
||||
input_values: Mapping[LocalPath, Any],
|
||||
) -> list[InputBinding]:
|
||||
) -> list[StepInputBinding]:
|
||||
"""Convert typed authoring maps into canonical core input bindings."""
|
||||
value_bindings = [
|
||||
InputValueBinding(target=target, value=value)
|
||||
@@ -229,7 +229,7 @@ class WorkflowBuilder:
|
||||
spec: NodeSpec[Any, Any],
|
||||
*,
|
||||
id: str | None = None,
|
||||
input: Sequence[InputBindingArg] | None = None,
|
||||
input: Sequence[StepInputBindingArg] | None = None,
|
||||
output: Sequence[OutputBindingArg] | None = None,
|
||||
desc: str | None = None,
|
||||
) -> NodeUse: ...
|
||||
@@ -252,7 +252,7 @@ class WorkflowBuilder:
|
||||
spec: NodeSpec[Any, Any],
|
||||
*,
|
||||
id: str | None = None,
|
||||
input: Sequence[InputBindingArg] | None = None,
|
||||
input: Sequence[StepInputBindingArg] | None = None,
|
||||
output: Sequence[OutputBindingArg] | None = None,
|
||||
in_map: MapArg | None = None,
|
||||
input_values: Mapping[Any, Any] | None = None,
|
||||
@@ -275,7 +275,7 @@ class WorkflowBuilder:
|
||||
normalized_input_schema = cast(SchemaRef, self.input_schema)
|
||||
normalized_state_schema = cast(StateSchema, self.state_schema)
|
||||
if input is not None:
|
||||
node_input = normalize_input_bindings(input)
|
||||
node_input = normalize_step_input_bindings(input)
|
||||
else:
|
||||
raw_in_map = (
|
||||
auto_input_map(
|
||||
@@ -318,7 +318,7 @@ class WorkflowBuilder:
|
||||
name: str | CapabilityRef,
|
||||
*,
|
||||
id: str | None = None,
|
||||
input: Sequence[InputBindingArg] | None = None,
|
||||
input: Sequence[StepInputBindingArg] | None = None,
|
||||
output: Sequence[OutputBindingArg] | None = None,
|
||||
desc: str | None = None,
|
||||
) -> NodeUse: ...
|
||||
@@ -341,7 +341,7 @@ class WorkflowBuilder:
|
||||
name: str | CapabilityRef,
|
||||
*,
|
||||
id: str | None = None,
|
||||
input: Sequence[InputBindingArg] | None = None,
|
||||
input: Sequence[StepInputBindingArg] | None = None,
|
||||
output: Sequence[OutputBindingArg] | None = None,
|
||||
in_map: MapArg | None = None,
|
||||
input_values: Mapping[Any, Any] | None = None,
|
||||
@@ -368,7 +368,7 @@ class WorkflowBuilder:
|
||||
out_map=out_map,
|
||||
)
|
||||
node_input = (
|
||||
normalize_input_bindings(input)
|
||||
normalize_step_input_bindings(input)
|
||||
if input is not None
|
||||
else _canonical_input_bindings(
|
||||
normalize_input_mapping(in_map),
|
||||
@@ -397,7 +397,7 @@ class WorkflowBuilder:
|
||||
*,
|
||||
workflow: Workflow,
|
||||
id: str | None = None,
|
||||
input: Sequence[InputBindingArg] | None = None,
|
||||
input: Sequence[StepInputBindingArg] | None = None,
|
||||
output: Sequence[OutputBindingArg] | None = None,
|
||||
workflow_ref: WorkflowRef | str | Mapping[str, object] | None = None,
|
||||
desc: str | None = None,
|
||||
@@ -412,7 +412,7 @@ class WorkflowBuilder:
|
||||
node = subgraph_ref(
|
||||
id=id or self._next_step_id(_workflow_ref_base(workflow_ref, workflow)),
|
||||
workflow=workflow,
|
||||
input=normalize_input_bindings(input),
|
||||
input=normalize_step_input_bindings(input),
|
||||
output=normalize_output_bindings(output),
|
||||
workflow_ref=workflow_ref,
|
||||
desc=desc,
|
||||
@@ -600,7 +600,7 @@ class WorkflowBuilder:
|
||||
*,
|
||||
id: str | None = None,
|
||||
kind: str,
|
||||
request: Sequence[InputBindingArg] | None = None,
|
||||
request: Sequence[StepInputBindingArg] | None = None,
|
||||
resume: Sequence[OutputBindingArg] | None = None,
|
||||
outcomes: list[str] | None = None,
|
||||
request_schema: Mapping[str, Any] | None = None,
|
||||
@@ -626,7 +626,7 @@ class WorkflowBuilder:
|
||||
*,
|
||||
id: str | None = None,
|
||||
kind: str,
|
||||
request: Sequence[InputBindingArg] | None = None,
|
||||
request: Sequence[StepInputBindingArg] | None = None,
|
||||
resume: Sequence[OutputBindingArg] | None = None,
|
||||
request_map: MapArg | None = None,
|
||||
out_map: MapArg | None = None,
|
||||
@@ -646,7 +646,7 @@ class WorkflowBuilder:
|
||||
stacklevel=2,
|
||||
)
|
||||
request_bindings = (
|
||||
normalize_input_bindings(request)
|
||||
normalize_step_input_bindings(request)
|
||||
if request is not None
|
||||
else _canonical_input_bindings(
|
||||
normalize_input_mapping(request_map),
|
||||
|
||||
@@ -5,10 +5,11 @@ from typing import Any, TypeAlias
|
||||
|
||||
from wf_core import SchemaRef, StateSchema
|
||||
from wf_core.models.steps import (
|
||||
InputBinding,
|
||||
InputExpressionBinding,
|
||||
InputPathBinding,
|
||||
InputValueBinding,
|
||||
OutputBinding,
|
||||
StepInputBinding,
|
||||
)
|
||||
from wf_core.paths import GraphSourcePath, LocalPath, StatePath
|
||||
|
||||
@@ -24,7 +25,7 @@ MapArg: TypeAlias = Mapping[Any, Any]
|
||||
InputMap: TypeAlias = dict[GraphSourcePath, LocalPath]
|
||||
OutputMap: TypeAlias = dict[LocalPath, StatePath]
|
||||
InputValues: TypeAlias = dict[LocalPath, Any]
|
||||
InputBindingArg: TypeAlias = InputBinding | Mapping[str, object]
|
||||
StepInputBindingArg: TypeAlias = StepInputBinding | Mapping[str, object]
|
||||
OutputBindingArg: TypeAlias = OutputBinding | Mapping[str, object]
|
||||
|
||||
|
||||
@@ -78,20 +79,24 @@ def normalize_input_values(mapping: Mapping[Any, Any] | None) -> InputValues:
|
||||
return {coerce_local_path(target): value for target, value in mapping.items()}
|
||||
|
||||
|
||||
def normalize_input_bindings(
|
||||
bindings: Sequence[InputBindingArg] | None,
|
||||
) -> list[InputBinding]:
|
||||
"""Validate canonical input binding structs for WorkflowBuilder.use()."""
|
||||
def normalize_step_input_bindings(
|
||||
bindings: Sequence[StepInputBindingArg] | None,
|
||||
) -> list[StepInputBinding]:
|
||||
"""Normalize canonical node-local bindings, including expression records."""
|
||||
if bindings is None:
|
||||
return []
|
||||
normalized: list[InputBinding] = []
|
||||
normalized: list[StepInputBinding] = []
|
||||
for binding in bindings:
|
||||
if isinstance(binding, InputPathBinding | InputValueBinding):
|
||||
if isinstance(
|
||||
binding, InputPathBinding | InputValueBinding | InputExpressionBinding
|
||||
):
|
||||
normalized.append(binding)
|
||||
continue
|
||||
if not isinstance(binding, Mapping):
|
||||
raise TypeError(f"unsupported input binding {binding!r}")
|
||||
if "path" in binding:
|
||||
if "expression" in binding:
|
||||
normalized.append(InputExpressionBinding.model_validate(binding))
|
||||
elif "path" in binding:
|
||||
normalized.append(InputPathBinding.model_validate(binding))
|
||||
elif "value" in binding:
|
||||
normalized.append(InputValueBinding.model_validate(binding))
|
||||
|
||||
@@ -14,7 +14,7 @@ from wf_core import (
|
||||
execute_workflow_async,
|
||||
workflow_ref_from,
|
||||
)
|
||||
from wf_core.models.steps import InputBinding, OutputBinding
|
||||
from wf_core.models.steps import OutputBinding, StepInputBinding
|
||||
|
||||
from .nodes import NodeSpec
|
||||
|
||||
@@ -26,7 +26,7 @@ def subgraph_ref(
|
||||
*,
|
||||
id: str,
|
||||
workflow: Workflow,
|
||||
input: list[InputBinding] | None = None,
|
||||
input: list[StepInputBinding] | None = None,
|
||||
output: list[OutputBinding] | None = None,
|
||||
workflow_ref: WorkflowRef | str | Mapping[str, object] | None = None,
|
||||
desc: str | None = None,
|
||||
|
||||
@@ -137,7 +137,10 @@ def add_step_from_capability(
|
||||
Path | None,
|
||||
typer.Option(
|
||||
"--bindings-file",
|
||||
help="Ordered canonical JSON input-binding list.",
|
||||
help=(
|
||||
"Ordered canonical JSON node-input binding list; use this file "
|
||||
"for composite expressions."
|
||||
),
|
||||
),
|
||||
] = None,
|
||||
output_mapping: Annotated[
|
||||
|
||||
@@ -13,10 +13,12 @@ from wf_core.models.steps import (
|
||||
InputPathBinding,
|
||||
InputValueBinding,
|
||||
OutputBinding,
|
||||
StepInputBinding,
|
||||
)
|
||||
from wf_core.paths import GraphSourcePath, LocalPath, PathResolutionError, StatePath
|
||||
|
||||
_INPUT_BINDINGS_ADAPTER = TypeAdapter(list[InputBinding])
|
||||
_STEP_INPUT_BINDINGS_ADAPTER = TypeAdapter(list[StepInputBinding])
|
||||
_WORKFLOW_OUTPUT_BINDINGS_ADAPTER = TypeAdapter(list[InputBinding])
|
||||
_OUTPUT_BINDINGS_ADAPTER = TypeAdapter(list[OutputBinding])
|
||||
|
||||
|
||||
@@ -209,10 +211,10 @@ def _parse_input_value_binding_flags(
|
||||
return bindings
|
||||
|
||||
|
||||
def parse_step_input_bindings_file(path: Path) -> list[InputBinding]:
|
||||
"""Read and validate an ordered canonical input-binding list."""
|
||||
def parse_step_input_bindings_file(path: Path) -> list[StepInputBinding]:
|
||||
"""Read node-local bindings, including expressions, from canonical JSON."""
|
||||
try:
|
||||
return _INPUT_BINDINGS_ADAPTER.validate_python(
|
||||
return _STEP_INPUT_BINDINGS_ADAPTER.validate_python(
|
||||
parse_json_file(path, option_name="--bindings-file")
|
||||
)
|
||||
except ValidationError as exc:
|
||||
@@ -240,7 +242,7 @@ def parse_workflow_output_value_flags(
|
||||
def parse_workflow_output_bindings_file(path: Path) -> list[InputBinding]:
|
||||
"""Read an ordered canonical workflow-output binding list."""
|
||||
try:
|
||||
return _INPUT_BINDINGS_ADAPTER.validate_python(
|
||||
return _WORKFLOW_OUTPUT_BINDINGS_ADAPTER.validate_python(
|
||||
parse_json_file(path, option_name="--bindings-file")
|
||||
)
|
||||
except ValidationError as exc:
|
||||
|
||||
@@ -88,7 +88,10 @@ def update_capability_step(
|
||||
Path | None,
|
||||
typer.Option(
|
||||
"--bindings-file",
|
||||
help="Ordered canonical JSON input-binding list.",
|
||||
help=(
|
||||
"Ordered canonical JSON node-input binding list; use this file "
|
||||
"for composite expressions."
|
||||
),
|
||||
),
|
||||
] = None,
|
||||
clear_input: Annotated[
|
||||
|
||||
@@ -468,7 +468,10 @@ def set_step_input(
|
||||
Path | None,
|
||||
typer.Option(
|
||||
"--bindings-file",
|
||||
help="JSON file containing the complete ordered canonical binding list.",
|
||||
help=(
|
||||
"JSON file containing the complete ordered node-input binding "
|
||||
"list; use this file for composite expressions."
|
||||
),
|
||||
),
|
||||
] = None,
|
||||
clear: Annotated[
|
||||
@@ -490,7 +493,9 @@ def set_step_input(
|
||||
"""Replace one step's canonical inputs, or use compatibility map merge.
|
||||
|
||||
By default, repeated --map and --value flags replace the complete ordered
|
||||
binding list. --bindings-file replaces from canonical JSON, while --clear
|
||||
binding list. --bindings-file replaces from canonical JSON and is the only
|
||||
way to submit composite expressions; inline --map/--value flags remain
|
||||
simple-only. --clear
|
||||
sends an empty list. Use --merge only with map-only compatibility edits;
|
||||
it rejects existing bindings that cannot round-trip safely. Canonical
|
||||
fan-out or reordered path/literal bindings require complete canonical
|
||||
|
||||
@@ -137,6 +137,7 @@ EXPLAIN_CARDS: tuple[ExplainCard, ...] = (
|
||||
"Run `wf schema InputPathBinding` to confirm binding shape.",
|
||||
"Inspect the draft input and state schemas.",
|
||||
"Use `wf draft set-input --merge` to repair step input bindings.",
|
||||
"Use `wf draft set-input --bindings-file bindings.json` for composite input expressions; inline --map/--value flags remain simple-only.",
|
||||
"Patch the draft input_schema/state_schema when the workflow genuinely needs a new path.",
|
||||
"Run `wf draft validate <workspace_id>` after the edit.",
|
||||
],
|
||||
|
||||
@@ -188,8 +188,9 @@ InputBinding = Annotated[
|
||||
Field(
|
||||
union_mode="left_to_right",
|
||||
description=(
|
||||
"Canonical node input binding. Use either a path binding with "
|
||||
"`path`, or a literal binding with `value`; do not provide both."
|
||||
"Simple canonical binding for node inputs or workflow outputs. Use "
|
||||
"either a path binding with `path`, or a literal binding with `value`; "
|
||||
"composite `expression` bindings are node-local only."
|
||||
),
|
||||
),
|
||||
]
|
||||
|
||||
@@ -8,7 +8,7 @@ from wf_api import CapabilityStepUpdate
|
||||
from wf_api.next_actions import NextActionPatchExample, NextActions
|
||||
from wf_artifacts import ArtifactKind
|
||||
from wf_artifacts.draft_workspaces.models import WORKSPACE_ID_PATTERN
|
||||
from wf_core.models.steps import InputBinding, OutputBinding
|
||||
from wf_core.models.steps import InputBinding, OutputBinding, StepInputBinding
|
||||
from wf_core.paths import GraphSourcePath
|
||||
|
||||
WorkspaceId = Annotated[
|
||||
@@ -38,13 +38,14 @@ DraftPathMap = Annotated[
|
||||
]
|
||||
NonEmptyString = Annotated[str, Field(min_length=1)]
|
||||
DraftInputBindings = Annotated[
|
||||
list[InputBinding],
|
||||
list[StepInputBinding],
|
||||
Field(
|
||||
description=(
|
||||
"Canonical node input bindings. Use path bindings such as "
|
||||
"{'target': {'root': 'local', 'parts': ['text']}, "
|
||||
"'path': {'root': 'input', 'parts': ['text']}} or value bindings "
|
||||
"with {'target': ..., 'value': ...}."
|
||||
"with {'target': ..., 'value': ...}; composite expressions use "
|
||||
"the canonical {'target': ..., 'expression': ...} form."
|
||||
)
|
||||
),
|
||||
]
|
||||
|
||||
@@ -16,7 +16,7 @@ from wf_api.models import (
|
||||
)
|
||||
from wf_api.surface import RouteSource
|
||||
from wf_artifacts.drafts.models import DraftStep
|
||||
from wf_core.models.steps import InputBinding, OutputBinding
|
||||
from wf_core.models.steps import InputBinding, OutputBinding, StepInputBinding
|
||||
|
||||
from .base import RpcCaller
|
||||
|
||||
@@ -86,8 +86,8 @@ class RpcDraftClientMixin:
|
||||
input_schema: dict[str, Any] | None = None,
|
||||
state_schema: dict[str, Any] | None = None,
|
||||
output_schema: dict[str, Any] | None = None,
|
||||
input: Sequence[Any] | None = None,
|
||||
output: Sequence[Any] | None = None,
|
||||
input: Sequence[StepInputBinding] | None = None,
|
||||
output: Sequence[OutputBinding] | None = None,
|
||||
input_map: dict[str, str] | None = None,
|
||||
output_map: dict[str, str] | None = None,
|
||||
error_message_source: Any | None = None,
|
||||
@@ -104,8 +104,16 @@ class RpcDraftClientMixin:
|
||||
"input_schema": input_schema,
|
||||
"state_schema": state_schema,
|
||||
"output_schema": output_schema,
|
||||
"input": input,
|
||||
"output": output,
|
||||
"input": (
|
||||
[binding.model_dump(mode="json") for binding in input]
|
||||
if input is not None
|
||||
else None
|
||||
),
|
||||
"output": (
|
||||
[binding.model_dump(mode="json") for binding in output]
|
||||
if output is not None
|
||||
else None
|
||||
),
|
||||
"input_map": input_map,
|
||||
"output_map": output_map,
|
||||
"error_message_source": error_message_source,
|
||||
@@ -265,7 +273,7 @@ class RpcDraftClientMixin:
|
||||
workspace_id: str,
|
||||
revision: int,
|
||||
step_id: str,
|
||||
bindings: Sequence[InputBinding],
|
||||
bindings: Sequence[StepInputBinding],
|
||||
) -> DraftWorkspaceResult:
|
||||
return await _call_draft_workspace(
|
||||
self,
|
||||
@@ -405,7 +413,7 @@ class RpcDraftClientMixin:
|
||||
route_from_outcome: str = "ok",
|
||||
routes: dict[str, str] | None = None,
|
||||
input_map: dict[str, str] | None = None,
|
||||
input_bindings: Sequence[InputBinding] | None = None,
|
||||
input_bindings: Sequence[StepInputBinding] | None = None,
|
||||
bind_outputs: dict[str, str] | None = None,
|
||||
desc: str | None = None,
|
||||
retry: int | None = None,
|
||||
|
||||
@@ -7,7 +7,7 @@ from pydantic import BaseModel, ConfigDict, Field, model_validator
|
||||
from wf_api import CapabilityStepUpdate
|
||||
from wf_api.models import TraceRange
|
||||
from wf_artifacts.drafts.models import DraftStep
|
||||
from wf_core.models.steps import InputBinding, OutputBinding
|
||||
from wf_core.models.steps import InputBinding, OutputBinding, StepInputBinding
|
||||
|
||||
|
||||
class RpcParamsModel(BaseModel):
|
||||
@@ -75,8 +75,8 @@ class CreateDraftFromCapabilityParams(RpcParamsModel):
|
||||
input_schema: dict[str, Any] | None = None
|
||||
state_schema: dict[str, Any] | None = None
|
||||
output_schema: dict[str, Any] | None = None
|
||||
input: list[Any] | None = None
|
||||
output: list[Any] | None = None
|
||||
input: list[StepInputBinding] | None = None
|
||||
output: list[OutputBinding] | None = None
|
||||
input_map: dict[str, str] | None = None
|
||||
output_map: dict[str, str] | None = None
|
||||
error_message_source: Any | None = None
|
||||
@@ -220,7 +220,7 @@ class SetStepInputBindingsParams(RpcParamsModel):
|
||||
workspace_id: str = Field(min_length=1)
|
||||
revision: int = Field(ge=1)
|
||||
step_id: str = Field(min_length=1)
|
||||
bindings: list[InputBinding]
|
||||
bindings: list[StepInputBinding]
|
||||
|
||||
|
||||
class SetStepOutputBindingsParams(RpcParamsModel):
|
||||
@@ -275,7 +275,7 @@ class AddStepFromCapabilityParams(RpcParamsModel):
|
||||
route_from_outcome: str = Field(default="ok", min_length=1)
|
||||
routes: dict[str, str] | None = None
|
||||
input_map: dict[str, str] | None = None
|
||||
input_bindings: list[InputBinding] | None = None
|
||||
input_bindings: list[StepInputBinding] | None = None
|
||||
bind_outputs: dict[str, str] = Field(default_factory=dict)
|
||||
desc: str | None = Field(default=None, min_length=1)
|
||||
retry: int | None = Field(default=None, ge=0)
|
||||
|
||||
Reference in New Issue
Block a user