fix: backfill legacy discriminators and add source validation context

This commit is contained in:
Greyson LaLonde
2026-05-21 00:22:53 +08:00
parent 97e959cb0c
commit b07c1439a3
2 changed files with 46 additions and 4 deletions

View File

@@ -38,14 +38,20 @@ def _resolve_knowledge_sources(value: Any) -> Any:
if not isinstance(value, list):
return value
resolved: list[Any] = []
for item in value:
for idx, item in enumerate(value):
if isinstance(item, dict):
tag = item.get("source_type")
cls = _KNOWN_SOURCES.get(tag) if isinstance(tag, str) else None
if cls is None:
resolved.append(item)
else:
resolved.append(cls.model_validate(item))
try:
resolved.append(cls.model_validate(item))
except Exception as exc:
raise ValueError(
f"Failed to validate knowledge source at index {idx} "
f"with source_type={tag!r}: {exc}"
) from exc
else:
resolved.append(item)
return resolved

View File

@@ -113,12 +113,48 @@ def _migrate(data: dict[str, Any]) -> dict[str, Any]:
)
# --- migrations in version order ---
# if stored < Version("X.Y.Z"):
# data.setdefault("some_field", "default")
if stored < Version("1.14.6"):
for entity in data.get("entities") or []:
_backfill_discriminators(entity)
return data
def _backfill_memory_kind(value: Any) -> None:
"""Infer ``memory_kind`` from structural fields on legacy memory dicts."""
if not isinstance(value, dict) or "memory_kind" in value:
return
if "scopes" in value:
value["memory_kind"] = "slice"
elif "root_path" in value:
value["memory_kind"] = "scope"
else:
value["memory_kind"] = "memory"
def _backfill_source_type(source: Any) -> None:
"""Infer ``source_type`` for legacy knowledge source dicts when possible."""
if not isinstance(source, dict) or "source_type" in source:
return
if "content" in source:
source["source_type"] = "string"
def _backfill_discriminators(entity: Any) -> None:
"""Walk an entity dict and backfill discriminator fields added in 1.14.6."""
if not isinstance(entity, dict):
return
_backfill_memory_kind(entity.get("memory"))
for agent in entity.get("agents") or []:
_backfill_memory_kind(agent.get("memory") if isinstance(agent, dict) else None)
for container in (entity.get("knowledge"), entity):
if isinstance(container, dict):
for src in (
container.get("sources") or container.get("knowledge_sources") or []
):
_backfill_source_type(src)
class RuntimeState(RootModel): # type: ignore[type-arg]
root: list[Entity]
_provider: BaseProvider = PrivateAttr(default_factory=JsonProvider)