1515# Enables postponed evaluation of annotations (for string-based type hints)
1616from __future__ import annotations
1717
18+ import inspect
1819import time
19- from dataclasses import asdict
2020from typing import Any , ClassVar , Dict
2121from uuid import UUID , uuid4
2222
@@ -63,37 +63,62 @@ class MemoryRecord(BaseModel):
6363 "FunctionCallingMessage" : FunctionCallingMessage ,
6464 }
6565
66+ # Cache for constructor parameters (performance optimization)
67+ _constructor_params_cache : ClassVar [Dict [str , set ]] = {}
68+
69+ @classmethod
70+ def _get_constructor_params (cls , message_cls ) -> set :
71+ """Get constructor parameters for a message class with caching."""
72+ cls_name = message_cls .__name__
73+ if cls_name not in cls ._constructor_params_cache :
74+ sig = inspect .signature (message_cls .__init__ )
75+ cls ._constructor_params_cache [cls_name ] = set (
76+ sig .parameters .keys ()
77+ ) - {'self' }
78+ return cls ._constructor_params_cache [cls_name ]
79+
6680 @classmethod
6781 def from_dict (cls , record_dict : Dict [str , Any ]) -> "MemoryRecord" :
6882 r"""Reconstruct a :obj:`MemoryRecord` from the input dict.
6983
7084 Args:
7185 record_dict(Dict[str, Any]): A dict generated by :meth:`to_dict`.
7286 """
73- from camel .types import (
74- OpenAIBackendRole ,
75- RoleType ,
76- )
87+ from camel .types import OpenAIBackendRole , RoleType
7788
7889 message_cls = cls ._MESSAGE_TYPES [record_dict ["message" ]["__class__" ]]
79- kwargs : Dict = record_dict ["message" ].copy ()
80- kwargs .pop ("__class__" )
81-
82- # Convert role_type string back to RoleType enum if it's a string
83- if "role_type" in kwargs and isinstance (kwargs ["role_type" ], str ):
84- kwargs ["role_type" ] = RoleType (kwargs ["role_type" ])
85-
86- reconstructed_message = message_cls (** kwargs )
87-
88- # Convert role_at_backend string back to OpenAIBackendRole enum if
89- # it's a string
90+ data = record_dict ["message" ].copy ()
91+ data .pop ("__class__" )
92+
93+ # Convert role_type string to enum
94+ if "role_type" in data and isinstance (data ["role_type" ], str ):
95+ data ["role_type" ] = RoleType (data ["role_type" ])
96+
97+ # Get valid constructor parameters (cached)
98+ valid_params = cls ._get_constructor_params (message_cls )
99+
100+ # Separate constructor args from extra fields
101+ kwargs = {k : v for k , v in data .items () if k in valid_params }
102+ extra_fields = {k : v for k , v in data .items () if k not in valid_params }
103+
104+ # Handle meta_dict properly: merge existing meta_dict with extra fields
105+ existing_meta = kwargs .get ("meta_dict" , {}) or {}
106+ if extra_fields :
107+ # Extra fields take precedence, but preserve existing meta_dict
108+ # structure
109+ merged_meta = {** existing_meta , ** extra_fields }
110+ kwargs ["meta_dict" ] = merged_meta
111+ elif not existing_meta :
112+ kwargs ["meta_dict" ] = None
113+
114+ # Convert role_at_backend
90115 role_at_backend = record_dict ["role_at_backend" ]
91116 if isinstance (role_at_backend , str ):
92117 role_at_backend = OpenAIBackendRole (role_at_backend )
93118
94119 return cls (
95120 uuid = UUID (record_dict ["uuid" ]),
96- message = reconstructed_message ,
121+ message = message_cls ( ** kwargs ) ,
97122 role_at_backend = role_at_backend ,
98123 extra_info = record_dict ["extra_info" ],
99124 timestamp = record_dict ["timestamp" ],
@@ -108,7 +133,7 @@ def to_dict(self) -> Dict[str, Any]:
108133 "uuid" : str (self .uuid ),
109134 "message" : {
110135 "__class__" : self .message .__class__ .__name__ ,
111- ** asdict ( self .message ),
136+ ** self .message . to_dict ( ),
112137 },
113138 "role_at_backend" : self .role_at_backend .value
114139 if hasattr (self .role_at_backend , "value" )
0 commit comments