p2: old style deprecated support + wf_authoring to use new stuff

holy moly file changes
This commit is contained in:
lda
2026-05-20 17:47:37 +07:00 Verified
parent 3683c23937
commit 1a2349e017
28 changed files with 702 additions and 137 deletions
+65 -21
View File
@@ -22,7 +22,14 @@ from wf_core import (
from wf_core.errors import WorkflowExecutionError
from wf_core.models.conditions import Condition as CoreCondition
from wf_core.models.conditions import BinaryCondition, ExistsCondition, PathOperand
from wf_core.models.steps import Step
from wf_core.models.steps import (
InputBinding,
InputPathBinding,
InputValueBinding,
OutputBinding,
Step,
)
from wf_core.paths import GraphSourcePath, LocalPath, StatePath
from wf_core.runtime.ops.merges import ReducerDefinition
from ..dsl import Expr, PathArg, PathExpr, compile_condition
@@ -52,14 +59,41 @@ from .refs import (
def _condition_base(condition: CoreCondition) -> str:
"""Return a small source-derived id base when one path is obvious."""
if isinstance(condition, ExistsCondition):
return slug_id(condition.path)
return slug_id(str(condition.path))
if isinstance(condition, BinaryCondition) and isinstance(
condition.left, PathOperand
):
return slug_id(condition.left.path)
return slug_id(str(condition.left.path))
return "condition"
def _canonical_input_bindings(
in_map: Mapping[str, str],
input_values: Mapping[str, Any],
) -> list[InputBinding]:
"""Convert authoring compatibility maps into canonical core input bindings."""
value_bindings = [
InputValueBinding(target=LocalPath.parse(target), value=value)
for target, value in input_values.items()
]
path_bindings = [
InputPathBinding(
target=LocalPath.parse(target),
path=GraphSourcePath.parse(path),
)
for path, target in in_map.items()
]
return [*value_bindings, *path_bindings]
def _canonical_output_bindings(out_map: Mapping[str, str]) -> list[OutputBinding]:
"""Convert authoring compatibility maps into canonical core output bindings."""
return [
OutputBinding(source=LocalPath.parse(source), target=StatePath.parse(target))
for source, target in out_map.items()
]
@dataclass(slots=True)
class WorkflowBuilder:
name: str
@@ -91,26 +125,31 @@ class WorkflowBuilder:
self.node_specs[spec.name] = spec
normalized_input_schema = cast(SchemaRef, self.input_schema)
normalized_state_schema = cast(StateSchema, self.state_schema)
normalized_in_map = (
auto_input_map(
spec,
input_schema=normalized_input_schema,
state_schema=normalized_state_schema,
)
if in_map is None
else normalize_mapping(in_map)
)
normalized_input_values = dict(input_values or {})
normalized_out_map = (
auto_output_map(spec, state_schema=normalized_state_schema)
if out_map is None
else normalize_mapping(out_map)
)
node = NodeUse(
id=id or self._next_step_id(slug_id(spec.name)),
type="node",
node=spec.name,
desc=desc or spec.description,
in_map=(
auto_input_map(
spec,
input_schema=normalized_input_schema,
state_schema=normalized_state_schema,
)
if in_map is None
else normalize_mapping(in_map)
),
input_values=dict(input_values or {}),
out_map=(
auto_output_map(spec, state_schema=normalized_state_schema)
if out_map is None
else normalize_mapping(out_map)
input=_canonical_input_bindings(
normalized_in_map,
normalized_input_values,
),
output=_canonical_output_bindings(normalized_out_map),
)
self.nodes.append(node)
return node
@@ -132,14 +171,19 @@ class WorkflowBuilder:
hatch for MCP/saved-workflow capability refs that are resolved later by
the environment runner into node definitions and registry handlers.
"""
normalized_in_map = normalize_mapping(in_map)
normalized_input_values = dict(input_values or {})
normalized_out_map = normalize_mapping(out_map)
node = NodeUse(
id=id or self._next_step_id(slug_id(name)),
type="node",
node=name,
desc=desc,
in_map=normalize_mapping(in_map),
input_values=dict(input_values or {}),
out_map=normalize_mapping(out_map),
input=_canonical_input_bindings(
normalized_in_map,
normalized_input_values,
),
output=_canonical_output_bindings(normalized_out_map),
)
self.nodes.append(node)
return node
@@ -326,7 +370,7 @@ class WorkflowBuilder:
conditions: list[ConditionNode] = []
default_target = self._resolve_branch_ref(default)
previous_condition: ConditionNode | None = None
condition_base = id or slug_id(value.path)
condition_base = id or slug_id(str(value.path))
for case_value, target in cases.items():
condition = self.condition(
id=self._next_step_id(condition_base),
+3 -2
View File
@@ -51,10 +51,11 @@ def auto_output_map(
state_schema: StateSchema,
) -> dict[str, str]:
"""Map node output fields back into matching state fields."""
state_fields = state_schema.field_map()
return {
field: f"state.{field}"
for field in spec.output_model.model_json_schema().get("properties", {})
if field in state_schema.fields
if field in state_fields
}
@@ -64,7 +65,7 @@ def _auto_source_path(
input_schema: SchemaRef,
state_schema: StateSchema,
) -> str:
if field in state_schema.fields:
if field in state_schema.root_fields():
return f"state.{field}"
if field in input_schema.properties:
return f"input.{field}"
+7 -4
View File
@@ -12,15 +12,16 @@ from wf_core.models.conditions import (
PathOperand,
VariadicCondition,
)
from wf_core.paths import GraphSourcePath
from .paths import GraphPath, context_path, input_path, state_path
def _operand(value: object) -> PathOperand | LiteralOperand:
if isinstance(value, PathExpr):
return PathOperand(path=value.path)
return PathOperand(path=GraphSourcePath.parse(value.path))
if isinstance(value, GraphPath):
return PathOperand(path=value.value)
return PathOperand(path=GraphSourcePath.parse(value.value))
return LiteralOperand(value=value)
@@ -61,7 +62,7 @@ class PathExpr:
return Expr(
BinaryCondition(
op=op,
left=PathOperand(path=self.path),
left=PathOperand(path=GraphSourcePath.parse(self.path)),
right=_operand(other),
)
)
@@ -126,7 +127,9 @@ def context(field: str) -> PathExpr:
def exists(value: PathExpr | GraphPath) -> Expr:
return Expr(ExistsCondition(op="exists", path=_path_str(value)))
return Expr(
ExistsCondition(op="exists", path=GraphSourcePath.parse(_path_str(value)))
)
def not_(value: Condition | Expr) -> Expr:
+12 -6
View File
@@ -2,11 +2,17 @@ from __future__ import annotations
from dataclasses import dataclass
from wf_core.paths import GraphSourcePath
@dataclass(frozen=True, slots=True)
class GraphPath:
value: str
def __post_init__(self) -> None:
"""Validate authoring paths at construction so invalid roots fail early."""
object.__setattr__(self, "value", str(GraphSourcePath.parse(self.value)))
def __str__(self) -> str:
return self.value
@@ -15,13 +21,13 @@ def graph_path(value: str) -> GraphPath:
return GraphPath(value)
def input_path(field: str) -> GraphPath:
return GraphPath(f"input.{field}")
def input_path(*parts: str) -> GraphPath:
return GraphPath(str(GraphSourcePath.input(*parts)))
def state_path(field: str) -> GraphPath:
return GraphPath(f"state.{field}")
def state_path(*parts: str) -> GraphPath:
return GraphPath(str(GraphSourcePath.state(*parts)))
def context_path(field: str) -> GraphPath:
return GraphPath(f"context.{field}")
def context_path(*parts: str) -> GraphPath:
return GraphPath(str(GraphSourcePath.context(*parts)))
+1 -1
View File
@@ -61,7 +61,7 @@ def state_schema_from(value: StateSchemaLike) -> StateSchema:
)
for path, property_schema in _flatten_state_properties(schema)
}
return StateSchema(fields=fields)
return StateSchema.from_field_map(fields)
def _reducer_ref_from(value: ReducerLike) -> ReducerRef: