conversion fns

This commit is contained in:
lda
2026-05-25 03:36:02 +07:00 Verified
parent 84b931f821
commit 492226a596
4 changed files with 100 additions and 2 deletions
+13
View File
@@ -632,6 +632,19 @@ Saved workflow artifact names use a separate grammar and ref type:
`workflow.<artifact_id>.v<version>`. Artifact ids may contain dots, so this `workflow.<artifact_id>.v<version>`. Artifact ids may contain dots, so this
must not be parsed as a generic `CapabilityRef`. must not be parsed as a generic `CapabilityRef`.
When an artifact is used as a child workflow dependency, convert it to the core
`WorkflowRef` shape instead of reusing display strings:
```python
workflow_ref_from_artifact(artifact)
workflow_ref_from_capability(WorkflowCapabilityRef("echo_wrapper", 1))
workflow_capability_ref_from_workflow_ref(ref) # only for artifact-backed refs
```
This keeps the three identities separate: `WorkflowArtifact` is the saved
document, `WorkflowCapabilityRef` is the public callable capability name, and
`WorkflowRef` is the core subgraph dependency pointer.
The first implementation should prefer artifact validation and dependency The first implementation should prefer artifact validation and dependency
diagnostics before attempting persistent nested resume. diagnostics before attempting persistent nested resume.
+9 -1
View File
@@ -32,7 +32,12 @@ from .models import (
WorkflowArtifact, WorkflowArtifact,
WorkflowDeployment, WorkflowDeployment,
) )
from .refs import WorkflowCapabilityRef from .refs import (
WorkflowCapabilityRef,
workflow_capability_ref_from_workflow_ref,
workflow_ref_from_artifact,
workflow_ref_from_capability,
)
from .store import FileWorkflowArtifactStore, WorkflowArtifactStore from .store import FileWorkflowArtifactStore, WorkflowArtifactStore
from .validation import validate_deployment_dependencies from .validation import validate_deployment_dependencies
from .references import logical_ref_for_concrete_ref, normalize_plan_node_refs from .references import logical_ref_for_concrete_ref, normalize_plan_node_refs
@@ -70,4 +75,7 @@ __all__ = [
"summarize_draft_workspace", "summarize_draft_workspace",
"validate_deployment_dependencies", "validate_deployment_dependencies",
"validate_workflow_draft", "validate_workflow_draft",
"workflow_capability_ref_from_workflow_ref",
"workflow_ref_from_artifact",
"workflow_ref_from_capability",
] ]
+23
View File
@@ -5,6 +5,10 @@ from typing import Any
from pydantic_core import core_schema from pydantic_core import core_schema
from wf_core import WorkflowRef
from .models import WorkflowArtifact
@dataclass(frozen=True, slots=True) @dataclass(frozen=True, slots=True)
class WorkflowCapabilityRef: class WorkflowCapabilityRef:
@@ -70,3 +74,22 @@ class WorkflowCapabilityRef:
def _serialize(value: WorkflowCapabilityRef) -> dict[str, int | str]: def _serialize(value: WorkflowCapabilityRef) -> dict[str, int | str]:
"""Serialize canonical saved workflow refs without a display-name parser.""" """Serialize canonical saved workflow refs without a display-name parser."""
return {"artifact_id": value.artifact_id, "version": value.version} return {"artifact_id": value.artifact_id, "version": value.version}
def workflow_ref_from_artifact(artifact: WorkflowArtifact) -> WorkflowRef:
"""Return the core child-workflow ref for one saved artifact version."""
return WorkflowRef(artifact_id=artifact.id, version=artifact.version)
def workflow_ref_from_capability(ref: WorkflowCapabilityRef) -> WorkflowRef:
"""Return the core child-workflow ref for a workflow capability identity."""
return WorkflowRef(artifact_id=ref.artifact_id, version=ref.version)
def workflow_capability_ref_from_workflow_ref(
ref: WorkflowRef,
) -> WorkflowCapabilityRef:
"""Return the public capability ref for an artifact-backed workflow ref."""
if ref.artifact_id is None or ref.version is None:
raise ValueError("workflow capability refs require an artifact workflow ref")
return WorkflowCapabilityRef(artifact_id=ref.artifact_id, version=ref.version)
+55 -1
View File
@@ -3,7 +3,13 @@ from __future__ import annotations
import pytest import pytest
from pydantic import BaseModel, ValidationError from pydantic import BaseModel, ValidationError
from wf_artifacts import WorkflowCapabilityRef from wf_artifacts import (
WorkflowArtifact,
WorkflowCapabilityRef,
workflow_capability_ref_from_workflow_ref,
workflow_ref_from_artifact,
workflow_ref_from_capability,
)
from wf_core import WorkflowRef from wf_core import WorkflowRef
@@ -101,3 +107,51 @@ def test_core_workflow_ref_rejects_mixed_name_and_artifact_ref() -> None:
CoreWorkflowRefPayload.model_validate( CoreWorkflowRefPayload.model_validate(
{"ref": {"name": "child", "artifact_id": "echo", "version": 1}} {"ref": {"name": "child", "artifact_id": "echo", "version": 1}}
) )
def test_workflow_ref_from_artifact_uses_artifact_identity() -> None:
ref = workflow_ref_from_artifact(_artifact("echo_wrapper", 2))
assert ref.artifact_id == "echo_wrapper"
assert ref.version == 2
assert ref.name is None
def test_workflow_ref_from_capability_uses_saved_workflow_identity() -> None:
ref = workflow_ref_from_capability(
WorkflowCapabilityRef(artifact_id="echo_wrapper", version=2)
)
assert ref.artifact_id == "echo_wrapper"
assert ref.version == 2
def test_workflow_capability_ref_from_workflow_ref_requires_artifact_ref() -> None:
capability = workflow_capability_ref_from_workflow_ref(
WorkflowRef(artifact_id="echo_wrapper", version=2)
)
assert capability == WorkflowCapabilityRef(artifact_id="echo_wrapper", version=2)
with pytest.raises(ValueError, match="artifact"):
workflow_capability_ref_from_workflow_ref(WorkflowRef(name="local_child"))
def _artifact(artifact_id: str, version: int) -> WorkflowArtifact:
return WorkflowArtifact(
id=artifact_id,
version=version,
title="Echo Wrapper",
input_schema={"type": "object"},
output_schema={"type": "object"},
outcomes=("ok",),
plan={
"name": "echo_wrapper",
"input_schema": {"type": "object"},
"state_schema": {"type": "object", "properties": {}},
"output_schema": {"type": "object"},
"start": "end",
"nodes": [{"id": "end", "type": "end", "outcome": "ok"}],
"edges": [],
},
)