mirror of
https://github.com/langgenius/dify.git
synced 2026-07-20 00:53:34 -04:00
fix(agent): accept legacy missing asset placeholders (#39039)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
@@ -188,6 +188,14 @@ def validate_config_skill_name(name: str) -> str:
|
||||
return normalized
|
||||
|
||||
|
||||
def _normalize_legacy_missing_asset_file_id(value: Any) -> Any:
|
||||
"""Canonicalize the null placeholder emitted by early portable Agent DSLs."""
|
||||
|
||||
if isinstance(value, dict) and value.get("is_missing") is True and value.get("file_id") is None:
|
||||
return {**value, "file_id": ""}
|
||||
return value
|
||||
|
||||
|
||||
class AgentConfigFileRefConfig(BaseModel):
|
||||
"""Stable Agent Soul reference to one config file payload."""
|
||||
|
||||
@@ -201,6 +209,11 @@ class AgentConfigFileRefConfig(BaseModel):
|
||||
hash: str | None = None
|
||||
mime_type: str | None = None
|
||||
|
||||
@model_validator(mode="before")
|
||||
@classmethod
|
||||
def _normalize_legacy_file_id(cls, value: Any) -> Any:
|
||||
return _normalize_legacy_missing_asset_file_id(value)
|
||||
|
||||
@field_validator("name")
|
||||
@classmethod
|
||||
def _validate_name(cls, value: str) -> str:
|
||||
@@ -231,6 +244,11 @@ class AgentConfigSkillRefConfig(BaseModel):
|
||||
hash: str | None = None
|
||||
mime_type: str | None = "application/zip"
|
||||
|
||||
@model_validator(mode="before")
|
||||
@classmethod
|
||||
def _normalize_legacy_file_id(cls, value: Any) -> Any:
|
||||
return _normalize_legacy_missing_asset_file_id(value)
|
||||
|
||||
@field_validator("name")
|
||||
@classmethod
|
||||
def _validate_name(cls, value: str) -> str:
|
||||
|
||||
@@ -153,6 +153,43 @@ def test_agent_package_round_trips_as_strict_dsl_dto() -> None:
|
||||
assert restored == package
|
||||
|
||||
|
||||
def test_agent_package_normalizes_legacy_null_missing_asset_file_ids() -> None:
|
||||
package = make_portable_agent_package(
|
||||
_agent(),
|
||||
AgentSoulConfig.model_validate(
|
||||
{
|
||||
"config_skills": [{"name": "research", "file_id": "skill-file"}],
|
||||
"config_files": [{"name": "guide.md", "file_kind": "tool_file", "file_id": "config-file"}],
|
||||
}
|
||||
),
|
||||
).model_dump(mode="json")
|
||||
package["soul"]["config_skills"][0]["file_id"] = None
|
||||
package["soul"]["config_files"][0]["file_id"] = None
|
||||
|
||||
restored = AgentPackage.model_validate(package)
|
||||
|
||||
assert restored.soul.config_skills[0].file_id == ""
|
||||
assert restored.soul.config_files[0].file_id == ""
|
||||
assert restored.model_dump(mode="json")["soul"]["config_skills"][0]["file_id"] == ""
|
||||
assert restored.model_dump(mode="json")["soul"]["config_files"][0]["file_id"] == ""
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"asset",
|
||||
[
|
||||
{"name": "research", "file_id": None, "is_missing": False},
|
||||
{"name": "guide.md", "file_kind": "tool_file", "file_id": None, "is_missing": False},
|
||||
],
|
||||
)
|
||||
def test_agent_package_rejects_null_file_id_for_available_assets(asset: dict) -> None:
|
||||
package = make_portable_agent_package(_agent(), AgentSoulConfig()).model_dump(mode="json")
|
||||
target = "config_files" if "file_kind" in asset else "config_skills"
|
||||
package["soul"][target] = [asset]
|
||||
|
||||
with pytest.raises(ValidationError):
|
||||
AgentPackage.model_validate(package)
|
||||
|
||||
|
||||
def test_import_warnings_cover_runtime_setup_removed_from_package(monkeypatch) -> None:
|
||||
soul = AgentSoulConfig.model_validate(
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user