Skip to content

Commit c9b1cba

Browse files
authored
Dev v2.0.15 (#1678)
## Description Please include a summary of the change, the problem it solves, the implementation approach, and relevant context. List any dependencies required for this change. Related Issue (Required): Fixes #issue_number ## Type of change Please delete options that are not relevant. - [ ] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) - [ ] Refactor (does not change functionality, e.g. code style improvements, linting) - [ ] Documentation update ## How Has This Been Tested? Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration - [ ] Unit Test - [ ] Test Script Or Test Steps (please provide) - [ ] Pipeline Automated API Test (please provide) ## Checklist - [ ] I have performed a self-review of my own code | 我已自行检查了自己的代码 - [ ] I have commented my code in hard-to-understand areas | 我已在难以理解的地方对代码进行了注释 - [ ] I have added tests that prove my fix is effective or that my feature works | 我已添加测试以证明我的修复有效或功能正常 - [ ] I have created related documentation issue/PR in [MemOS-Docs](https://github.com/MemTensor/MemOS-Docs) (if applicable) | 我已在 [MemOS-Docs](https://github.com/MemTensor/MemOS-Docs) 中创建了相关的文档 issue/PR(如果适用) - [ ] I have linked the issue to this PR (if applicable) | 我已将 issue 链接到此 PR(如果适用) - [ ] I have mentioned the person who will review this PR | 我已提及将审查此 PR 的人 ## Reviewer Checklist - [ ] closes #xxxx (Replace xxxx with the GitHub issue number) - [ ] Made sure Checks passed - [ ] Tests have been provided
2 parents 4fd2ae9 + 19b4f95 commit c9b1cba

12 files changed

Lines changed: 465 additions & 59 deletions

File tree

evaluation/README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,12 @@ First prepare the dataset `longmemeval_s` from https://huggingface.co/datasets/x
6464
./scripts/run_lme_eval.sh
6565
```
6666

67+
#### Question date and `reference_time`
68+
69+
LongMemEval gives each question a **question date**; evaluation should use that as the reference “now”, not the time when you run the script. The LongMemEval search script passes `question_date` as **`reference_time`** where the backend supports it.
70+
71+
**MemOS Cloud** currently does not support supplying question date on search the same way, so LongMemEval scores there may differ from a spec-faithful run. **Prefer evaluating LongMemEval against the open-source MemOS server** when you need comparable numbers.
72+
6773
### PrefEval Evaluation
6874
Downloading benchmark_dataset/filtered_inter_turns.json from https://github.com/amazon-science/PrefEval/blob/main/benchmark_dataset/filtered_inter_turns.json and save it as `./data/prefeval/filtered_inter_turns.json`.
6975
To evaluate the **Prefeval** dataset — run the following [script](./scripts/run_prefeval_eval.sh):

evaluation/scripts/longmemeval/lme_search.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,11 @@ def mem0_search(client, query, user_id, top_k):
4141
return context, duration_ms
4242

4343

44-
def memos_search(client, query, user_id, top_k):
44+
def memos_search(client, query, user_id, top_k, reference_time=None):
4545
start = time()
46-
results = client.search(query=query, user_id=user_id, top_k=top_k)
46+
results = client.search(
47+
query=query, user_id=user_id, top_k=top_k, reference_time=reference_time
48+
)
4749
context = (
4850
"\n".join([i["memory"] for i in results["text_mem"][0]["memories"]])
4951
+ f"\n{results.get('pref_string', '')}"
@@ -122,12 +124,16 @@ def process_user(lme_df, conv_idx, frame, version, top_k=20):
122124
from utils.client import MemosApiClient
123125

124126
client = MemosApiClient()
125-
context, duration_ms = memos_search(client, question, user_id, top_k)
127+
context, duration_ms = memos_search(
128+
client, question, user_id, top_k, reference_time=question_date
129+
)
126130
elif frame == "memos-api-online":
127131
from utils.client import MemosApiOnlineClient
128132

129133
client = MemosApiOnlineClient()
130-
context, duration_ms = memos_search(client, question, user_id, top_k)
134+
context, duration_ms = memos_search(
135+
client, question, user_id, top_k, reference_time=question_date
136+
)
131137
elif frame == "memu":
132138
from utils.client import MemuClient
133139

src/memos/api/handlers/search_handler.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
from memos.multi_mem_cube.composite_cube import CompositeCubeView
2121
from memos.multi_mem_cube.single_cube import SingleCubeView
2222
from memos.multi_mem_cube.views import MemCubeView
23+
from memos.plugins.hooks import hookable
2324

2425

2526
logger = get_logger(__name__)
@@ -44,6 +45,7 @@ def __init__(self, dependencies: HandlerDependencies):
4445
"naive_mem_cube", "mem_scheduler", "searcher", "deepsearch_agent"
4546
)
4647

48+
@hookable("search")
4749
def handle_search_memories(self, search_req: APISearchRequest) -> SearchResponse:
4850
"""
4951
Main handler for search memories endpoint.

src/memos/api/product_models.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,14 @@ class APISearchRequest(BaseRequest):
438438
)
439439

440440
# ==== Context ====
441+
reference_time: str | None = Field(
442+
None,
443+
description=(
444+
"Optional reference time for time-sensitive search parsing. "
445+
"If omitted, search uses the current server time."
446+
),
447+
)
448+
441449
chat_history: MessageList | None = Field(
442450
None,
443451
description=(
@@ -608,6 +616,17 @@ class APIADDRequest(BaseRequest):
608616
description=("Whether this request represents user feedback. Default: False."),
609617
)
610618

619+
# ==== Upload skill flag ====
620+
is_upload_skill: bool = Field(
621+
False,
622+
description=(
623+
"Whether this request is an upload skill request. "
624+
"When True, the messages field should contain file items "
625+
"with zip file download URLs for pre-built skill packages. "
626+
"Default: False."
627+
),
628+
)
629+
611630
# ==== Backward compatibility fields (will delete later) ====
612631
mem_cube_id: str | None = Field(
613632
None,

src/memos/mem_reader/multi_modal_struct.py

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
from memos.mem_reader.utils import parse_json_result
1818
from memos.memories.textual.item import TextualMemoryItem, TreeNodeTextualMemoryMetadata
1919
from memos.plugins.hook_defs import H
20-
from memos.plugins.hooks import trigger_single_hook
20+
from memos.plugins.hooks import trigger_hook, trigger_single_hook
2121
from memos.templates.mem_reader_prompts import MEMORY_MERGE_PROMPT_EN, MEMORY_MERGE_PROMPT_ZH
2222
from memos.templates.tool_mem_prompts import TOOL_TRAJECTORY_PROMPT_EN, TOOL_TRAJECTORY_PROMPT_ZH
2323
from memos.types import MessagesType
@@ -785,6 +785,14 @@ def _process_one_item(
785785
)
786786
rawfile_node.metadata.summary_ids = [mem_node.id for mem_node in fine_items]
787787
fine_items.append(rawfile_node)
788+
enriched_items = trigger_hook(
789+
H.MEMORY_ITEMS_AFTER_FINE_EXTRACT,
790+
items=fine_items,
791+
user_context=kwargs.get("user_context"),
792+
mem_reader=self,
793+
extract_mode="fine",
794+
)
795+
fine_items = enriched_items if enriched_items is not None else fine_items
788796
return fine_items
789797

790798
fine_memory_items: list[TextualMemoryItem] = []
@@ -993,6 +1001,7 @@ def _process_multi_modal_data(
9931001
return fast_memory_items
9941002

9951003
# Stage: llm_extract — fine mode 4-way parallel LLM + per-source serial
1004+
is_upload_skill = kwargs.pop("is_upload_skill", False)
9961005
non_file_url_fast_items = [
9971006
item for item in fast_memory_items if not self._is_file_url_only_item(item)
9981007
]
@@ -1009,14 +1018,17 @@ def _process_multi_modal_data(
10091018
)
10101019
future_skill = executor.submit(
10111020
process_skill_memory_fine,
1012-
fast_memory_items=non_file_url_fast_items,
1021+
fast_memory_items=fast_memory_items
1022+
if is_upload_skill
1023+
else non_file_url_fast_items,
10131024
info=info,
10141025
searcher=self.searcher,
10151026
graph_db=self.graph_db,
10161027
llm=self.general_llm,
10171028
embedder=self.embedder,
10181029
oss_config=self.oss_config,
10191030
skills_dir_config=self.skills_dir_config,
1031+
is_upload_skill=is_upload_skill,
10201032
**kwargs,
10211033
)
10221034
future_pref = executor.submit(
@@ -1039,6 +1051,10 @@ def _process_multi_modal_data(
10391051
fine_memory_items.extend(fine_memory_items_pref_parser)
10401052

10411053
# Part B: per-source serial processing
1054+
if is_upload_skill:
1055+
# (skip for upload skill to avoid zip being parsed)
1056+
return fine_memory_items
1057+
10421058
with timed_stage("add", "per_source") as ts_ps:
10431059
for fast_item in fast_memory_items:
10441060
sources = fast_item.metadata.sources
@@ -1072,6 +1088,8 @@ def _process_transfer_multi_modal_data(
10721088
logger.warning("[MultiModalStruct] No raw nodes found.")
10731089
return []
10741090

1091+
is_upload_skill = kwargs.pop("is_upload_skill", False)
1092+
10751093
# Extract info from raw_nodes (same as simple_struct.py)
10761094
info = {
10771095
"user_id": raw_nodes[0].metadata.user_id,
@@ -1093,14 +1111,15 @@ def _process_transfer_multi_modal_data(
10931111
)
10941112
future_skill = executor.submit(
10951113
process_skill_memory_fine,
1096-
non_file_url_nodes,
1114+
raw_nodes if is_upload_skill else non_file_url_nodes,
10971115
info,
10981116
searcher=self.searcher,
10991117
llm=self.general_llm,
11001118
embedder=self.embedder,
11011119
graph_db=self.graph_db,
11021120
oss_config=self.oss_config,
11031121
skills_dir_config=self.skills_dir_config,
1122+
is_upload_skill=is_upload_skill,
11041123
**kwargs,
11051124
)
11061125
# Add preference memory extraction
@@ -1125,6 +1144,9 @@ def _process_transfer_multi_modal_data(
11251144
fine_memory_items.extend(fine_memory_items_pref_parser)
11261145

11271146
# Part B: get fine multimodal items
1147+
if is_upload_skill:
1148+
# (skip for upload skill to avoid zip being parsed)
1149+
return fine_memory_items
11281150
for raw_node in raw_nodes:
11291151
sources = raw_node.metadata.sources
11301152
for source in sources:

src/memos/mem_reader/read_skill_memory/process_skill_memory.py

Lines changed: 17 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -252,36 +252,6 @@ def create_task(skill_mem, gen_type, prompt, requirements, context, **kwargs):
252252
return [item[0] for item in raw_skills_data]
253253

254254

255-
def add_id_to_mysql(memory_id: str, mem_cube_id: str):
256-
"""Add id to mysql, will deprecate this function in the future"""
257-
# TODO: tmp function, deprecate soon
258-
import requests
259-
260-
skill_mysql_url = os.getenv("SKILLS_MYSQL_URL", "")
261-
skill_mysql_bearer = os.getenv("SKILLS_MYSQL_BEARER", "")
262-
263-
if not skill_mysql_url or not skill_mysql_bearer:
264-
logger.warning("[PROCESS_SKILLS] SKILLS_MYSQL_URL or SKILLS_MYSQL_BEARER is not set")
265-
return None
266-
headers = {"Authorization": skill_mysql_bearer, "Content-Type": "application/json"}
267-
data = {"memCubeId": mem_cube_id, "skillId": memory_id}
268-
try:
269-
response = requests.post(skill_mysql_url, headers=headers, json=data)
270-
271-
logger.info(f"[PROCESS_SKILLS] response: \n\n{response.json()}")
272-
logger.info(f"[PROCESS_SKILLS] memory_id: \n\n{memory_id}")
273-
logger.info(f"[PROCESS_SKILLS] mem_cube_id: \n\n{mem_cube_id}")
274-
logger.info(f"[PROCESS_SKILLS] skill_mysql_url: \n\n{skill_mysql_url}")
275-
logger.info(f"[PROCESS_SKILLS] skill_mysql_bearer: \n\n{skill_mysql_bearer}")
276-
logger.info(f"[PROCESS_SKILLS] headers: \n\n{headers}")
277-
logger.info(f"[PROCESS_SKILLS] data: \n\n{data}")
278-
279-
return response.json()
280-
except Exception as e:
281-
logger.warning(f"[PROCESS_SKILLS] Error adding id to mysql: {e}")
282-
return None
283-
284-
285255
@require_python_package(
286256
import_name="alibabacloud_oss_v2",
287257
install_command="pip install alibabacloud-oss-v2",
@@ -948,6 +918,7 @@ def create_skill_memory_item(
948918
scripts=skill_memory.get("scripts"),
949919
others=skill_memory.get("others"),
950920
url=skill_memory.get("url", ""),
921+
skill_source=skill_memory.get("skill_source"),
951922
manager_user_id=manager_user_id,
952923
project_id=project_id,
953924
)
@@ -1024,6 +995,21 @@ def process_skill_memory_fine(
1024995
complete_skill_memory: bool = True,
1025996
**kwargs,
1026997
) -> list[TextualMemoryItem]:
998+
is_upload_skill = kwargs.pop("is_upload_skill", False)
999+
if is_upload_skill:
1000+
from memos.mem_reader.read_skill_memory.upload_skill_memory import (
1001+
process_upload_skill_memory,
1002+
)
1003+
1004+
return process_upload_skill_memory(
1005+
fast_memory_items=fast_memory_items,
1006+
info=info,
1007+
embedder=embedder,
1008+
oss_config=oss_config,
1009+
skills_dir_config=skills_dir_config,
1010+
**kwargs,
1011+
)
1012+
10271013
skills_repo_backend = _get_skill_file_storage_location()
10281014
oss_client, _missing_keys, flag = _skill_init(
10291015
skills_repo_backend, oss_config, skills_dir_config
@@ -1252,6 +1238,7 @@ def _full_extract():
12521238
if source:
12531239
skill_sources.append(source)
12541240

1241+
skill_memory["skill_source"] = "auto_create"
12551242
memory_item = create_skill_memory_item(
12561243
skill_memory, info, embedder, sources=skill_sources, **kwargs
12571244
)
@@ -1260,12 +1247,4 @@ def _full_extract():
12601247
logger.warning(f"[PROCESS_SKILLS] Error creating skill memory item: {e}")
12611248
continue
12621249

1263-
# TODO: deprecate this funtion and call
1264-
for skill_memory, skill_memory_item in zip(skill_memories, skill_memory_items, strict=False):
1265-
if skill_memory.get("update", False) and skill_memory.get("old_memory_id", ""):
1266-
continue
1267-
add_id_to_mysql(
1268-
memory_id=skill_memory_item.id,
1269-
mem_cube_id=kwargs.get("user_name", info.get("user_id", "")),
1270-
)
12711250
return skill_memory_items

0 commit comments

Comments
 (0)