fix: advertise structural workflow paths in schemas

This commit is contained in:
lda
2026-06-30 01:33:02 +07:00 Verified
parent 0f340bb8a5
commit 30ad99fca1
6 changed files with 101 additions and 30 deletions
+13 -7
View File
@@ -18,15 +18,18 @@ class InputPathBinding(BaseModel):
target: LocalPath = Field(
description=(
"Node-local input path to populate. Use {'root': 'local', "
"'parts': ['field']} or {'root': 'local', 'parts': []} for the "
"whole node input payload."
"Node-local input path to populate. Prefer canonical strings such "
"as `field` or `.` for the whole node input payload. Structural "
"objects such as {'root': 'local', 'parts': ['field']} are also "
"accepted as input."
)
)
path: GraphSourcePath = Field(
description=(
"Workflow source path to read from input, state, or context. "
"Example: {'root': 'input', 'parts': ['text']}."
"Prefer canonical strings such as `input.text` or `state.report`. "
"Structural objects such as {'root': 'input', 'parts': ['text']} "
"are also accepted as input."
)
)
@@ -67,14 +70,17 @@ class OutputBinding(BaseModel):
source: LocalPath = Field(
description=(
"Node-local output path to read. Use {'root': 'local', 'parts': []} "
"to write the whole node output payload."
"Node-local output path to read. Prefer canonical strings such as "
"`result` or `.` for the whole node output payload. Structural "
"objects such as {'root': 'local', 'parts': []} are also accepted "
"as input."
)
)
target: StatePath = Field(
description=(
"Writable workflow state path. Bare state is invalid; use a field "
"path such as {'root': 'state', 'parts': ['echoed']}."
"path such as `state.echoed`. Structural objects such as "
"{'root': 'state', 'parts': ['echoed']} are also accepted as input."
)
)
+46 -6
View File
@@ -78,15 +78,47 @@ def format_toml_path_segments(parts: tuple[str, ...]) -> str:
)
def _path_json_schema(description: str) -> dict[str, Any]:
"""Return the canonical string schema for path fields.
def _path_json_schema(
description: str, *, roots: tuple[str, ...], allow_empty_parts: bool
) -> dict[str, Any]:
"""Return the public schema for path fields.
Structural objects remain input-only compatibility for persisted records.
New schemas and serializers expose the canonical TOML-key string form.
Strings are the canonical serialized form. Structural objects are still a
first-class input form, so the JSON Schema must advertise both; otherwise
machine clients tend to quote structural objects and produce invalid TOML
path strings.
"""
root_schema: dict[str, Any]
if len(roots) == 1:
root_schema = {"const": roots[0]}
else:
root_schema = {"enum": list(roots)}
min_items = 0 if allow_empty_parts else 1
return {
"type": "string",
"description": description,
"oneOf": [
{
"type": "string",
"description": "Canonical TOML-key path string.",
},
{
"type": "object",
"description": ("Structural path object accepted as input."),
"additionalProperties": False,
"required": ["root", "parts"],
"properties": {
"root": {
"type": "string",
**root_schema,
},
"parts": {
"type": "array",
"items": {"type": "string", "minLength": 1},
"minItems": min_items,
},
},
},
],
}
@@ -185,6 +217,8 @@ class LocalPath:
) -> dict[str, Any]:
return _path_json_schema(
"Node-local path. Use the root marker `.` for the whole payload.",
roots=("local",),
allow_empty_parts=True,
)
@@ -269,6 +303,8 @@ class GraphSourcePath:
) -> dict[str, Any]:
return _path_json_schema(
"Readable graph path rooted at input, state, or context.",
roots=("input", "state", "context"),
allow_empty_parts=True,
)
@@ -334,7 +370,11 @@ class StatePath:
def __get_pydantic_json_schema__(
cls, _core_schema: core_schema.CoreSchema, _handler: object
) -> dict[str, Any]:
return _path_json_schema("Writable workflow state path.")
return _path_json_schema(
"Writable workflow state path.",
roots=("state",),
allow_empty_parts=False,
)
def split_graph_path(path: str | GraphSourcePath | StatePath) -> tuple[str, list[str]]: