From b6ee787b0977145d618ac9b1c0d3019cc0ef7bec Mon Sep 17 00:00:00 2001 From: Yufeng He <40085740+he-yufeng@users.noreply.github.com> Date: Wed, 22 Jul 2026 18:59:10 +0800 Subject: [PATCH] Python: make SerializationMixin.from_dict enforce the documented type check from_dict resolved the expected type identifier from the payload itself (_get_type_identifier(value) prefers value["type"]), so the mismatch guard could never fire: any supplied 'type' matched itself, and a payload like {"type": "function_tool", ...} silently deserialized into a Message, getting its type rewritten on the next to_dict. The docstring has always promised a ValueError on mismatch. Resolve the identifier from the class instead, matching what to_dict emits, so a mismatched or foreign 'type' now raises as documented. Payloads without a 'type' field and dependency-injection lookups are unchanged: in every previously valid case the class-resolved identifier is the same string the payload carried. Fixes #7255 Signed-off-by: Yufeng He <40085740+he-yufeng@users.noreply.github.com> --- .../core/agent_framework/_serialization.py | 6 ++++-- .../tests/core/test_serializable_mixin.py | 21 +++++++++++++++++++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/python/packages/core/agent_framework/_serialization.py b/python/packages/core/agent_framework/_serialization.py index f1cf962fe3..a4d00fb4b7 100644 --- a/python/packages/core/agent_framework/_serialization.py +++ b/python/packages/core/agent_framework/_serialization.py @@ -535,8 +535,10 @@ async def get_current_weather(location: Annotated[str, "The city name"]) -> str: if dependencies is None: dependencies = {} - # Get the type identifier - type_id = cls._get_type_identifier(value) + # Resolve the expected identifier from the class, not the payload: + # reading it from `value` makes the mismatch check tautological, so + # any supplied 'type' would silently match itself. + type_id = cls._get_type_identifier() if (supplied_type := value.get("type")) and supplied_type != type_id: raise ValueError(f"Type mismatch: expected '{type_id}', got '{supplied_type}'") diff --git a/python/packages/core/tests/core/test_serializable_mixin.py b/python/packages/core/tests/core/test_serializable_mixin.py index 9e3a6604d6..8cbebdd792 100644 --- a/python/packages/core/tests/core/test_serializable_mixin.py +++ b/python/packages/core/tests/core/test_serializable_mixin.py @@ -5,6 +5,7 @@ import logging from typing import Any +import pytest from typing_extensions import Self from agent_framework._serialization import SerializationMixin @@ -369,6 +370,26 @@ def __init__(self, value: str): out = obj.to_dict() assert out["type"] == "my_custom_type" + def test_from_dict_rejects_mismatched_type(self): + """from_dict raises ValueError when the payload type doesn't match the class.""" + + class TestClass(SerializationMixin): + def __init__(self, value: str): + self.value = value + + with pytest.raises(ValueError, match="Type mismatch: expected 'test_class', got 'function_tool'"): + TestClass.from_dict({"type": "function_tool", "value": "x"}) + + def test_from_json_rejects_mismatched_type(self): + """from_json surfaces the same mismatch error instead of silently coercing.""" + + class TestClass(SerializationMixin): + def __init__(self, value: str): + self.value = value + + with pytest.raises(ValueError, match="Type mismatch"): + TestClass.from_json('{"type": "some_other_type", "value": "x"}') + def test_from_json(self): """Test from_json deserializes JSON string."""