paths throughout to serialize as object.

all for clarity! ahh
This commit is contained in:
lda
2026-05-21 02:02:04 +07:00 Verified
parent 2fcff9936f
commit e568db3169
19 changed files with 1003 additions and 232 deletions
+18 -3
View File
@@ -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
+39
View File
@@ -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}