77from enum import Enum
88from typing import Any , Final , Protocol , cast
99
10- from pydantic import BaseModel , InstanceOf
10+ from pydantic import BaseModel , InstanceOf , ValidationError
1111
1212from memmachine .common .configuration import Configuration
1313from memmachine .common .configuration .episodic_config import (
1414 EpisodicMemoryConf ,
15- LongTermMemoryConf ,
16- ShortTermMemoryConf ,
15+ EpisodicMemoryConfPartial ,
16+ LongTermMemoryConfPartial ,
17+ ShortTermMemoryConfPartial ,
1718)
1819from memmachine .common .episode_store import Episode , EpisodeEntry , EpisodeIdT
20+ from memmachine .common .errors import ConfigurationError
1921from memmachine .common .filter .filter_parser import (
2022 And as FilterAnd ,
2123)
@@ -74,17 +76,62 @@ def __init__(
7476 ) -> None :
7577 """Create a MemMachine using the provided configuration."""
7678 self ._conf = conf
77-
7879 if resources is not None :
7980 self ._resources = resources
8081 else :
8182 self ._resources = ResourceManagerImpl (conf )
83+ self ._initialize_default_episodic_configuration ()
84+ self ._started = False
85+
86+ def _initialize_default_episodic_configuration (self ) -> None :
87+ # initialize the default value for episodic memory configuration
88+ # Can not put the logic into the data type
89+ default_prompt = "Based on the following episodes: {episodes}, and the previous summary: {summary}, please update the summary. Keep it under {max_length} characters."
90+ if self ._conf .episodic_memory is None :
91+ self ._conf .episodic_memory = EpisodicMemoryConfPartial ()
92+ self ._conf .episodic_memory .enabled = False
93+ if self ._conf .episodic_memory .long_term_memory is None :
94+ self ._conf .episodic_memory .long_term_memory = LongTermMemoryConfPartial ()
95+ self ._conf .episodic_memory .long_term_memory_enabled = False
96+ if self ._conf .episodic_memory .short_term_memory is None :
97+ self ._conf .episodic_memory .short_term_memory = ShortTermMemoryConfPartial ()
98+ self ._conf .episodic_memory .short_term_memory_enabled = False
99+ if self ._conf .episodic_memory .long_term_memory .embedder is None :
100+ self ._conf .episodic_memory .long_term_memory .embedder = (
101+ self ._conf .default_long_term_memory_embedder
102+ )
103+ if self ._conf .episodic_memory .long_term_memory .reranker is None :
104+ self ._conf .episodic_memory .long_term_memory .reranker = (
105+ self ._conf .default_long_term_memory_reranker
106+ )
107+ if self ._conf .episodic_memory .short_term_memory .llm_model is None :
108+ self ._conf .episodic_memory .short_term_memory .llm_model = "gpt-4.1"
109+ if self ._conf .episodic_memory .short_term_memory .summary_prompt_system is None :
110+ self ._conf .episodic_memory .short_term_memory .summary_prompt_system = (
111+ "You are a helpful assistant."
112+ )
113+ if self ._conf .episodic_memory .short_term_memory .summary_prompt_user is None :
114+ self ._conf .episodic_memory .short_term_memory .summary_prompt_user = (
115+ default_prompt
116+ )
117+ if self ._conf .episodic_memory .long_term_memory .vector_graph_store is None :
118+ self ._conf .episodic_memory .long_term_memory .vector_graph_store = (
119+ "default_store"
120+ )
82121
83122 async def start (self ) -> None :
123+ if self ._started :
124+ return
125+ self ._started = True
126+
84127 semantic_service = await self ._resources .get_semantic_service ()
85128 await semantic_service .start ()
86129
87130 async def stop (self ) -> None :
131+ if not self ._started :
132+ return
133+ self ._started = False
134+
88135 semantic_service = await self ._resources .get_semantic_service ()
89136 await semantic_service .stop ()
90137
@@ -93,75 +140,39 @@ async def stop(self) -> None:
93140 def _with_default_episodic_memory_conf (
94141 self ,
95142 * ,
96- embedder_name : str | None = None ,
97- reranker_name : str | None = None ,
143+ user_conf : EpisodicMemoryConfPartial | None = None ,
98144 session_key : str ,
99145 ) -> EpisodicMemoryConf :
100146 # Get default prompts from config, with fallbacks
101- short_term = self ._conf .episodic_memory .short_term_memory
102- summary_prompt_system = (
103- short_term .summary_prompt_system
104- if short_term and short_term .summary_prompt_system
105- else "You are a helpful assistant."
106- )
107- summary_prompt_user = (
108- short_term .summary_prompt_user
109- if short_term and short_term .summary_prompt_user
110- else "Based on the following episodes: {episodes}, and the previous summary: {summary}, please update the summary. Keep it under {max_length} characters."
111- )
112-
113- # Get default embedder and reranker from config
114- long_term = self ._conf .episodic_memory .long_term_memory
115-
116- if not embedder_name :
117- embedder_name = self ._conf .default_long_term_memory_embedder
118- if not reranker_name :
119- reranker_name = self ._conf .default_long_term_memory_reranker
120-
121- self ._conf .check_reranker (reranker_name )
122- self ._conf .check_embedder (embedder_name )
123-
124- target_vector_store = (
125- long_term .vector_graph_store
126- if long_term and long_term .vector_graph_store
127- else "default_store"
128- )
129-
130- target_short_llm_model = (
131- short_term .llm_model if short_term and short_term .llm_model else "gpt-4.1"
132- )
133-
134- return EpisodicMemoryConf (
135- session_key = session_key ,
136- long_term_memory = LongTermMemoryConf (
137- session_id = session_key ,
138- vector_graph_store = target_vector_store ,
139- embedder = embedder_name ,
140- reranker = reranker_name ,
141- ),
142- short_term_memory = ShortTermMemoryConf (
143- session_key = session_key ,
144- llm_model = target_short_llm_model ,
145- summary_prompt_system = summary_prompt_system ,
146- summary_prompt_user = summary_prompt_user ,
147- ),
148- long_term_memory_enabled = True ,
149- short_term_memory_enabled = True ,
150- enabled = True ,
151- )
147+ try :
148+ if user_conf is None :
149+ user_conf = EpisodicMemoryConfPartial ()
150+ user_conf .session_key = session_key
151+ episodic_conf = user_conf .merge (self ._conf .episodic_memory )
152+ if episodic_conf .long_term_memory is not None :
153+ if episodic_conf .long_term_memory .embedder is not None :
154+ self ._conf .check_embedder (episodic_conf .long_term_memory .embedder )
155+ if episodic_conf .long_term_memory .reranker is not None :
156+ self ._conf .check_reranker (episodic_conf .long_term_memory .reranker )
157+ except ValidationError as e :
158+ logger .exception (
159+ "Faield to merge configuration: %s, %s" ,
160+ str (user_conf ),
161+ str (self ._conf .episodic_memory ),
162+ )
163+ raise ConfigurationError ("Failed to merge configuration" ) from e
164+ return episodic_conf
152165
153166 async def create_session (
154167 self ,
155168 session_key : str ,
156169 * ,
157170 description : str = "" ,
158- embedder_name : str | None = None ,
159- reranker_name : str | None = None ,
171+ user_conf : EpisodicMemoryConfPartial | None = None ,
160172 ) -> SessionDataManager .SessionInfo :
161173 """Create a new session."""
162174 episodic_memory_conf = self ._with_default_episodic_memory_conf (
163- embedder_name = embedder_name ,
164- reranker_name = reranker_name ,
175+ user_conf = user_conf ,
165176 session_key = session_key ,
166177 )
167178
0 commit comments