paths throughout to serialize as object.
all for clarity! ahh
This commit is contained in:
@@ -29,7 +29,12 @@ class DiagnosticSeverity(StrEnum):
|
||||
|
||||
|
||||
class RequiredCapability(BaseModel):
|
||||
"""Saved contract for one capability an artifact references."""
|
||||
"""Saved contract for one capability an artifact references.
|
||||
|
||||
`ref` is canonical structure. Old dotted strings are accepted as
|
||||
compatibility input, but new saves should preserve the source/capability
|
||||
boundary because capability keys may contain dots.
|
||||
"""
|
||||
|
||||
ref: CapabilityRefInput
|
||||
kind: Literal["tool", "resource", "prompt", "node_spec", "reducer", "workflow"]
|
||||
@@ -68,7 +73,12 @@ class RequiredCapability(BaseModel):
|
||||
logical_source = data.pop("logical_source", None)
|
||||
capability_name = data.pop("capability_name", None)
|
||||
if isinstance(logical_source, str) and isinstance(capability_name, str):
|
||||
data["ref"] = f"{logical_source}.{capability_name}"
|
||||
# Preserve the source/capability boundary; capability names may
|
||||
# contain dots, so joining then reparsing would corrupt the ref.
|
||||
data["ref"] = {
|
||||
"source": logical_source,
|
||||
"capability_key": capability_name,
|
||||
}
|
||||
return data
|
||||
|
||||
|
||||
@@ -90,7 +100,12 @@ class AvailableSource(BaseModel):
|
||||
|
||||
|
||||
class SourceBinding(BaseModel):
|
||||
"""Deployment-time mapping from artifact logical source to concrete source."""
|
||||
"""Deployment-time mapping from artifact logical source to concrete source.
|
||||
|
||||
`logical_source` is the artifact-local alias used by saved refs.
|
||||
`concrete_source` is the deployment-selected source id. Neither field is a
|
||||
capability name.
|
||||
"""
|
||||
|
||||
logical_source: SourceRefInput
|
||||
concrete_source: SourceRefInput
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from typing import Any
|
||||
|
||||
from pydantic_core import core_schema
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
@@ -31,3 +34,39 @@ class WorkflowCapabilityRef:
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"workflow.{self.artifact_id}.v{self.version}"
|
||||
|
||||
@classmethod
|
||||
def __get_pydantic_core_schema__(
|
||||
cls,
|
||||
_source_type: object,
|
||||
_handler: object,
|
||||
) -> core_schema.CoreSchema:
|
||||
"""Validate legacy display strings but save workflow refs structurally."""
|
||||
return core_schema.no_info_plain_validator_function(
|
||||
cls._validate,
|
||||
serialization=core_schema.plain_serializer_function_ser_schema(
|
||||
cls._serialize,
|
||||
when_used="json",
|
||||
),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def _validate(cls, value: Any) -> WorkflowCapabilityRef:
|
||||
if isinstance(value, WorkflowCapabilityRef):
|
||||
return value
|
||||
if isinstance(value, str):
|
||||
return cls.parse(value)
|
||||
if isinstance(value, dict):
|
||||
artifact_id = value.get("artifact_id")
|
||||
version = value.get("version")
|
||||
if isinstance(artifact_id, str) and isinstance(version, int):
|
||||
return cls(artifact_id=artifact_id, version=version)
|
||||
raise TypeError(
|
||||
"workflow capability ref must be a workflow.<artifact>.v<version> "
|
||||
"string or {'artifact_id': str, 'version': int}"
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _serialize(value: WorkflowCapabilityRef) -> dict[str, int | str]:
|
||||
"""Serialize canonical saved workflow refs without a display-name parser."""
|
||||
return {"artifact_id": value.artifact_id, "version": value.version}
|
||||
|
||||
Reference in New Issue
Block a user