Fix workflow authoring contract integration
This commit is contained in:
@@ -54,15 +54,21 @@ def schema_path_options(
|
||||
|
||||
normalized_uses = list(uses)
|
||||
options: list[AuthoringPathOptionPayload] = []
|
||||
_append_schema_options(
|
||||
schema,
|
||||
location=(),
|
||||
prefix=prefix,
|
||||
origin=origin,
|
||||
uses=normalized_uses,
|
||||
options=options,
|
||||
active_references=frozenset(),
|
||||
)
|
||||
try:
|
||||
_append_schema_options(
|
||||
schema,
|
||||
location=(),
|
||||
prefix=prefix,
|
||||
origin=origin,
|
||||
uses=normalized_uses,
|
||||
options=options,
|
||||
active_references=frozenset(),
|
||||
depth=0,
|
||||
)
|
||||
except RecursionError as exc:
|
||||
raise ValueError(
|
||||
"schema nesting exceeds the authoring traversal limit"
|
||||
) from exc
|
||||
return options
|
||||
|
||||
|
||||
@@ -100,7 +106,7 @@ def project_authoring_contract_inventory(
|
||||
state_sources = schema_path_options(
|
||||
state_schema,
|
||||
root="state",
|
||||
uses=["step_input", "step_output_source", "workflow_output"],
|
||||
uses=["step_input", "workflow_output"],
|
||||
)
|
||||
state_targets = schema_path_options(
|
||||
state_schema,
|
||||
@@ -153,7 +159,7 @@ def project_authoring_step_contract(
|
||||
"output_sources": schema_path_options(
|
||||
output_schema,
|
||||
root="step_output",
|
||||
uses=["step_output_source", "workflow_output"],
|
||||
uses=["step_output_source"],
|
||||
),
|
||||
"outcomes": list(outcomes),
|
||||
}
|
||||
@@ -241,7 +247,10 @@ def _append_schema_options(
|
||||
uses: list[AuthoringPathUse],
|
||||
options: list[AuthoringPathOptionPayload],
|
||||
active_references: frozenset[str],
|
||||
depth: int,
|
||||
) -> None:
|
||||
if depth >= _MAX_LOCAL_SCHEMA_REFERENCE_DEPTH:
|
||||
return
|
||||
fragment = schema_fragment_at_location(schema, location)
|
||||
resolved = _resolve_local_reference(schema, fragment)
|
||||
properties = resolved.get("properties")
|
||||
@@ -296,6 +305,7 @@ def _append_schema_options(
|
||||
uses=uses,
|
||||
options=options,
|
||||
active_references=next_active_references,
|
||||
depth=depth + 1,
|
||||
)
|
||||
|
||||
|
||||
|
||||
+14
-2
@@ -424,12 +424,24 @@ class WorkflowApi:
|
||||
description = raw_step.get("desc")
|
||||
if not isinstance(description, str):
|
||||
description = resolved_contract.description
|
||||
input_schema = _authoring_schema(
|
||||
resolved_contract.input_schema,
|
||||
field_name=f"step {raw_step_id!r} input schema",
|
||||
root="step_input",
|
||||
warnings=warnings,
|
||||
)
|
||||
output_schema = _authoring_schema(
|
||||
resolved_contract.output_schema,
|
||||
field_name=f"step {raw_step_id!r} output schema",
|
||||
root="step_output",
|
||||
warnings=warnings,
|
||||
)
|
||||
projected_contract = project_authoring_step_contract(
|
||||
step_id=raw_step_id,
|
||||
label=_step_label(raw_step_id),
|
||||
description=description,
|
||||
input_schema=resolved_contract.input_schema,
|
||||
output_schema=resolved_contract.output_schema,
|
||||
input_schema=input_schema,
|
||||
output_schema=output_schema,
|
||||
outcomes=resolved_contract.outcomes,
|
||||
)
|
||||
entry_steps.append(projected_contract)
|
||||
|
||||
@@ -136,6 +136,7 @@ def _analyze(workflow: Workflow) -> _ContextAnalysis:
|
||||
foreach_nodes,
|
||||
node_id,
|
||||
scopes,
|
||||
scopes_by_node,
|
||||
)
|
||||
return _ContextAnalysis(fields_by_node, tuple(warnings.values))
|
||||
|
||||
@@ -145,6 +146,7 @@ def _available_fields(
|
||||
foreach_nodes: Mapping[str, ForeachNode],
|
||||
node_id: str,
|
||||
scopes: set[FrameScope],
|
||||
scopes_by_node: Mapping[str, set[FrameScope]],
|
||||
) -> tuple[ContextFieldAvailability, ...]:
|
||||
del node_id
|
||||
fields_by_name: dict[str, ContextFieldContract] = {}
|
||||
@@ -158,7 +160,13 @@ def _available_fields(
|
||||
*contracts,
|
||||
*foreach_context_fields(
|
||||
foreach.as_,
|
||||
_foreach_item_schema(workflow, foreach, scope, foreach_nodes),
|
||||
_foreach_item_schema(
|
||||
workflow,
|
||||
foreach,
|
||||
scopes_by_node.get(foreach.id, {None}),
|
||||
foreach_nodes,
|
||||
scopes_by_node,
|
||||
),
|
||||
),
|
||||
)
|
||||
for contract in contracts:
|
||||
@@ -195,12 +203,26 @@ def _available_fields(
|
||||
def _foreach_item_schema(
|
||||
workflow: Workflow,
|
||||
foreach: ForeachNode,
|
||||
active_scope: FrameScope,
|
||||
source_scopes: set[FrameScope],
|
||||
foreach_nodes: Mapping[str, ForeachNode],
|
||||
scopes_by_node: Mapping[str, set[FrameScope]],
|
||||
) -> ContextSchema:
|
||||
source_schema = _schema_at_path(
|
||||
workflow, foreach.over.root, foreach.over.parts, active_scope, foreach_nodes
|
||||
)
|
||||
source_schemas = [
|
||||
_schema_at_path(
|
||||
workflow,
|
||||
foreach.over.root,
|
||||
foreach.over.parts,
|
||||
source_scope,
|
||||
foreach_nodes,
|
||||
scopes_by_node,
|
||||
)
|
||||
for source_scope in sorted(source_scopes, key=lambda value: value or "")
|
||||
]
|
||||
if not source_schemas or any(
|
||||
schema != source_schemas[0] for schema in source_schemas
|
||||
):
|
||||
return {}
|
||||
source_schema = source_schemas[0]
|
||||
if not isinstance(source_schema, Mapping):
|
||||
return {}
|
||||
source_type = source_schema.get("type")
|
||||
@@ -212,7 +234,13 @@ def _foreach_item_schema(
|
||||
return {}
|
||||
try:
|
||||
resolved_items = _resolve_local_reference(
|
||||
_schema_document(workflow, foreach.over.root), items
|
||||
_schema_document(
|
||||
workflow,
|
||||
foreach.over.root,
|
||||
foreach_nodes=foreach_nodes,
|
||||
scopes_by_node=scopes_by_node,
|
||||
),
|
||||
items,
|
||||
)
|
||||
except ValueError:
|
||||
return {}
|
||||
@@ -225,6 +253,7 @@ def _schema_at_path(
|
||||
parts: tuple[str, ...],
|
||||
active_scope: FrameScope,
|
||||
foreach_nodes: Mapping[str, ForeachNode],
|
||||
scopes_by_node: Mapping[str, set[FrameScope]],
|
||||
) -> Mapping[str, object] | None:
|
||||
try:
|
||||
schema_document = _schema_document(
|
||||
@@ -232,6 +261,7 @@ def _schema_at_path(
|
||||
root,
|
||||
active_scope=active_scope,
|
||||
foreach_nodes=foreach_nodes,
|
||||
scopes_by_node=scopes_by_node,
|
||||
)
|
||||
current: object = schema_document
|
||||
for part in parts:
|
||||
@@ -255,6 +285,7 @@ def _schema_document(
|
||||
*,
|
||||
active_scope: FrameScope = None,
|
||||
foreach_nodes: Mapping[str, ForeachNode] | None = None,
|
||||
scopes_by_node: Mapping[str, set[FrameScope]] | None = None,
|
||||
) -> Mapping[str, object]:
|
||||
if root == "input":
|
||||
return workflow.input_schema.model_dump(mode="json", exclude_none=True)
|
||||
@@ -264,7 +295,11 @@ def _schema_document(
|
||||
current: dict[str, object] = {
|
||||
field.name: field.schema for field in STANDARD_CONTEXT_FIELDS
|
||||
}
|
||||
if active_scope is not None and foreach_nodes is not None:
|
||||
if (
|
||||
active_scope is not None
|
||||
and foreach_nodes is not None
|
||||
and scopes_by_node is not None
|
||||
):
|
||||
foreach = foreach_nodes.get(active_scope)
|
||||
if foreach is not None:
|
||||
current.update(
|
||||
@@ -275,8 +310,9 @@ def _schema_document(
|
||||
_foreach_item_schema(
|
||||
workflow,
|
||||
foreach,
|
||||
None,
|
||||
scopes_by_node.get(foreach.id, {None}),
|
||||
foreach_nodes,
|
||||
scopes_by_node,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user