Skip to content

Commit bcc672c

Browse files
fridayLCaralHsi
andauthored
feat: add code for internet api and change deault config for memos (#155)
* feat: update config * fix:dim * change dim * fix:change default db * fix:delay * fix:len * fix:change recently mem size * fix:dup node error * fix: remove mock_data * fix: change config * feat: reorganize code * add: add json parse for en * fix:change user_id * fix: logger info * fix: remove unsed change * feat: add topk for api * feat: add logger * fix:fix scheduler logs and * add: fix max_user instances * fix: logger for config and qa * feat: update add context * fix:mv env to docker * fix: rm ref id for response for scheduler * add: status for product * feat: add internet config * fix: id is null * add:internet search * fix:add internet_search * fix:chunk * fix: remove internet * fix: inter net bug * fix: chunk len * fix: filed bug * fix:fix ci and test * fix: url and passwd * fix: input len * feat: change reorganizer time * feat:change settings and add env for change default .memos info * fix:add mem * fix:datatime * fix: update * fix:ic and code test --------- Co-authored-by: CaralHsi <[email protected]>
1 parent db814c3 commit bcc672c

File tree

13 files changed

+124
-26
lines changed

13 files changed

+124
-26
lines changed

src/memos/api/config.py

Lines changed: 59 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,48 @@ def get_embedder_config() -> dict[str, Any]:
115115
},
116116
}
117117

118+
@staticmethod
119+
def get_internet_config() -> dict[str, Any]:
120+
"""Get embedder configuration."""
121+
return {
122+
"backend": "xinyu",
123+
"config": {
124+
"api_key": os.getenv("XINYU_API_KEY"),
125+
"search_engine_id": os.getenv("XINYU_SEARCH_ENGINE_ID"),
126+
"max_results": 15,
127+
"num_per_request": 10,
128+
"reader": {
129+
"backend": "simple_struct",
130+
"config": {
131+
"llm": {
132+
"backend": "openai",
133+
"config": {
134+
"model_name_or_path": os.getenv("MEMRADER_MODEL"),
135+
"temperature": 0.6,
136+
"max_tokens": 5000,
137+
"top_p": 0.95,
138+
"top_k": 20,
139+
"api_key": "EMPTY",
140+
"api_base": os.getenv("MEMRADER_API_BASE"),
141+
"remove_think_prefix": True,
142+
"extra_body": {"chat_template_kwargs": {"enable_thinking": False}},
143+
},
144+
},
145+
"embedder": APIConfig.get_embedder_config(),
146+
"chunker": {
147+
"backend": "sentence",
148+
"config": {
149+
"tokenizer_or_token_counter": "gpt2",
150+
"chunk_size": 512,
151+
"chunk_overlap": 128,
152+
"min_sentences_per_chunk": 1,
153+
},
154+
},
155+
},
156+
},
157+
},
158+
}
159+
118160
@staticmethod
119161
def get_neo4j_community_config(user_id: str | None = None) -> dict[str, Any]:
120162
"""Get Neo4j community configuration."""
@@ -340,7 +382,6 @@ def create_user_config(user_name: str, user_id: str) -> tuple[MOSConfig, General
340382
"top_k": 30,
341383
"max_turns_window": 20,
342384
}
343-
344385
# Add scheduler configuration if enabled
345386
if APIConfig.is_scheduler_enabled():
346387
config_dict["mem_scheduler"] = APIConfig.get_scheduler_config()
@@ -352,14 +393,19 @@ def create_user_config(user_name: str, user_id: str) -> tuple[MOSConfig, General
352393

353394
neo4j_community_config = APIConfig.get_neo4j_community_config(user_id)
354395
neo4j_config = APIConfig.get_neo4j_config(user_id)
355-
396+
internet_config = (
397+
APIConfig.get_internet_config()
398+
if os.getenv("ENABLE_INTERNET", "false").lower() == "true"
399+
else None
400+
)
356401
graph_db_backend_map = {
357402
"neo4j-community": neo4j_community_config,
358403
"neo4j": neo4j_config,
359404
}
360405
graph_db_backend = os.getenv("NEO4J_BACKEND", "neo4j-community").lower()
361406
if graph_db_backend in graph_db_backend_map:
362407
# Create MemCube config
408+
363409
default_cube_config = GeneralMemCubeConfig.model_validate(
364410
{
365411
"user_id": user_id,
@@ -374,6 +420,7 @@ def create_user_config(user_name: str, user_id: str) -> tuple[MOSConfig, General
374420
"config": graph_db_backend_map[graph_db_backend],
375421
},
376422
"embedder": APIConfig.get_embedder_config(),
423+
"internet_retriever": internet_config,
377424
},
378425
},
379426
"act_mem": {}
@@ -384,7 +431,10 @@ def create_user_config(user_name: str, user_id: str) -> tuple[MOSConfig, General
384431
)
385432
else:
386433
raise ValueError(f"Invalid Neo4j backend: {graph_db_backend}")
387-
434+
if os.getenv("ENABLE_INTERNET", "false").lower() == "true":
435+
default_cube_config.text_mem.config["internet_retriever"] = (
436+
APIConfig.get_internet_config()
437+
)
388438
default_mem_cube = GeneralMemCube(default_cube_config)
389439
return default_config, default_mem_cube
390440

@@ -405,6 +455,11 @@ def get_default_cube_config() -> GeneralMemCubeConfig | None:
405455
"neo4j-community": neo4j_community_config,
406456
"neo4j": neo4j_config,
407457
}
458+
internet_config = (
459+
APIConfig.get_internet_config()
460+
if os.getenv("ENABLE_INTERNET", "false").lower() == "true"
461+
else None
462+
)
408463
graph_db_backend = os.getenv("NEO4J_BACKEND", "neo4j-community").lower()
409464
if graph_db_backend in graph_db_backend_map:
410465
return GeneralMemCubeConfig.model_validate(
@@ -423,6 +478,7 @@ def get_default_cube_config() -> GeneralMemCubeConfig | None:
423478
"embedder": APIConfig.get_embedder_config(),
424479
"reorganize": os.getenv("MOS_ENABLE_REORGANIZE", "false").lower()
425480
== "true",
481+
"internet_retriever": internet_config,
426482
},
427483
},
428484
"act_mem": {}

src/memos/api/product_models.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ class ChatRequest(BaseRequest):
8383
query: str = Field(..., description="Chat query message")
8484
mem_cube_id: str | None = Field(None, description="Cube ID to use for chat")
8585
history: list[MessageDict] | None = Field(None, description="Chat history")
86+
internet_search: bool = Field(True, description="Whether to use internet search")
8687

8788

8889
class UserCreate(BaseRequest):

src/memos/api/routers/product_router.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,7 @@ async def generate_chat_response():
234234
user_id=chat_req.user_id,
235235
cube_id=chat_req.mem_cube_id,
236236
history=chat_req.history,
237+
internet_search=chat_req.internet_search,
237238
):
238239
yield chunk
239240
await asyncio.sleep(0.00001) # 50ms delay between chunks

src/memos/configs/mem_reader.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,15 @@ class BaseMemReaderConfig(BaseConfig):
1515
created_at: datetime = Field(
1616
default_factory=datetime.now, description="Creation timestamp for the MemReader"
1717
)
18+
19+
@field_validator("created_at", mode="before")
20+
@classmethod
21+
def parse_datetime(cls, value):
22+
"""Parse datetime from string if needed."""
23+
if isinstance(value, str):
24+
return datetime.fromisoformat(value.replace("Z", "+00:00"))
25+
return value
26+
1827
llm: LLMConfigFactory = Field(..., description="LLM configuration for the MemReader")
1928
embedder: EmbedderConfigFactory = Field(
2029
..., description="Embedder configuration for the MemReader"

src/memos/log.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ def _setup_logfile() -> Path:
4848
"class": "logging.handlers.RotatingFileHandler",
4949
"filename": _setup_logfile(),
5050
"maxBytes": 1024**2 * 10,
51-
"backupCount": 3,
51+
"backupCount": 10,
5252
"formatter": "standard",
5353
},
5454
},

src/memos/mem_cube/utils.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,10 +71,6 @@ def merge_config_with_default(
7171

7272
# Define graph_db fields to preserve (user-specific)
7373
preserve_graph_fields = {
74-
"uri",
75-
"user",
76-
"password",
77-
"db_name",
7874
"auto_create",
7975
"user_name",
8076
"use_multi_db",

src/memos/mem_os/core.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -530,6 +530,8 @@ def search(
530530
user_id: str | None = None,
531531
install_cube_ids: list[str] | None = None,
532532
top_k: int | None = None,
533+
mode: Literal["fast", "fine"] = "fast",
534+
internet_search: bool = False,
533535
) -> MOSSearchResult:
534536
"""
535537
Search for textual memories across all registered MemCubes.
@@ -567,7 +569,11 @@ def search(
567569
and self.config.enable_textual_memory
568570
):
569571
memories = mem_cube.text_mem.search(
570-
query, top_k=top_k if top_k else self.config.top_k
572+
query,
573+
top_k=top_k if top_k else self.config.top_k,
574+
mode=mode,
575+
manual_close_internet=not internet_search,
576+
info={"user_id": target_user_id, "session_id": str(uuid.uuid4())},
571577
)
572578
result["text_mem"].append({"cube_id": mem_cube_id, "memories": memories})
573579
logger.info(

src/memos/mem_os/product.py

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,7 @@ def _build_system_prompt(self, user_id: str, memories_all: list[TextualMemoryIte
363363
for i, memory in enumerate(memories_all, 1):
364364
# Format: [memory_id]: memory_content
365365
memory_id = f"{memory.id.split('-')[0]}" if hasattr(memory, "id") else f"mem_{i}"
366-
memory_content = memory.memory if hasattr(memory, "memory") else str(memory)
366+
memory_content = memory.memory[:500] if hasattr(memory, "memory") else str(memory)
367367
memory_context += f"{memory_id}: {memory_content}\n"
368368
return base_prompt + memory_context
369369

@@ -694,7 +694,7 @@ def get_suggestion_query(self, user_id: str, language: str = "zh") -> list[str]:
694694
"text_mem"
695695
]
696696
if text_mem_result:
697-
memories = "\n".join([m.memory for m in text_mem_result[0]["memories"]])
697+
memories = "\n".join([m.memory[:200] for m in text_mem_result[0]["memories"]])
698698
else:
699699
memories = ""
700700
message_list = [{"role": "system", "content": suggestion_prompt.format(memories=memories)}]
@@ -710,6 +710,7 @@ def chat_with_references(
710710
cube_id: str | None = None,
711711
history: MessageList | None = None,
712712
top_k: int = 10,
713+
internet_search: bool = False,
713714
) -> Generator[str, None, None]:
714715
"""
715716
Chat with LLM with memory references and streaming output.
@@ -729,7 +730,12 @@ def chat_with_references(
729730
memories_list = []
730731
yield f"data: {json.dumps({'type': 'status', 'data': '0'})}\n\n"
731732
memories_result = super().search(
732-
query, user_id, install_cube_ids=[cube_id] if cube_id else None, top_k=top_k
733+
query,
734+
user_id,
735+
install_cube_ids=[cube_id] if cube_id else None,
736+
top_k=top_k,
737+
mode="fine",
738+
internet_search=internet_search,
733739
)["text_mem"]
734740
yield f"data: {json.dumps({'type': 'status', 'data': '1'})}\n\n"
735741
self._send_message_to_scheduler(
@@ -829,6 +835,7 @@ def chat_with_references(
829835
memories_json["metadata"]["embedding"] = []
830836
memories_json["metadata"]["sources"] = []
831837
memories_json["metadata"]["memory"] = memories.memory
838+
memories_json["metadata"]["id"] = memories.id
832839
reference.append({"metadata": memories_json["metadata"]})
833840

834841
yield f"data: {json.dumps({'type': 'reference', 'data': reference})}\n\n"
@@ -1008,13 +1015,18 @@ def get_subgraph(
10081015
return reformat_memory_list
10091016

10101017
def search(
1011-
self, query: str, user_id: str, install_cube_ids: list[str] | None = None, top_k: int = 10
1018+
self,
1019+
query: str,
1020+
user_id: str,
1021+
install_cube_ids: list[str] | None = None,
1022+
top_k: int = 10,
1023+
mode: Literal["fast", "fine"] = "fast",
10121024
):
10131025
"""Search memories for a specific user."""
10141026

10151027
# Load user cubes if not already loaded
10161028
self._load_user_cubes(user_id, self.default_cube_config)
1017-
search_result = super().search(query, user_id, install_cube_ids, top_k)
1029+
search_result = super().search(query, user_id, install_cube_ids, top_k, mode=mode)
10181030
text_memory_list = search_result["text_mem"]
10191031
reformat_memory_list = []
10201032
for memory in text_memory_list:

src/memos/memories/textual/tree.py

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -117,13 +117,19 @@ def search(
117117
logger.warning(
118118
"Internet retriever is init by config , but this search set manual_close_internet is True and will close it"
119119
)
120-
self.internet_retriever = None
121-
searcher = Searcher(
122-
self.dispatcher_llm,
123-
self.graph_store,
124-
self.embedder,
125-
internet_retriever=self.internet_retriever,
126-
)
120+
searcher = Searcher(
121+
self.dispatcher_llm,
122+
self.graph_store,
123+
self.embedder,
124+
internet_retriever=None,
125+
)
126+
else:
127+
searcher = Searcher(
128+
self.dispatcher_llm,
129+
self.graph_store,
130+
self.embedder,
131+
internet_retriever=self.internet_retriever,
132+
)
127133
return searcher.search(query, top_k, info, mode, memory_type)
128134

129135
def get_relevant_subgraph(

src/memos/memories/textual/tree_text_memory/organize/reorganizer.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,8 +125,8 @@ def _run_structure_organizer_loop(self):
125125
"""
126126
import schedule
127127

128-
schedule.every(20).seconds.do(self.optimize_structure, scope="LongTermMemory")
129-
schedule.every(20).seconds.do(self.optimize_structure, scope="UserMemory")
128+
schedule.every(600).seconds.do(self.optimize_structure, scope="LongTermMemory")
129+
schedule.every(600).seconds.do(self.optimize_structure, scope="UserMemory")
130130

131131
logger.info("Structure optimizer schedule started.")
132132
while not getattr(self, "_stop_scheduler", False):

0 commit comments

Comments
 (0)