From 4716fd391cb281a7453d372ce3f596db6ebd6a0f Mon Sep 17 00:00:00 2001 From: OUAO-FRANK <3295933938@qq.com> Date: Thu, 22 Oct 2026 23:29:24 +0800 Subject: [PATCH] Fix MemoryContent.mime_type degenerating to str after dump/load roundtrip MemoryContent serializes an enum mime_type to its string value, but had no inverse validator, so a ListMemory dump_component()/load_component() roundtrip left mime_type as a plain str. Code that dispatches on `content.mime_type is MemoryMimeType.X` silently changed behaviour after a save/load. Coerce a string matching an enum value back to MemoryMimeType on validation; other custom MIME-type strings stay strings. --- .../src/autogen_core/memory/_base_memory.py | 20 +++++++++++++++- .../autogen-core/tests/test_memory.py | 23 +++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/python/packages/autogen-core/src/autogen_core/memory/_base_memory.py b/python/packages/autogen-core/src/autogen_core/memory/_base_memory.py index 5385b37d3e35..1d62ca9a8ca1 100644 --- a/python/packages/autogen-core/src/autogen_core/memory/_base_memory.py +++ b/python/packages/autogen-core/src/autogen_core/memory/_base_memory.py @@ -2,7 +2,7 @@ from enum import Enum from typing import Any, Dict, List, Union -from pydantic import BaseModel, ConfigDict, field_serializer +from pydantic import BaseModel, ConfigDict, field_serializer, field_validator from .._cancellation_token import CancellationToken from .._component_config import ComponentBase @@ -44,6 +44,24 @@ def serialize_mime_type(self, mime_type: MemoryMimeType | str) -> str: return mime_type.value return mime_type + @field_validator("mime_type", mode="before") + @classmethod + def _coerce_mime_type(cls, v: object) -> object: + """Restore a serialized string back to its :class:`MemoryMimeType`. + + ``dump_component()`` serializes an enum member to its string value, so a + ``load_component()`` roundtrip used to leave ``mime_type`` as a plain + ``str``, changing behaviour for code that dispatches on the enum. A + string that matches an enum value is coerced back; other custom MIME + type strings stay strings. + """ + if isinstance(v, str): + try: + return MemoryMimeType(v) + except ValueError: + return v + return v + class MemoryQueryResult(BaseModel): """Result of a memory :meth:`~autogen_core.memory.Memory.query` operation.""" diff --git a/python/packages/autogen-core/tests/test_memory.py b/python/packages/autogen-core/tests/test_memory.py index ce98aaffb97f..d63c8dd29d8a 100644 --- a/python/packages/autogen-core/tests/test_memory.py +++ b/python/packages/autogen-core/tests/test_memory.py @@ -51,6 +51,29 @@ def test_memory_component_dump_config_to_base_model() -> None: assert len(config.config["memory_contents"]) == 1 +def test_memory_mime_type_roundtrip() -> None: + """A dump/load roundtrip restores the enum, not a plain str (#8293).""" + memory = ListMemory( + name="test_memory", + memory_contents=[MemoryContent(content="hello", mime_type=MemoryMimeType.MARKDOWN)], + ) + config = memory.dump_component() + + loaded = ListMemory.load_component(config) + assert loaded.content[0].mime_type is MemoryMimeType.MARKDOWN + + +def test_memory_mime_type_custom_string_stays_str() -> None: + """A custom MIME type that is not an enum value stays a string.""" + memory = ListMemory( + name="test_memory", + memory_contents=[MemoryContent(content="hello", mime_type="application/custom")], + ) + loaded = ListMemory.load_component(memory.dump_component()) + assert loaded.content[0].mime_type == "application/custom" + assert not isinstance(loaded.content[0].mime_type, MemoryMimeType) + + def test_memory_abc_implementation() -> None: """Test that Memory ABC is properly implemented."""