Skip to content

Commit 867763b

Browse files
authored
Merge branch 'dev' into feat/redis_scheduler
2 parents b28543f + 105d8a6 commit 867763b

File tree

9 files changed

+281
-19
lines changed

9 files changed

+281
-19
lines changed

src/memos/api/handlers/base_handler.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,11 @@ def mos_server(self):
161161
"""Get MOS server instance."""
162162
return self.deps.mos_server
163163

164+
@property
165+
def deepsearch_agent(self):
166+
"""Get deepsearch agent instance."""
167+
return self.deps.deepsearch_agent
168+
164169
def _validate_dependencies(self, *required_deps: str) -> None:
165170
"""
166171
Validate that required dependencies are available.

src/memos/api/handlers/chat_handler.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -402,9 +402,9 @@ def generate_chat_response() -> Generator[str, None, None]:
402402

403403
# Prepare preference markdown string
404404
if chat_req.include_preference:
405-
pref_md_string = self._build_pref_md_string_for_playground(
406-
search_response.data["pref_mem"][0].get("memories", [])
407-
)
405+
pref_list = search_response.data.get("pref_mem") or []
406+
pref_memories = pref_list[0].get("memories", []) if pref_list else []
407+
pref_md_string = self._build_pref_md_string_for_playground(pref_memories)
408408
yield f"data: {json.dumps({'type': 'pref_md_string', 'data': pref_md_string})}\n\n"
409409

410410
# Step 2: Build system prompt with memories
@@ -425,8 +425,6 @@ def generate_chat_response() -> Generator[str, None, None]:
425425
f"current_system_prompt: {system_prompt}"
426426
)
427427

428-
yield f"data: {json.dumps({'type': 'status', 'data': '2'})}\n\n"
429-
430428
# Step 3: Generate streaming response from LLM
431429
if (
432430
chat_req.model_name_or_path
@@ -448,9 +446,11 @@ def generate_chat_response() -> Generator[str, None, None]:
448446
for chunk in response_stream:
449447
if chunk == "<think>":
450448
in_think = True
449+
yield f"data: {json.dumps({'type': 'status', 'data': 'reasoning'})}\n\n"
451450
continue
452451
if chunk == "</think>":
453452
in_think = False
453+
yield f"data: {json.dumps({'type': 'status', 'data': '2'})}\n\n"
454454
continue
455455

456456
if in_think:
@@ -564,17 +564,17 @@ def _build_pref_md_string_for_playground(self, pref_mem_list: list[any]) -> str:
564564
explicit = []
565565
implicit = []
566566
for pref_mem in pref_mem_list:
567-
if pref_mem["metadata"]["preference_type"] == "explicit":
567+
if pref_mem["metadata"]["preference_type"] == "explicit_preference":
568568
explicit.append(
569569
{
570-
"content": pref_mem["preference"],
570+
"content": pref_mem["metadata"]["preference"],
571571
"reasoning": pref_mem["metadata"]["reasoning"],
572572
}
573573
)
574-
elif pref_mem["metadata"]["preference_type"] == "implicit":
574+
elif pref_mem["metadata"]["preference_type"] == "implicit_preference":
575575
implicit.append(
576576
{
577-
"content": pref_mem["preference"],
577+
"content": pref_mem["metadata"]["preference"],
578578
"reasoning": pref_mem["metadata"]["reasoning"],
579579
}
580580
)

src/memos/api/handlers/component_init.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545

4646
if TYPE_CHECKING:
4747
from memos.memories.textual.tree import TreeTextMemory
48+
from memos.mem_agent.deepsearch_agent import DeepSearchMemAgent
4849
from memos.memories.textual.tree_text_memory.retrieve.internet_retriever_factory import (
4950
InternetRetrieverFactory,
5051
)
@@ -325,6 +326,10 @@ def init_server() -> dict[str, Any]:
325326
online_bot = get_online_bot_function() if dingding_enabled else None
326327
logger.info("DingDing bot is enabled")
327328

329+
deepsearch_agent = DeepSearchMemAgent(
330+
llm=llm,
331+
memory_retriever=tree_mem,
332+
)
328333
# Return all components as a dictionary for easy access and extension
329334
return {
330335
"graph_db": graph_db,
@@ -349,4 +354,5 @@ def init_server() -> dict[str, Any]:
349354
"pref_mem": pref_mem,
350355
"online_bot": online_bot,
351356
"redis_client": redis_client,
357+
"deepsearch_agent": deepsearch_agent,
352358
}

src/memos/api/handlers/search_handler.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,9 @@ def __init__(self, dependencies: HandlerDependencies):
3131
dependencies: HandlerDependencies instance
3232
"""
3333
super().__init__(dependencies)
34-
self._validate_dependencies("naive_mem_cube", "mem_scheduler", "searcher")
34+
self._validate_dependencies(
35+
"naive_mem_cube", "mem_scheduler", "searcher", "deepsearch_agent"
36+
)
3537

3638
def handle_search_memories(self, search_req: APISearchRequest) -> SearchResponse:
3739
"""
@@ -52,9 +54,10 @@ def handle_search_memories(self, search_req: APISearchRequest) -> SearchResponse
5254

5355
results = cube_view.search_memories(search_req)
5456

55-
self.logger.info(f"[AddHandler] Final add results count={len(results)}")
57+
self.logger.info(f"[SearchHandler] Final search results count={len(results)}")
58+
5659
return SearchResponse(
57-
message="Memory searched successfully",
60+
message="Search completed successfully",
5861
data=results,
5962
)
6063

@@ -82,6 +85,7 @@ def _build_cube_view(self, search_req: APISearchRequest) -> MemCubeView:
8285
mem_scheduler=self.mem_scheduler,
8386
logger=self.logger,
8487
searcher=self.searcher,
88+
deepsearch_agent=self.deepsearch_agent,
8589
)
8690
else:
8791
single_views = [
@@ -92,6 +96,7 @@ def _build_cube_view(self, search_req: APISearchRequest) -> MemCubeView:
9296
mem_scheduler=self.mem_scheduler,
9397
logger=self.logger,
9498
searcher=self.searcher,
99+
deepsearch_agent=self.deepsearch_agent,
95100
)
96101
for cube_id in cube_ids
97102
]

src/memos/mem_agent/deepsearch_agent.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
if TYPE_CHECKING:
2727
from memos.types import MessageList
2828

29+
logger = get_logger(__name__)
30+
2931

3032
class JSONResponseParser:
3133
"""Elegant JSON response parser for LLM outputs"""
@@ -48,9 +50,6 @@ def parse(response: str) -> dict[str, Any]:
4850
raise ValueError(f"Cannot parse JSON response: {response[:100]}...")
4951

5052

51-
logger = get_logger(__name__)
52-
53-
5453
class QueryRewriter(BaseMemAgent):
5554
"""Specialized agent for rewriting queries based on conversation history"""
5655

@@ -141,7 +140,7 @@ def __init__(
141140
memory_retriever: Memory retrieval interface (e.g., naive_mem_cube.text_mem)
142141
config: Configuration for deep search behavior
143142
"""
144-
self.config = config or DeepSearchAgentConfig()
143+
self.config = config or DeepSearchAgentConfig(agent_name="DeepSearchMemAgent")
145144
self.max_iterations = self.config.max_iterations
146145
self.timeout = self.config.timeout
147146
self.llm: BaseLLM = llm
@@ -219,7 +218,7 @@ def run(self, query: str, **kwargs) -> str | list[TextualMemoryItem]:
219218
return self._remove_duplicate_memories(accumulated_memories)
220219
else:
221220
return self._generate_final_answer(
222-
query, accumulated_memories, accumulated_context, "", history
221+
query, accumulated_memories, accumulated_context, history
223222
)
224223

225224
def _remove_duplicate_memories(
@@ -248,9 +247,9 @@ def _generate_final_answer(
248247
original_query: str,
249248
search_results: list[TextualMemoryItem],
250249
context: list[str],
251-
missing_info: str = "",
252250
history: list[str] | None = None,
253251
sources: list[str] | None = None,
252+
missing_info: str | None = None,
254253
) -> str:
255254
"""
256255
Generate the final answer.

src/memos/multi_mem_cube/single_cube.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ class SingleCubeView(MemCubeView):
4545
mem_scheduler: Any
4646
logger: Any
4747
searcher: Any
48+
deepsearch_agent: Any
4849

4950
def add_memories(self, add_req: APIADDRequest) -> list[dict[str, Any]]:
5051
"""
@@ -202,6 +203,15 @@ def _deep_search(
202203
formatted_memories = [format_memory_item(data) for data in enhanced_memories]
203204
return formatted_memories
204205

206+
def _deep_search(
207+
self, search_req: APISearchRequest, user_context: UserContext, max_thinking_depth: int
208+
) -> list:
209+
deepsearch_results = self.deepsearch_agent.run(
210+
search_req.query, user_id=user_context.mem_cube_id
211+
)
212+
formatted_memories = [format_memory_item(data) for data in deepsearch_results]
213+
return formatted_memories
214+
205215
def _fine_search(
206216
self,
207217
search_req: APISearchRequest,

src/memos/templates/mem_agent_prompts.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,14 @@
2222
{context}
2323
2424
Analyze the context and determine the next step. Return your response in JSON format with the following structure:
25-
{{
25+
```json
26+
{{
2627
"status": "sufficient|missing_info|needs_raw",
2728
"reasoning": "Brief explanation of your decision",
2829
"missing_entities": ["entity1", "entity2"],
2930
"new_search_query": "new search query",
3031
}}
32+
```
3133
3234
Status definitions:
3335
- "sufficient": Context fully answers the query

tests/api/test_server_router.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ def mock_init_server():
4949
"online_bot": None,
5050
"chat_llms": Mock(),
5151
"redis_client": Mock(),
52+
"deepsearch_agent": Mock(),
5253
}
5354

5455
with patch("memos.api.handlers.init_server", return_value=mock_components):

0 commit comments

Comments
 (0)