Skip to content

Commit dabe6b5

Browse files
committed
Merge memtensor/dev: resolve conflict in product_models.py, keep dev branch version
2 parents 8ec9bec + 0a4a935 commit dabe6b5

File tree

71 files changed

+4956
-1067
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

71 files changed

+4956
-1067
lines changed
Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
"""
2+
DeepSearch Agent Usage Examples - Simplified Version
3+
4+
This example demonstrates simplified initialization of DeepSearchMemAgent without
5+
external config builders, using APIConfig methods directly.
6+
"""
7+
8+
import os
9+
10+
from typing import Any
11+
12+
from memos.api.config import APIConfig
13+
from memos.configs.embedder import EmbedderConfigFactory
14+
from memos.configs.graph_db import GraphDBConfigFactory
15+
from memos.configs.internet_retriever import InternetRetrieverConfigFactory
16+
from memos.configs.llm import LLMConfigFactory
17+
from memos.configs.mem_agent import MemAgentConfigFactory
18+
from memos.configs.mem_reader import MemReaderConfigFactory
19+
from memos.configs.reranker import RerankerConfigFactory
20+
from memos.embedders.factory import EmbedderFactory
21+
from memos.graph_dbs.factory import GraphStoreFactory
22+
from memos.llms.factory import LLMFactory
23+
from memos.log import get_logger
24+
from memos.mem_agent.deepsearch_agent import DeepSearchMemAgent
25+
from memos.mem_agent.factory import MemAgentFactory
26+
from memos.mem_cube.navie import NaiveMemCube
27+
from memos.mem_reader.factory import MemReaderFactory
28+
from memos.memories.textual.simple_tree import SimpleTreeTextMemory
29+
from memos.memories.textual.tree_text_memory.organize.manager import MemoryManager
30+
from memos.memories.textual.tree_text_memory.retrieve.internet_retriever_factory import (
31+
InternetRetrieverFactory,
32+
)
33+
from memos.reranker.factory import RerankerFactory
34+
35+
36+
logger = get_logger(__name__)
37+
38+
39+
def build_minimal_components():
40+
"""
41+
Build minimal components for DeepSearchMemAgent with simplified configuration.
42+
43+
This function creates all necessary components using APIConfig methods,
44+
similar to config_builders.py but inline for easier customization.
45+
"""
46+
logger.info("Initializing simplified MemOS components...")
47+
48+
# Build component configurations using APIConfig methods (like config_builders.py)
49+
50+
# Graph DB configuration - using APIConfig.get_nebular_config()
51+
graph_db_backend = os.getenv("NEO4J_BACKEND", "polardb").lower()
52+
graph_db_backend_map = {
53+
"polardb": APIConfig.get_polardb_config(),
54+
}
55+
graph_db_config = GraphDBConfigFactory.model_validate(
56+
{
57+
"backend": graph_db_backend,
58+
"config": graph_db_backend_map[graph_db_backend],
59+
}
60+
)
61+
62+
# LLM configuration - using APIConfig.get_openai_config()
63+
llm_config = LLMConfigFactory.model_validate(
64+
{
65+
"backend": "openai",
66+
"config": APIConfig.get_openai_config(),
67+
}
68+
)
69+
70+
# Embedder configuration - using APIConfig.get_embedder_config()
71+
embedder_config = EmbedderConfigFactory.model_validate(APIConfig.get_embedder_config())
72+
73+
# Memory reader configuration - using APIConfig.get_product_default_config()
74+
mem_reader_config = MemReaderConfigFactory.model_validate(
75+
APIConfig.get_product_default_config()["mem_reader"]
76+
)
77+
78+
# Reranker configuration - using APIConfig.get_reranker_config()
79+
reranker_config = RerankerConfigFactory.model_validate(APIConfig.get_reranker_config())
80+
81+
# Internet retriever configuration - using APIConfig.get_internet_config()
82+
internet_retriever_config = InternetRetrieverConfigFactory.model_validate(
83+
APIConfig.get_internet_config()
84+
)
85+
86+
logger.debug("Component configurations built successfully")
87+
88+
# Create component instances
89+
graph_db = GraphStoreFactory.from_config(graph_db_config)
90+
llm = LLMFactory.from_config(llm_config)
91+
embedder = EmbedderFactory.from_config(embedder_config)
92+
mem_reader = MemReaderFactory.from_config(mem_reader_config)
93+
reranker = RerankerFactory.from_config(reranker_config)
94+
internet_retriever = InternetRetrieverFactory.from_config(
95+
internet_retriever_config, embedder=embedder
96+
)
97+
98+
logger.debug("Core components instantiated")
99+
100+
# Get default cube configuration like component_init.py
101+
default_cube_config = APIConfig.get_default_cube_config()
102+
103+
# Get default memory size from cube config (like component_init.py)
104+
def get_memory_size_from_config(cube_config):
105+
return getattr(cube_config.text_mem.config, "memory_size", None) or {
106+
"WorkingMemory": 20,
107+
"LongTermMemory": 1500,
108+
"UserMemory": 480,
109+
}
110+
111+
memory_size = get_memory_size_from_config(default_cube_config)
112+
is_reorganize = getattr(default_cube_config.text_mem.config, "reorganize", False)
113+
114+
# Initialize memory manager with config from APIConfig
115+
memory_manager = MemoryManager(
116+
graph_db,
117+
embedder,
118+
llm,
119+
memory_size=memory_size,
120+
is_reorganize=is_reorganize,
121+
)
122+
text_memory_config = default_cube_config.text_mem.config
123+
text_mem = SimpleTreeTextMemory(
124+
llm=llm,
125+
embedder=embedder,
126+
mem_reader=mem_reader,
127+
graph_db=graph_db,
128+
reranker=reranker,
129+
memory_manager=memory_manager,
130+
config=text_memory_config,
131+
internet_retriever=internet_retriever,
132+
)
133+
134+
naive_mem_cube = NaiveMemCube(
135+
text_mem=text_mem,
136+
pref_mem=None, # Simplified: no preference memory
137+
act_mem=None,
138+
para_mem=None,
139+
)
140+
141+
return {
142+
"llm": llm,
143+
"naive_mem_cube": naive_mem_cube,
144+
"embedder": embedder,
145+
"graph_db": graph_db,
146+
"mem_reader": mem_reader,
147+
}
148+
149+
150+
def factory_initialization() -> tuple[DeepSearchMemAgent, dict[str, Any]]:
151+
# Build necessary components with simplified setup
152+
components = build_minimal_components()
153+
llm = components["llm"]
154+
naive_mem_cube = components["naive_mem_cube"]
155+
156+
# Create configuration Factory with simplified config
157+
agent_config_factory = MemAgentConfigFactory(
158+
backend="deep_search",
159+
config={
160+
"agent_name": "SimplifiedDeepSearchAgent",
161+
"description": "Simplified intelligent agent for deep search",
162+
"max_iterations": 3, # Maximum number of iterations
163+
"timeout": 60, # Timeout in seconds
164+
},
165+
)
166+
167+
# Create Agent using Factory
168+
# Pass text_mem as memory_retriever, it provides search method
169+
deep_search_agent = MemAgentFactory.from_config(
170+
config_factory=agent_config_factory, llm=llm, memory_retriever=naive_mem_cube.text_mem
171+
)
172+
173+
logger.info("✓ DeepSearchMemAgent created successfully")
174+
logger.info(f" - Agent name: {deep_search_agent.config.agent_name}")
175+
logger.info(f" - Max iterations: {deep_search_agent.max_iterations}")
176+
logger.info(f" - Timeout: {deep_search_agent.timeout} seconds")
177+
178+
return deep_search_agent, components
179+
180+
181+
def main():
182+
agent_factory, components_factory = factory_initialization()
183+
results = agent_factory.run(
184+
"Caroline met up with friends, family, and mentors in early July 2023.",
185+
user_id="locomo_exp_user_0_speaker_b_ct-1118",
186+
)
187+
print(results)
188+
189+
190+
if __name__ == "__main__":
191+
main()

examples/mem_scheduler/memos_w_scheduler.py

Lines changed: 105 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1+
import re
12
import shutil
23
import sys
34

5+
from datetime import datetime
46
from pathlib import Path
57
from queue import Queue
6-
from typing import TYPE_CHECKING
78

89
from memos.configs.mem_cube import GeneralMemCubeConfig
910
from memos.configs.mem_os import MOSConfig
@@ -12,12 +13,16 @@
1213
from memos.mem_cube.general import GeneralMemCube
1314
from memos.mem_os.main import MOS
1415
from memos.mem_scheduler.general_scheduler import GeneralScheduler
15-
16-
17-
if TYPE_CHECKING:
18-
from memos.mem_scheduler.schemas.message_schemas import (
19-
ScheduleLogForWebItem,
20-
)
16+
from memos.mem_scheduler.schemas.general_schemas import (
17+
ADD_LABEL,
18+
ANSWER_LABEL,
19+
MEM_ARCHIVE_LABEL,
20+
MEM_ORGANIZE_LABEL,
21+
MEM_UPDATE_LABEL,
22+
QUERY_LABEL,
23+
)
24+
from memos.mem_scheduler.schemas.message_schemas import ScheduleLogForWebItem
25+
from memos.mem_scheduler.utils.filter_utils import transform_name_to_key
2126

2227

2328
FILE_PATH = Path(__file__).absolute()
@@ -70,6 +75,91 @@ def init_task():
7075
return conversations, questions
7176

7277

78+
def _truncate_with_rules(text: str) -> str:
79+
has_cjk = bool(re.search(r"[\u4e00-\u9fff]", text))
80+
limit = 32 if has_cjk else 64
81+
normalized = text.strip().replace("\n", " ")
82+
if len(normalized) <= limit:
83+
return normalized
84+
return normalized[:limit] + "..."
85+
86+
87+
def _format_title(ts: datetime, title_text: str) -> str:
88+
return f"{ts.astimezone().strftime('%H:%M:%S')} {title_text}"
89+
90+
91+
def _cube_display_from(mem_cube_id: str) -> str:
92+
if "public" in (mem_cube_id or "").lower():
93+
return "PublicMemCube"
94+
return "UserMemCube"
95+
96+
97+
_TYPE_SHORT = {
98+
"LongTermMemory": "LTM",
99+
"UserMemory": "User",
100+
"WorkingMemory": "Working",
101+
"ActivationMemory": "Activation",
102+
"ParameterMemory": "Parameter",
103+
"TextMemory": "Text",
104+
"UserInput": "Input",
105+
"NotApplicable": "NA",
106+
}
107+
108+
109+
def _format_entry(item: ScheduleLogForWebItem) -> tuple[str, str]:
110+
cube_display = getattr(item, "memcube_name", None) or _cube_display_from(item.mem_cube_id)
111+
label = item.label
112+
content = item.log_content or ""
113+
memcube_content = getattr(item, "memcube_log_content", None) or []
114+
memory_len = getattr(item, "memory_len", None) or len(memcube_content) or 1
115+
116+
def _first_content() -> str:
117+
if memcube_content:
118+
return memcube_content[0].get("content", "") or content
119+
return content
120+
121+
if label in ("addMessage", QUERY_LABEL, ANSWER_LABEL):
122+
target_cube = cube_display.replace("MemCube", "")
123+
title = _format_title(item.timestamp, f"addMessages to {target_cube} MemCube")
124+
return title, _truncate_with_rules(_first_content())
125+
126+
if label in ("addMemory", ADD_LABEL):
127+
title = _format_title(item.timestamp, f"{cube_display} added {memory_len} memories")
128+
return title, _truncate_with_rules(_first_content())
129+
130+
if label in ("updateMemory", MEM_UPDATE_LABEL):
131+
title = _format_title(item.timestamp, f"{cube_display} updated {memory_len} memories")
132+
return title, _truncate_with_rules(_first_content())
133+
134+
if label in ("archiveMemory", MEM_ARCHIVE_LABEL):
135+
title = _format_title(item.timestamp, f"{cube_display} archived {memory_len} memories")
136+
return title, _truncate_with_rules(_first_content())
137+
138+
if label in ("mergeMemory", MEM_ORGANIZE_LABEL):
139+
title = _format_title(item.timestamp, f"{cube_display} merged {memory_len} memories")
140+
merged = [c for c in memcube_content if c.get("type") == "merged"]
141+
post = [c for c in memcube_content if c.get("type") == "postMerge"]
142+
parts = []
143+
if merged:
144+
parts.append("Merged: " + " | ".join(c.get("content", "") for c in merged))
145+
if post:
146+
parts.append("Result: " + " | ".join(c.get("content", "") for c in post))
147+
detail = " ".join(parts) if parts else _first_content()
148+
return title, _truncate_with_rules(detail)
149+
150+
if label == "scheduleMemory":
151+
title = _format_title(item.timestamp, f"{cube_display} scheduled {memory_len} memories")
152+
if memcube_content:
153+
return title, _truncate_with_rules(memcube_content[0].get("content", ""))
154+
key = transform_name_to_key(content)
155+
from_short = _TYPE_SHORT.get(item.from_memory_type, item.from_memory_type)
156+
to_short = _TYPE_SHORT.get(item.to_memory_type, item.to_memory_type)
157+
return title, _truncate_with_rules(f"[{from_short}{to_short}] {key}: {content}")
158+
159+
title = _format_title(item.timestamp, f"{cube_display} event")
160+
return title, _truncate_with_rules(_first_content())
161+
162+
73163
def show_web_logs(mem_scheduler: GeneralScheduler):
74164
"""Display all web log entries from the scheduler's log queue.
75165
@@ -84,24 +174,25 @@ def show_web_logs(mem_scheduler: GeneralScheduler):
84174

85175
# Create a temporary queue to preserve the original queue contents
86176
temp_queue = Queue()
87-
log_count = 0
177+
collected: list[ScheduleLogForWebItem] = []
88178

89179
while not mem_scheduler._web_log_message_queue.empty():
90180
log_item: ScheduleLogForWebItem = mem_scheduler._web_log_message_queue.get()
181+
collected.append(log_item)
91182
temp_queue.put(log_item)
92-
log_count += 1
93-
94-
# Print log entry details
95-
print(f"\nLog Entry #{log_count}:")
96-
print(f'- "{log_item.label}" log: {log_item}')
97183

184+
for idx, log_item in enumerate(sorted(collected, key=lambda x: x.timestamp, reverse=True), 1):
185+
title, content = _format_entry(log_item)
186+
print(f"\nLog Entry #{idx}:")
187+
print(title)
188+
print(content)
98189
print("-" * 50)
99190

100191
# Restore items back to the original queue
101192
while not temp_queue.empty():
102193
mem_scheduler._web_log_message_queue.put(temp_queue.get())
103194

104-
print(f"\nTotal {log_count} web log entries displayed.")
195+
print(f"\nTotal {len(collected)} web log entries displayed.")
105196
print("=" * 110 + "\n")
106197

107198

src/memos/api/config.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,9 @@ def get_memreader_config() -> dict[str, Any]:
328328
"top_p": 0.95,
329329
"top_k": 20,
330330
"api_key": os.getenv("MEMRADER_API_KEY", "EMPTY"),
331-
"api_base": os.getenv("MEMRADER_API_BASE"),
331+
# Default to OpenAI base URL when env var is not provided to satisfy pydantic
332+
# validation requirements during tests/import.
333+
"api_base": os.getenv("MEMRADER_API_BASE", "https://api.openai.com/v1"),
332334
"remove_think_prefix": True,
333335
"extra_body": {"chat_template_kwargs": {"enable_thinking": False}},
334336
},
@@ -379,7 +381,7 @@ def get_reranker_config() -> dict[str, Any]:
379381
"url": os.getenv("MOS_RERANKER_URL"),
380382
"model": os.getenv("MOS_RERANKER_MODEL", "bge-reranker-v2-m3"),
381383
"timeout": 10,
382-
"headers_extra": os.getenv("MOS_RERANKER_HEADERS_EXTRA"),
384+
"headers_extra": json.loads(os.getenv("MOS_RERANKER_HEADERS_EXTRA", "{}")),
383385
"rerank_source": os.getenv("MOS_RERANK_SOURCE"),
384386
"reranker_strategy": os.getenv("MOS_RERANKER_STRATEGY", "single_turn"),
385387
},
@@ -405,6 +407,7 @@ def get_embedder_config() -> dict[str, Any]:
405407
"provider": os.getenv("MOS_EMBEDDER_PROVIDER", "openai"),
406408
"api_key": os.getenv("MOS_EMBEDDER_API_KEY", "sk-xxxx"),
407409
"model_name_or_path": os.getenv("MOS_EMBEDDER_MODEL", "text-embedding-3-large"),
410+
"headers_extra": json.loads(os.getenv("MOS_EMBEDDER_HEADERS_EXTRA", "{}")),
408411
"base_url": os.getenv("MOS_EMBEDDER_API_BASE", "http://openai.com"),
409412
},
410413
}

0 commit comments

Comments
 (0)