Skip to content

Commit e1de4ad

Browse files
fridayLCaralHsiCarltonXiangharvey_xiang
authored
Feat/merge dev (#374)
* fix: format (#341) * change version to 1.1.0 * change: version to v1.1.1 * feat: add memory size in product api (#348) * feat: add memory size config in product api * fix: memory_size config bug * Fix/remove bug (#356) * fix: nebula search bug * fix: nebula search bug * fix: auto create bug * feat: add single-db-only assertion * feat: make count_nodes support optional memory_type filtering * fix: dim_field when filter non-embedding nodes * feat: add optional whether include embedding when export graph * fix[WIP]: remove oldest memory update * feat: modify nebula search embedding efficiency * fix: modify nebula remove old memory * Fix/api client (#357) * fix: api client get_message models * fix: format error --------- Co-authored-by: chunyu li <[email protected]> Co-authored-by: harvey_xiang <[email protected]> Co-authored-by: CaralHsi <[email protected]> * fix: remove old mem (#361) * feat: only single-db mode in nebula now; modify index gql for better efficiency (#363) * feat: only single-db mode in nebula now; modify index gql for better effciency * feat: delete multi-db nebula example * fix:code ci * fix:code ci * fix: nebular bug --------- Co-authored-by: CaralHsi <[email protected]> Co-authored-by: HarveyXiang <[email protected]> Co-authored-by: harvey_xiang <[email protected]>
1 parent 7bb5bd6 commit e1de4ad

File tree

10 files changed

+69
-118
lines changed

10 files changed

+69
-118
lines changed

examples/basic_modules/nebular_example.py

Lines changed: 0 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -52,56 +52,6 @@ def embed_memory_item(memory: str) -> list[float]:
5252
return embedding_list
5353

5454

55-
def example_multi_db(db_name: str = "paper"):
56-
# Step 1: Build factory config
57-
config = GraphDBConfigFactory(
58-
backend="nebular",
59-
config={
60-
"uri": json.loads(os.getenv("NEBULAR_HOSTS", "localhost")),
61-
"user": os.getenv("NEBULAR_USER", "root"),
62-
"password": os.getenv("NEBULAR_PASSWORD", "xxxxxx"),
63-
"space": db_name,
64-
"use_multi_db": True,
65-
"auto_create": True,
66-
"embedding_dimension": embedder_dimension,
67-
},
68-
)
69-
70-
# Step 2: Instantiate the graph store
71-
graph = GraphStoreFactory.from_config(config)
72-
graph.clear()
73-
74-
# Step 3: Create topic node
75-
topic = TextualMemoryItem(
76-
memory="This research addresses long-term multi-UAV navigation for energy-efficient communication coverage.",
77-
metadata=TreeNodeTextualMemoryMetadata(
78-
memory_type="LongTermMemory",
79-
key="Multi-UAV Long-Term Coverage",
80-
hierarchy_level="topic",
81-
type="fact",
82-
memory_time="2024-01-01",
83-
source="file",
84-
sources=["paper://multi-uav-coverage/intro"],
85-
status="activated",
86-
confidence=95.0,
87-
tags=["UAV", "coverage", "multi-agent"],
88-
entities=["UAV", "coverage", "navigation"],
89-
visibility="public",
90-
updated_at=datetime.now().isoformat(),
91-
embedding=embed_memory_item(
92-
"This research addresses long-term "
93-
"multi-UAV navigation for "
94-
"energy-efficient communication "
95-
"coverage."
96-
),
97-
),
98-
)
99-
100-
graph.add_node(
101-
id=topic.id, memory=topic.memory, metadata=topic.metadata.model_dump(exclude_none=True)
102-
)
103-
104-
10555
def example_shared_db(db_name: str = "shared-traval-group"):
10656
"""
10757
Example: Single(Shared)-DB multi-tenant (logical isolation)
@@ -404,9 +354,6 @@ def example_complex_shared_db(db_name: str = "shared-traval-group-complex"):
404354

405355

406356
if __name__ == "__main__":
407-
print("\n=== Example: Multi-DB ===")
408-
example_multi_db(db_name="paper-new")
409-
410357
print("\n=== Example: Single-DB ===")
411358
example_shared_db(db_name="shared_traval_group-new")
412359

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
##############################################################################
55

66
name = "MemoryOS"
7-
version = "1.0.1"
7+
version = "1.1.1"
88
description = "Intelligence Begins with Memory"
99
license = {text = "Apache-2.0"}
1010
readme = "README.md"

src/memos/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
__version__ = "1.0.1"
1+
__version__ = "1.1.1"
22

33
from memos.configs.mem_cube import GeneralMemCubeConfig
44
from memos.configs.mem_os import MOSConfig

src/memos/api/client.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ def get_message(
5050
)
5151
response.raise_for_status()
5252
response_data = response.json()
53+
5354
return MemOSGetMessagesResponse(**response_data)
5455
except Exception as e:
5556
logger.error(f"Failed to get messages (retry {retry + 1}/3): {e}")
@@ -74,6 +75,7 @@ def add_message(
7475
)
7576
response.raise_for_status()
7677
response_data = response.json()
78+
7779
return MemOSAddResponse(**response_data)
7880
except Exception as e:
7981
logger.error(f"Failed to add memory (retry {retry + 1}/3): {e}")
@@ -102,6 +104,7 @@ def search_memory(
102104
)
103105
response.raise_for_status()
104106
response_data = response.json()
107+
105108
return MemOSSearchResponse(**response_data)
106109
except Exception as e:
107110
logger.error(f"Failed to search memory (retry {retry + 1}/3): {e}")

src/memos/api/config.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -530,6 +530,13 @@ def create_user_config(user_name: str, user_id: str) -> tuple[MOSConfig, General
530530
"embedder": APIConfig.get_embedder_config(),
531531
"internet_retriever": internet_config,
532532
"reranker": APIConfig.get_reranker_config(),
533+
"reorganize": os.getenv("MOS_ENABLE_REORGANIZE", "false").lower()
534+
== "true",
535+
"memory_size": {
536+
"WorkingMemory": os.getenv("NEBULAR_WORKING_MEMORY", 20),
537+
"LongTermMemory": os.getenv("NEBULAR_LONGTERM_MEMORY", 1e6),
538+
"UserMemory": os.getenv("NEBULAR_USER_MEMORY", 1e6),
539+
},
533540
},
534541
},
535542
"act_mem": {}
@@ -587,6 +594,11 @@ def get_default_cube_config() -> GeneralMemCubeConfig | None:
587594
"reorganize": os.getenv("MOS_ENABLE_REORGANIZE", "false").lower()
588595
== "true",
589596
"internet_retriever": internet_config,
597+
"memory_size": {
598+
"WorkingMemory": os.getenv("NEBULAR_WORKING_MEMORY", 20),
599+
"LongTermMemory": os.getenv("NEBULAR_LONGTERM_MEMORY", 1e6),
600+
"UserMemory": os.getenv("NEBULAR_USER_MEMORY", 1e6),
601+
},
590602
},
591603
},
592604
"act_mem": {}

src/memos/api/product_models.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ class GetMessagesData(BaseModel):
241241
"""Data model for get messages response based on actual API."""
242242

243243
message_detail_list: list[MessageDetail] = Field(
244-
default_factory=list, alias="memory_detail_list", description="List of message details"
244+
default_factory=list, alias="message_detail_list", description="List of message details"
245245
)
246246

247247

src/memos/graph_dbs/nebular.py

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,19 @@ def _get_or_create_shared_client(cls, cfg: NebulaGraphDBConfig) -> tuple[str, "N
187187
client = cls._CLIENT_CACHE.get(key)
188188
if client is None:
189189
# Connection setting
190+
191+
tmp_client = NebulaClient(
192+
hosts=cfg.uri,
193+
username=cfg.user,
194+
password=cfg.password,
195+
session_config=SessionConfig(graph=None),
196+
session_pool_config=SessionPoolConfig(size=1, wait_timeout=3000),
197+
)
198+
try:
199+
cls._ensure_space_exists(tmp_client, cfg)
200+
finally:
201+
tmp_client.close()
202+
190203
conn_conf: ConnectionConfig | None = getattr(cfg, "conn_config", None)
191204
if conn_conf is None:
192205
conn_conf = ConnectionConfig.from_defults(
@@ -317,6 +330,7 @@ def __init__(self, config: NebulaGraphDBConfig):
317330
}
318331
"""
319332

333+
assert config.use_multi_db is False, "Multi-DB MODE IS NOT SUPPORTED"
320334
self.config = config
321335
self.db_name = config.space
322336
self.user_name = config.user_name
@@ -349,7 +363,7 @@ def __init__(self, config: NebulaGraphDBConfig):
349363
if (str(self.embedding_dimension) != str(self.default_memory_dimension))
350364
else "embedding"
351365
)
352-
self.system_db_name = "system" if config.use_multi_db else config.space
366+
self.system_db_name = config.space
353367

354368
# ---- NEW: pool acquisition strategy
355369
# Get or create a shared pool from the class-level cache
@@ -436,7 +450,7 @@ def remove_oldest_memory(
436450
WHERE n.memory_type = '{memory_type}'
437451
{optional_condition}
438452
ORDER BY n.updated_at DESC
439-
OFFSET {keep_latest}
453+
OFFSET {int(keep_latest)}
440454
DETACH DELETE n
441455
"""
442456
self.execute_query(query)
@@ -481,7 +495,7 @@ def node_not_exist(self, scope: str, user_name: str | None = None) -> int:
481495
user_name = user_name if user_name else self.config.user_name
482496
filter_clause = f'n.memory_type = "{scope}" AND n.user_name = "{user_name}"'
483497
query = f"""
484-
MATCH (n@Memory)
498+
MATCH (n@Memory /*+ INDEX(idx_memory_user_name) */)
485499
WHERE {filter_clause}
486500
RETURN n.id AS id
487501
LIMIT 1
@@ -838,7 +852,7 @@ def get_neighbors_by_tag(
838852
query = f"""
839853
LET tag_list = {tag_list_literal}
840854
841-
MATCH (n@Memory)
855+
MATCH (n@Memory /*+ INDEX(idx_memory_user_name) */)
842856
WHERE {where_clause}
843857
RETURN {return_fields},
844858
size( filter( n.tags, t -> t IN tag_list ) ) AS overlap_count
@@ -1392,6 +1406,17 @@ def get_structure_optimization_candidates(
13921406
logger.error(f"Failed : {e}, traceback: {traceback.format_exc()}")
13931407
return candidates
13941408

1409+
@timed
1410+
def drop_database(self) -> None:
1411+
"""
1412+
Permanently delete the entire database this instance is using.
1413+
WARNING: This operation is destructive and cannot be undone.
1414+
"""
1415+
raise ValueError(
1416+
f"Refusing to drop protected database: `{self.db_name}` in "
1417+
f"Shared Database Multi-Tenant mode"
1418+
)
1419+
13951420
@timed
13961421
def detect_conflicts(self) -> list[tuple[str, str]]:
13971422
"""
@@ -1462,6 +1487,25 @@ def merge_nodes(self, id1: str, id2: str) -> str:
14621487
"""
14631488
raise NotImplementedError
14641489

1490+
@classmethod
1491+
def _ensure_space_exists(cls, tmp_client, cfg):
1492+
"""Lightweight check to ensure target graph (space) exists."""
1493+
db_name = getattr(cfg, "space", None)
1494+
if not db_name:
1495+
logger.warning("[NebulaGraphDBSync] No `space` specified in cfg.")
1496+
return
1497+
1498+
try:
1499+
res = tmp_client.execute("SHOW GRAPHS;")
1500+
existing = {row.values()[0].as_string() for row in res}
1501+
if db_name not in existing:
1502+
tmp_client.execute(f"CREATE GRAPH IF NOT EXISTS `{db_name}` TYPED MemOSBgeM3Type;")
1503+
logger.info(f"✅ Graph `{db_name}` created before session binding.")
1504+
else:
1505+
logger.debug(f"Graph `{db_name}` already exists.")
1506+
except Exception:
1507+
logger.exception("[NebulaGraphDBSync] Failed to ensure space exists")
1508+
14651509
@timed
14661510
def _ensure_database_exists(self):
14671511
graph_type_name = "MemOSBgeM3Type"

src/memos/memories/textual/tree.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -326,10 +326,10 @@ def load(self, dir: str) -> None:
326326
except Exception as e:
327327
logger.error(f"An error occurred while loading memories: {e}")
328328

329-
def dump(self, dir: str) -> None:
329+
def dump(self, dir: str, include_embedding: bool = False) -> None:
330330
"""Dump memories to os.path.join(dir, self.config.memory_filename)"""
331331
try:
332-
json_memories = self.graph_store.export_graph()
332+
json_memories = self.graph_store.export_graph(include_embedding=include_embedding)
333333

334334
os.makedirs(dir, exist_ok=True)
335335
memory_file = os.path.join(dir, self.config.memory_filename)

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ def __init__(
4444
"LongTermMemory": 1500,
4545
"UserMemory": 480,
4646
}
47+
logger.info(f"MemorySize is {self.memory_size}")
4748
self._threshold = threshold
4849
self.is_reorganize = is_reorganize
4950
self.reorganizer = GraphStructureReorganizer(

tests/api/test_start_api.py

Lines changed: 0 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -82,62 +82,6 @@ def mock_mos():
8282
yield mock_instance
8383

8484

85-
def test_configure(mock_mos):
86-
"""Test configuration endpoint."""
87-
with patch("memos.api.start_api.MOS_INSTANCE", None):
88-
# Use a valid configuration
89-
valid_config = {
90-
"user_id": "test_user",
91-
"session_id": "test_session",
92-
"enable_textual_memory": True,
93-
"enable_activation_memory": False,
94-
"top_k": 5,
95-
"chat_model": {
96-
"backend": "openai",
97-
"config": {
98-
"model_name_or_path": "gpt-3.5-turbo",
99-
"api_key": "test_key",
100-
"temperature": 0.7,
101-
"api_base": "https://api.openai.com/v1",
102-
},
103-
},
104-
"mem_reader": {
105-
"backend": "simple_struct",
106-
"config": {
107-
"llm": {
108-
"backend": "openai",
109-
"config": {
110-
"model_name_or_path": "gpt-3.5-turbo",
111-
"api_key": "test_key",
112-
"temperature": 0.7,
113-
"api_base": "https://api.openai.com/v1",
114-
},
115-
},
116-
"embedder": {
117-
"backend": "sentence_transformer",
118-
"config": {"model_name_or_path": "all-MiniLM-L6-v2"},
119-
},
120-
"chunker": {
121-
"backend": "sentence",
122-
"config": {
123-
"tokenizer_or_token_counter": "gpt2",
124-
"chunk_size": 512,
125-
"chunk_overlap": 128,
126-
"min_sentences_per_chunk": 1,
127-
},
128-
},
129-
},
130-
},
131-
}
132-
response = client.post("/configure", json=valid_config)
133-
assert response.status_code == 200
134-
assert response.json() == {
135-
"code": 200,
136-
"message": "Configuration set successfully",
137-
"data": None,
138-
}
139-
140-
14185
def test_configure_error(mock_mos):
14286
"""Test configuration endpoint with error."""
14387
with patch("memos.api.start_api.MOS_INSTANCE", None):

0 commit comments

Comments
 (0)