simplest categorical addition

This commit is contained in:
lda
2026-05-16 17:53:42 +07:00 Verified
parent 1140a7ccdd
commit 7dcfad92c1
6 changed files with 42 additions and 3 deletions
+3 -2
View File
@@ -85,7 +85,8 @@ how did the original provider happen to expose this thing?
### Wrapper / Adapter Artifact ### Wrapper / Adapter Artifact
A wrapper artifact is a saved reusable bridge from a raw capability to a A wrapper artifact is a saved reusable bridge from a raw capability to a
workflow capability. workflow capability. In storage it is not a second artifact family: it is a
`WorkflowArtifact` with `kind="wrapper"`.
Examples: Examples:
@@ -264,10 +265,10 @@ Today:
- `wf.std` owns local reusable workflow node specs - `wf.std` owns local reusable workflow node specs
- `wf.mcp` owns workflow-facing MCP runtime helpers - `wf.mcp` owns workflow-facing MCP runtime helpers
- discovered upstream tools can already become workflow node specs - discovered upstream tools can already become workflow node specs
- saved artifacts can be tagged with `kind="workflow"` or `kind="wrapper"`
Not yet implemented: Not yet implemented:
- a saved wrapper artifact model
- a direct public tool for calling arbitrary workflow node specs for authoring - a direct public tool for calling arbitrary workflow node specs for authoring
tests tests
- per-outcome output schemas - per-outcome output schemas
+2
View File
@@ -5,6 +5,7 @@ from .catalog import (
) )
from .factory import create_workflow_artifact_from_plan from .factory import create_workflow_artifact_from_plan
from .models import ( from .models import (
ArtifactKind,
AvailableCapability, AvailableCapability,
AvailableSource, AvailableSource,
DependencyDiagnostic, DependencyDiagnostic,
@@ -19,6 +20,7 @@ from .validation import validate_deployment_dependencies
__all__ = [ __all__ = [
"AvailableCapability", "AvailableCapability",
"ArtifactKind",
"AvailableSource", "AvailableSource",
"DependencyDiagnostic", "DependencyDiagnostic",
"DiagnosticSeverity", "DiagnosticSeverity",
+3 -1
View File
@@ -4,7 +4,7 @@ from collections.abc import Mapping
from wf_core import Workflow from wf_core import Workflow
from .models import JsonObject, RequiredCapability, WorkflowArtifact from .models import ArtifactKind, JsonObject, RequiredCapability, WorkflowArtifact
def create_workflow_artifact_from_plan( def create_workflow_artifact_from_plan(
@@ -14,6 +14,7 @@ def create_workflow_artifact_from_plan(
title: str, title: str,
plan: JsonObject, plan: JsonObject,
outcomes: tuple[str, ...], outcomes: tuple[str, ...],
kind: ArtifactKind = "workflow",
description: str | None = None, description: str | None = None,
required_capabilities: Mapping[str, RequiredCapability] | None = None, required_capabilities: Mapping[str, RequiredCapability] | None = None,
created_from_catalog_version: str | None = None, created_from_catalog_version: str | None = None,
@@ -24,6 +25,7 @@ def create_workflow_artifact_from_plan(
id=artifact_id, id=artifact_id,
version=version, version=version,
title=title, title=title,
kind=kind,
description=description, description=description,
input_schema=_required_object_field(plan, "input_schema"), input_schema=_required_object_field(plan, "input_schema"),
output_schema=_required_object_field(plan, "output_schema"), output_schema=_required_object_field(plan, "output_schema"),
+2
View File
@@ -6,6 +6,7 @@ from typing import Any, Literal
from pydantic import BaseModel, Field from pydantic import BaseModel, Field
JsonObject = dict[str, Any] JsonObject = dict[str, Any]
ArtifactKind = Literal["workflow", "wrapper"]
class DriftPolicy(StrEnum): class DriftPolicy(StrEnum):
@@ -71,6 +72,7 @@ class WorkflowArtifact(BaseModel):
id: str id: str
version: int = Field(ge=1) version: int = Field(ge=1)
title: str title: str
kind: ArtifactKind = "workflow"
description: str | None = None description: str | None = None
input_schema: JsonObject input_schema: JsonObject
output_schema: JsonObject output_schema: JsonObject
+14
View File
@@ -22,6 +22,7 @@ def test_create_workflow_artifact_from_plan_derives_boundary_schemas() -> None:
) )
assert artifact.id == "echo" assert artifact.id == "echo"
assert artifact.kind == "workflow"
assert artifact.version == 1 assert artifact.version == 1
assert artifact.title == "Echo" assert artifact.title == "Echo"
assert artifact.input_schema["properties"]["text"]["type"] == "string" assert artifact.input_schema["properties"]["text"]["type"] == "string"
@@ -32,6 +33,19 @@ def test_create_workflow_artifact_from_plan_derives_boundary_schemas() -> None:
assert artifact.created_from_catalog_version == "catalog-1" assert artifact.created_from_catalog_version == "catalog-1"
def test_create_workflow_artifact_from_plan_accepts_wrapper_kind() -> None:
artifact = create_workflow_artifact_from_plan(
artifact_id="normalize_status",
version=1,
title="Normalize Status",
plan=_plan(),
outcomes=("done",),
kind="wrapper",
)
assert artifact.kind == "wrapper"
def test_create_workflow_artifact_from_plan_rejects_missing_boundary_schema() -> None: def test_create_workflow_artifact_from_plan_rejects_missing_boundary_schema() -> None:
plan = _plan() plan = _plan()
plan.pop("output_schema") plan.pop("output_schema")
+18
View File
@@ -38,6 +38,7 @@ def test_workflow_artifact_serializes_required_capability_contract() -> None:
dumped = artifact.model_dump(mode="json") dumped = artifact.model_dump(mode="json")
assert dumped["id"] == "summarize_docs" assert dumped["id"] == "summarize_docs"
assert dumped["kind"] == "workflow"
assert dumped["version"] == 1 assert dumped["version"] == 1
assert dumped["outcomes"] == ["done", "failed"] assert dumped["outcomes"] == ["done", "failed"]
required = dumped["required_capabilities"]["context7.query-docs"] required = dumped["required_capabilities"]["context7.query-docs"]
@@ -45,6 +46,23 @@ def test_workflow_artifact_serializes_required_capability_contract() -> None:
assert required["input_schema_hash"] == "sha256:input" assert required["input_schema_hash"] == "sha256:input"
def test_workflow_artifact_can_be_marked_as_wrapper_intent() -> None:
artifact = WorkflowArtifact(
id="normalize_status",
version=1,
title="Normalize Status",
kind="wrapper",
input_schema={"type": "object", "properties": {}},
output_schema={"type": "object", "properties": {}},
outcomes=("done", "needs_input"),
plan={"name": "normalize_status", "nodes": [], "edges": []},
)
dumped = artifact.model_dump(mode="json")
assert dumped["kind"] == "wrapper"
def test_workflow_deployment_binds_logical_sources_to_concrete_sources() -> None: def test_workflow_deployment_binds_logical_sources_to_concrete_sources() -> None:
deployment = WorkflowDeployment( deployment = WorkflowDeployment(
id="summarize_docs.personal", id="summarize_docs.personal",