1111historical information.
1212"""
1313
14+ import asyncio
1415from typing import Any , Dict , List , Optional
1516
1617from loguru import logger
@@ -112,9 +113,51 @@ def __init__(
112113 self .last_query = None
113114 logger .info (f"Initialized Mem0MemoryService with { user_id = } , { agent_id = } , { run_id = } " )
114115
115- def _store_messages (self , messages : List [Dict [str , Any ]]):
116+ async def get_memories (self ) -> List [Dict [str , Any ]]:
117+ """Retrieve all stored memories for the configured user/agent/run IDs.
118+
119+ This is a convenience method for accessing memories outside the pipeline,
120+ e.g. to build a personalized greeting at connection time. It wraps the
121+ blocking Mem0 ``get_all()`` call in a background thread.
122+
123+ Returns:
124+ List of memory dictionaries. Each dict contains at least a
125+ ``"memory"`` key with the memory text. Returns an empty list on
126+ error.
127+ """
128+ try :
129+ if isinstance (self .memory_client , Memory ):
130+ params = {
131+ "user_id" : self .user_id ,
132+ "agent_id" : self .agent_id ,
133+ "run_id" : self .run_id ,
134+ }
135+ params = {k : v for k , v in params .items () if v is not None }
136+ memories = await asyncio .to_thread (lambda : self .memory_client .get_all (** params ))
137+ else :
138+ id_pairs = [
139+ ("user_id" , self .user_id ),
140+ ("agent_id" , self .agent_id ),
141+ ("run_id" , self .run_id ),
142+ ]
143+ clauses = [{name : value } for name , value in id_pairs if value is not None ]
144+ filters = {"OR" : clauses } if clauses else {}
145+ memories = await asyncio .to_thread (
146+ lambda : self .memory_client .get_all (filters = filters )
147+ )
148+
149+ results = memories .get ("results" , []) if isinstance (memories , dict ) else memories
150+ return results
151+ except Exception as e :
152+ logger .error (f"Error retrieving memories from Mem0: { e } " )
153+ return []
154+
155+ async def _store_messages (self , messages : List [Dict [str , Any ]]):
116156 """Store messages in Mem0.
117157
158+ Runs the blocking Mem0 API call in a background thread to avoid
159+ blocking the event loop.
160+
118161 Args:
119162 messages: List of message dictionaries to store in memory.
120163 """
@@ -131,14 +174,16 @@ def _store_messages(self, messages: List[Dict[str, Any]]):
131174
132175 if isinstance (self .memory_client , Memory ):
133176 del params ["output_format" ]
134- # Note: You can run this in background to avoid blocking the conversation
135- self .memory_client .add (** params )
177+ await asyncio .to_thread (lambda : self .memory_client .add (** params ))
136178 except Exception as e :
137179 logger .error (f"Error storing messages in Mem0: { e } " )
138180
139- def _retrieve_memories (self , query : str ) -> List [Dict [str , Any ]]:
181+ async def _retrieve_memories (self , query : str ) -> List [Dict [str , Any ]]:
140182 """Retrieve relevant memories from Mem0.
141183
184+ Runs the blocking Mem0 API call in a background thread to avoid
185+ blocking the event loop.
186+
142187 Args:
143188 query: The query to search for relevant memories.
144189
@@ -156,7 +201,7 @@ def _retrieve_memories(self, query: str) -> List[Dict[str, Any]]:
156201 "limit" : self .search_limit ,
157202 }
158203 params = {k : v for k , v in params .items () if v is not None }
159- results = self .memory_client .search (** params )
204+ results = await asyncio . to_thread ( lambda : self .memory_client .search (** params ) )
160205 else :
161206 id_pairs = [
162207 ("user_id" , self .user_id ),
@@ -165,13 +210,15 @@ def _retrieve_memories(self, query: str) -> List[Dict[str, Any]]:
165210 ]
166211 clauses = [{name : value } for name , value in id_pairs if value is not None ]
167212 filters = {"OR" : clauses } if clauses else {}
168- results = self .memory_client .search (
169- query = query ,
170- filters = filters ,
171- version = self .api_version ,
172- top_k = self .search_limit ,
173- threshold = self .search_threshold ,
174- output_format = "v1.1" ,
213+ results = await asyncio .to_thread (
214+ lambda : self .memory_client .search (
215+ query = query ,
216+ filters = filters ,
217+ version = self .api_version ,
218+ top_k = self .search_limit ,
219+ threshold = self .search_threshold ,
220+ output_format = "v1.1" ,
221+ )
175222 )
176223
177224 logger .debug (f"Retrieved { len (results )} memories from Mem0" )
@@ -180,7 +227,9 @@ def _retrieve_memories(self, query: str) -> List[Dict[str, Any]]:
180227 logger .error (f"Error retrieving memories from Mem0: { e } " )
181228 return []
182229
183- def _enhance_context_with_memories (self , context : LLMContext | OpenAILLMContext , query : str ):
230+ async def _enhance_context_with_memories (
231+ self , context : LLMContext | OpenAILLMContext , query : str
232+ ):
184233 """Enhance the LLM context with relevant memories.
185234
186235 Args:
@@ -193,7 +242,7 @@ def _enhance_context_with_memories(self, context: LLMContext | OpenAILLMContext,
193242
194243 self .last_query = query
195244
196- memories = self ._retrieve_memories (query )
245+ memories = await self ._retrieve_memories (query )
197246 if not memories :
198247 return
199248
@@ -203,11 +252,14 @@ def _enhance_context_with_memories(self, context: LLMContext | OpenAILLMContext,
203252 memory_text += f"{ i } . { memory .get ('memory' , '' )} \n \n "
204253
205254 # Add memories as a system message or user message based on configuration
206- if self .add_as_system_message :
207- context .add_message ({"role" : "system" , "content" : memory_text })
208- else :
209- # Add as a user message that provides context
210- context .add_message ({"role" : "user" , "content" : memory_text })
255+ role = "system" if self .add_as_system_message else "user"
256+ memory_message = {"role" : role , "content" : memory_text }
257+
258+ messages = context .get_messages ()
259+ position = max (0 , min (self .position , len (messages )))
260+ messages .insert (position , memory_message )
261+ context .set_messages (messages )
262+
211263 logger .debug (f"Enhanced context with { len (memories )} memories" )
212264
213265 async def process_frame (self , frame : Frame , direction : FrameDirection ):
@@ -240,10 +292,15 @@ async def process_frame(self, frame: Frame, direction: FrameDirection):
240292 break
241293
242294 if latest_user_message :
295+ # Filter to only user/assistant messages — Mem0 API
296+ # doesn't accept other roles (system, developer, etc.)
297+ messages_to_store = [
298+ m for m in context_messages if m .get ("role" ) in ("user" , "assistant" )
299+ ]
243300 # Enhance context with memories before passing it downstream
244- self ._enhance_context_with_memories (context , latest_user_message )
245- # Store the conversation in Mem0. Only call this when user message is detected
246- self ._store_messages (context_messages )
301+ await self ._enhance_context_with_memories (context , latest_user_message )
302+ # Store the conversation in Mem0 as a background task
303+ self .create_task ( self . _store_messages (messages_to_store ), name = "mem0_store" )
247304
248305 # If we received an LLMMessagesFrame, create a new one with the enhanced messages
249306 if messages is not None :
0 commit comments