Skip to content

Commit 9f75f24

Browse files
authored
Merge branch 'dev' into feat/demo-product
2 parents 86e16df + 244e748 commit 9f75f24

File tree

32 files changed

+911
-306
lines changed

32 files changed

+911
-306
lines changed

evaluation/scripts/locomo/openai_memory_locomo_eval_guide.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ Can you please extract relevant information from this conversation and create me
8181
* Click on **Manage** in the memory confirmation to view the newly generated memories.
8282
* Create a new local `.txt` file with the same name as the input file (e.g., `0-D1.txt`).
8383
* Copy each memory entry from ChatGPT and paste it into the new file, with each memory on a new line.
84-
5. **Reset Memories for the Next Conversation:**
84+
5. **Reset Memories for the Next Conversation:**
8585
* Once all sessions for a conversation are complete, it is essential to **delete all memories to ensure a clean state for the next conversation**. Navigate to Settings -> Personalization -> Manage and click Delete all.
8686

8787
**Example Memory Output (`0-D9.txt`):**

examples/basic_modules/embedder.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,3 +46,23 @@
4646
text_hf = "This is a sample text for Hugging Face embedding generation."
4747
embedding_hf = embedder_hf.embed([text_hf])
4848
print("Scenario 3 HF embedding shape:", len(embedding_hf[0]))
49+
print("==" * 20)
50+
51+
# === Scenario 4: Using UniversalAPIEmbedder ===
52+
53+
config_api = EmbedderConfigFactory.model_validate(
54+
{
55+
"backend": "universal_api",
56+
"config": {
57+
"provider": "openai",
58+
"api_key": "<YOUR_KEY>",
59+
"model_name_or_path": "text-embedding-3-large",
60+
"base_url": "https://api.myproxy.com/v1",
61+
},
62+
}
63+
)
64+
embedder_api = EmbedderFactory.from_config(config_api)
65+
text_api = "This is a sample text for embedding generation using OpenAI API."
66+
embedding_api = embedder_api.embed([text_api])
67+
print("Scenario 4: OpenAI API embedding vector length:", len(embedding_api[0]))
68+
print("Embedding preview:", embedding_api[0][:10])

examples/basic_modules/neo4j_example.py

Lines changed: 90 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,7 @@
88

99

1010
embedder_config = EmbedderConfigFactory.model_validate(
11-
{
12-
"backend": "sentence_transformer",
13-
"config": {
14-
"model_name_or_path": "nomic-ai/nomic-embed-text-v1.5",
15-
},
16-
}
11+
{"backend": "ollama", "config": {"model_name_or_path": "nomic-embed-text:latest"}}
1712
)
1813
embedder = EmbedderFactory.from_config(embedder_config)
1914

@@ -22,7 +17,7 @@ def embed_memory_item(memory: str) -> list[float]:
2217
return embedder.embed([memory])[0]
2318

2419

25-
def example_1_paper(db_name: str = "paper"):
20+
def example_multi_db(db_name: str = "paper"):
2621
# Step 1: Build factory config
2722
config = GraphDBConfigFactory(
2823
backend="neo4j",
@@ -33,6 +28,7 @@ def example_1_paper(db_name: str = "paper"):
3328
"db_name": db_name,
3429
"auto_create": True,
3530
"embedding_dimension": 768,
31+
"use_multi_db": True,
3632
},
3733
)
3834

@@ -68,7 +64,7 @@ def example_1_paper(db_name: str = "paper"):
6864
)
6965

7066
graph.add_node(
71-
id=topic.id, content=topic.memory, metadata=topic.metadata.model_dump(exclude_none=True)
67+
id=topic.id, memory=topic.memory, metadata=topic.metadata.model_dump(exclude_none=True)
7268
)
7369

7470
# Step 4: Define and write concept nodes
@@ -150,7 +146,7 @@ def example_1_paper(db_name: str = "paper"):
150146
for concept in concepts:
151147
graph.add_node(
152148
id=concept.id,
153-
content=concept.memory,
149+
memory=concept.memory,
154150
metadata=concept.metadata.model_dump(exclude_none=True),
155151
)
156152
graph.add_edge(source_id=concept.id, target_id=topic.id, type="RELATED")
@@ -259,113 +255,101 @@ def example_1_paper(db_name: str = "paper"):
259255
print(graph.get_node(node_i["id"]))
260256

261257

262-
def example_2_travel(db_name: str = "travel"):
263-
# Step 1: Build factory config
264-
config = GraphDBConfigFactory(
265-
backend="neo4j",
266-
config={
267-
"uri": "bolt://localhost:7687",
268-
"user": "neo4j",
269-
"password": "12345678",
270-
"db_name": db_name,
271-
"auto_create": True,
272-
"embedding_dimension": 768,
273-
},
274-
)
275-
276-
# Step 2: Instantiate the graph store
277-
graph = GraphStoreFactory.from_config(config)
278-
graph.clear()
279-
280-
# Step 3: Create topic node
281-
topic = TextualMemoryItem(
282-
memory="Travel",
283-
metadata=TreeNodeTextualMemoryMetadata(
284-
memory_type="LongTermMemory",
285-
hierarchy_level="topic",
286-
status="activated",
287-
visibility="public",
288-
embedding=embed_memory_item("Travel"),
289-
),
290-
)
291-
292-
graph.add_node(
293-
id=topic.id, content=topic.memory, metadata=topic.metadata.model_dump(exclude_none=True)
294-
)
295-
296-
concept1 = TextualMemoryItem(
297-
memory="Travel in Italy",
298-
metadata=TreeNodeTextualMemoryMetadata(
299-
memory_type="LongTermMemory",
300-
hierarchy_level="concept",
301-
status="activated",
302-
visibility="public",
303-
embedding=embed_memory_item("Travel in Italy"),
304-
),
305-
)
258+
def example_shared_db(db_name: str = "shared-traval-group"):
259+
"""
260+
Example: Single(Shared)-DB multi-tenant (logical isolation)
261+
Multiple users' data in the same Neo4j DB with user_name as a tag.
262+
"""
263+
# users
264+
user_list = ["travel_member_alice", "travel_member_bob"]
265+
266+
for user_name in user_list:
267+
# Step 1: Build factory config
268+
config = GraphDBConfigFactory(
269+
backend="neo4j",
270+
config={
271+
"uri": "bolt://localhost:7687",
272+
"user": "neo4j",
273+
"password": "12345678",
274+
"db_name": db_name,
275+
"user_name": user_name,
276+
"use_multi_db": False,
277+
"auto_create": True,
278+
"embedding_dimension": 768,
279+
},
280+
)
281+
# Step 2: Instantiate graph store
282+
graph = GraphStoreFactory.from_config(config)
283+
print(f"\n[INFO] Working in shared DB: {db_name}, for user: {user_name}")
284+
graph.clear()
285+
286+
# Step 3: Create topic node
287+
topic = TextualMemoryItem(
288+
memory=f"Travel notes for {user_name}",
289+
metadata=TreeNodeTextualMemoryMetadata(
290+
memory_type="LongTermMemory",
291+
hierarchy_level="topic",
292+
status="activated",
293+
visibility="public",
294+
embedding=embed_memory_item(f"Travel notes for {user_name}"),
295+
),
296+
)
306297

307-
graph.add_node(
308-
id=concept1.id,
309-
content=concept1.memory,
310-
metadata=concept1.metadata.model_dump(exclude_none=True),
311-
)
312-
graph.add_edge(source_id=topic.id, target_id=concept1.id, type="INCLUDE")
298+
graph.add_node(
299+
id=topic.id, memory=topic.memory, metadata=topic.metadata.model_dump(exclude_none=True)
300+
)
313301

314-
concept2 = TextualMemoryItem(
315-
memory="Traval plan",
316-
metadata=TreeNodeTextualMemoryMetadata(
317-
memory_type="LongTermMemory",
318-
hierarchy_level="concept",
319-
status="activated",
320-
visibility="public",
321-
embedding=embed_memory_item("Traval plan"),
322-
),
323-
)
302+
# Step 4: Add a concept for each user
303+
concept = TextualMemoryItem(
304+
memory=f"Itinerary plan for {user_name}",
305+
metadata=TreeNodeTextualMemoryMetadata(
306+
memory_type="LongTermMemory",
307+
hierarchy_level="concept",
308+
status="activated",
309+
visibility="public",
310+
embedding=embed_memory_item(f"Itinerary plan for {user_name}"),
311+
),
312+
)
324313

325-
graph.add_node(
326-
id=concept2.id,
327-
content=concept2.memory,
328-
metadata=concept2.metadata.model_dump(exclude_none=True),
329-
)
330-
graph.add_edge(source_id=concept1.id, target_id=concept2.id, type="INCLUDE")
314+
graph.add_node(
315+
id=concept.id,
316+
memory=concept.memory,
317+
metadata=concept.metadata.model_dump(exclude_none=True),
318+
)
331319

332-
fact1 = TextualMemoryItem(
333-
memory="10-Day Itinerary for Traveling in Italy",
334-
metadata=TreeNodeTextualMemoryMetadata(
335-
memory_type="WorkingMemory",
336-
key="Reward Components",
337-
value="Coverage gain, energy usage penalty, overlap penalty",
338-
hierarchy_level="fact",
339-
type="fact",
340-
memory_time="2024-01-01",
341-
source="file",
342-
sources=["paper://multi-uav-coverage/reward-details"],
343-
status="activated",
344-
confidence=90.0,
345-
tags=["reward", "overlap", "multi-agent"],
346-
entities=["coverage", "energy", "overlap"],
347-
visibility="public",
348-
embedding=embed_memory_item("10-Day Itinerary for Traveling in Italy"),
349-
updated_at=datetime.now().isoformat(),
350-
),
351-
)
320+
# Link concept to topic
321+
graph.add_edge(source_id=concept.id, target_id=topic.id, type="INCLUDE")
352322

353-
graph.add_node(
354-
id=fact1.id, content=fact1.memory, metadata=fact1.metadata.model_dump(exclude_none=True)
355-
)
356-
graph.add_edge(source_id=concept2.id, target_id=fact1.id, type="INCLUDE")
323+
print(f"[INFO] Added nodes for {user_name}")
357324

325+
# Step 5: Query and print ALL for verification
326+
print("\n=== Export entire DB (for verification, includes ALL users) ===")
327+
graph = GraphStoreFactory.from_config(config)
358328
all_graph_data = graph.export_graph()
359329
print(all_graph_data)
360330

361-
nodes = graph.search_by_embedding(vector=embed_memory_item("what does FT reflect?"), top_k=1)
362-
363-
for node_i in nodes:
364-
print(graph.get_node(node_i["id"]))
331+
# Step 6: Search for alice's data only
332+
print("\n=== Search for travel_member_alice ===")
333+
config_alice = GraphDBConfigFactory(
334+
backend="neo4j",
335+
config={
336+
"uri": "bolt://localhost:7687",
337+
"user": "neo4j",
338+
"password": "12345678",
339+
"db_name": db_name,
340+
"user_name": user_list[0],
341+
"embedding_dimension": 768,
342+
},
343+
)
344+
graph_alice = GraphStoreFactory.from_config(config_alice)
345+
nodes = graph_alice.search_by_embedding(vector=embed_memory_item("travel itinerary"), top_k=1)
346+
for node in nodes:
347+
print(graph_alice.get_node(node["id"]))
365348

366349

367350
if __name__ == "__main__":
368-
example_1_paper(db_name="paper")
351+
print("\n=== Example: Multi-DB ===")
352+
example_multi_db(db_name="paper")
369353

370-
if __name__ == "__main__":
371-
example_2_travel(db_name="traval")
354+
print("\n=== Example: Single-DB ===")
355+
example_shared_db(db_name="shared-traval-group11")

examples/core_memories/tree_textual_memory.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import time
2+
13
from memos import log
24
from memos.configs.embedder import EmbedderConfigFactory
35
from memos.configs.mem_reader import SimpleStructMemReaderConfig
@@ -25,7 +27,9 @@ def embed_memory_item(memory: str) -> list[float]:
2527
return embedder.embed([memory])[0]
2628

2729

28-
tree_config = TreeTextMemoryConfig.from_json_file("examples/data/config/tree_config.json")
30+
tree_config = TreeTextMemoryConfig.from_json_file(
31+
"examples/data/config/tree_config_shared_database.json"
32+
)
2933
my_tree_textual_memory = TreeTextMemory(tree_config)
3034
my_tree_textual_memory.delete_all()
3135

@@ -185,6 +189,8 @@ def embed_memory_item(memory: str) -> list[float]:
185189
my_tree_textual_memory.add(m_list)
186190
my_tree_textual_memory.memory_manager.wait_reorganizer()
187191

192+
time.sleep(60)
193+
188194
results = my_tree_textual_memory.search(
189195
"Talk about the user's childhood story?",
190196
top_k=10,
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
{
2+
"extractor_llm": {
3+
"backend": "ollama",
4+
"config": {
5+
"model_name_or_path": "qwen3:0.6b",
6+
"temperature": 0.0,
7+
"remove_think_prefix": true,
8+
"max_tokens": 8192
9+
}
10+
},
11+
"dispatcher_llm": {
12+
"backend": "ollama",
13+
"config": {
14+
"model_name_or_path": "qwen3:0.6b",
15+
"temperature": 0.0,
16+
"remove_think_prefix": true,
17+
"max_tokens": 8192
18+
}
19+
},
20+
"embedder": {
21+
"backend": "ollama",
22+
"config": {
23+
"model_name_or_path": "nomic-embed-text:latest"
24+
}
25+
},
26+
"graph_db": {
27+
"backend": "neo4j",
28+
"config": {
29+
"uri": "bolt://localhost:7687",
30+
"user": "neo4j",
31+
"password": "12345678",
32+
"db_name": "shared-tree-textual-memory",
33+
"user_name": "alice",
34+
"auto_create": true,
35+
"use_multi_db": false,
36+
"embedding_dimension": 768
37+
}
38+
},
39+
"reorganize": true
40+
}

poetry.lock

Lines changed: 21 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
[tool.poetry]
44
name = "MemoryOS"
5-
version = "0.1.13"
5+
version = "0.2.0"
66
description = "Intelligence Begins with Memory"
77
license = "Apache-2.0"
88
authors = ["MemTensor <[email protected]>"]
@@ -28,6 +28,7 @@ sqlalchemy = "^2.0.41"
2828
redis = "^6.2.0"
2929
pika = "^1.3.2"
3030
schedule = "^1.2.2"
31+
volcengine-python-sdk = "^4.0.4"
3132

3233
[tool.poetry.group.dev]
3334
optional = false

0 commit comments

Comments
 (0)