Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
65d5649
update reader and search strategy
Oct 28, 2025
6cad866
set strategy reader and search config
Oct 29, 2025
f040110
fix all reader conflicts
Oct 29, 2025
c389367
fix install problem
Oct 29, 2025
499502d
fix
Oct 29, 2025
e1bb223
fix test
Oct 29, 2025
72b7466
Merge branch 'dev' into dev_test
CaralHsi Oct 29, 2025
74585e8
Merge branch 'dev' into dev_test
fridayL Oct 30, 2025
790e99f
turn off graph recall
Oct 30, 2025
15b63a7
Merge branch 'dev' into dev_test
Oct 30, 2025
390ba29
turn off graph recall
Oct 30, 2025
9615282
turn off graph recall
Oct 30, 2025
2fb8ce0
Merge branch 'dev' into dev_test
fridayL Oct 30, 2025
6035522
Merge branch 'dev' into dev_test
Oct 30, 2025
04f412b
fix Searcher input bug
Oct 30, 2025
9716274
fix Searcher
Oct 30, 2025
c455a4e
Merge branch 'dev_test' of github.com:whipser030/MemOS into dev_test
Oct 30, 2025
f8b9b4a
fix Search
Oct 30, 2025
c840ad4
Merge branch 'dev' into dev_test
Oct 30, 2025
b9dbecd
fix bug
Nov 4, 2025
1798f60
Merge branch 'dev' of github.com:MemTensor/MemOS into dev
Nov 4, 2025
6db95e7
Merge branch 'dev' into dev_test
Nov 4, 2025
1173c07
adjust strategy reader
Nov 4, 2025
7ab465b
Merge branch 'dev' into dev_test
Nov 4, 2025
744d227
adjust strategy reader
Nov 4, 2025
a9a98fa
adjust search config input
Nov 4, 2025
900f5e6
reformat code
Nov 4, 2025
ac7aff5
Merge branch 'dev' into dev_test
CaralHsi Nov 4, 2025
144c446
re pr
Nov 5, 2025
a2b55c7
Merge branch 'dev' of github.com:MemTensor/MemOS into dev
Nov 5, 2025
441c52b
Merge branch 'dev' into dev_test
Nov 5, 2025
6f272db
Merge branch 'dev_test' of github.com:whipser030/MemOS into dev_test
Nov 5, 2025
f506d3e
format repair
Nov 5, 2025
db9041c
Merge branch 'dev' of github.com:MemTensor/MemOS into dev
Nov 5, 2025
d921284
Merge branch 'dev' into dev_test
CaralHsi Nov 5, 2025
d036c53
Merge branch 'dev' of github.com:MemTensor/MemOS into dev
Nov 11, 2025
5a3f0db
Merge branch 'dev' into dev_test
Nov 11, 2025
dc67413
fix time issue
Nov 11, 2025
7699b9a
Merge branch 'dev_test' of github.com:whipser030/MemOS into dev_test
Nov 11, 2025
8bfbf94
develop feedback process
Nov 19, 2025
875c551
Merge branch 'dev' of github.com:MemTensor/MemOS into dev
Nov 19, 2025
7f20f8b
Resolve merge conflicts
Nov 19, 2025
4d712eb
feedback handler configuration
Nov 20, 2025
36b93eb
Merge branch 'dev' of github.com:MemTensor/MemOS into dev
Nov 25, 2025
adec73e
merged
Nov 25, 2025
aef3aad
upgrade feedback using
Nov 26, 2025
81ec520
Merge branch 'dev' of github.com:MemTensor/MemOS into dev
Nov 26, 2025
55c9d89
fix
Nov 26, 2025
b4fbfde
Merge branch 'dev' of github.com:MemTensor/MemOS into dev
Nov 27, 2025
ee64719
Merge branch 'dev' into dev_test
Nov 27, 2025
0fa9be7
add threshold
Nov 27, 2025
4a4746e
Merge branch 'dev' of github.com:MemTensor/MemOS into dev
Nov 27, 2025
16de8da
Merge branch 'dev' into dev_test
Nov 27, 2025
facb7b3
update prompt
Nov 27, 2025
eab5fe6
update prompt
Nov 27, 2025
7577aac
fix handler
Nov 27, 2025
cc4069d
add feedback scheduler
Nov 29, 2025
2529db2
add handler change node update
Dec 1, 2025
898ccac
add handler change node update
Dec 1, 2025
faec340
Merge branch 'dev' of github.com:MemTensor/MemOS into dev
Dec 1, 2025
913c24d
add handler change node update
Dec 1, 2025
91d063d
add handler change node update
Dec 1, 2025
2a47880
add handler change node update
Dec 1, 2025
c5618c6
Merge branch 'dev' into dev_test
whipser030 Dec 2, 2025
b9737f1
Merge branch 'dev' into dev_test
CaralHsi Dec 2, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 27 additions & 1 deletion examples/api/product_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,23 @@ def chat_stream(query: str, session_id: str, history: list | None = None):
print(payload)


def feedback_memory(feedback_content: str, history: list | None = None):
url = f"{BASE_URL}/feedback"
data = {
"user_id": USER_ID,
"writable_cube_ids": [MEM_CUBE_ID],
"history": history,
"feedback_content": feedback_content,
"async_mode": "sync",
"corrected_answer": "false",
}

print("[*] Feedbacking memory ...")
resp = requests.post(url, headers=HEADERS, data=json.dumps(data), timeout=30)
print(resp.status_code, resp.text)
return resp.json()


if __name__ == "__main__":
print("===== STEP 1: Register User =====")
register_user()
Expand All @@ -140,5 +157,14 @@ def chat_stream(query: str, session_id: str, history: list | None = None):
],
)

print("\n===== STEP 4: Stream Chat =====")
print("\n===== STEP 5: Stream Chat =====")
chat_stream("我刚和你说什么了呢", SESSION_ID2, history=[])

print("\n===== STEP 6: Feedback Memory =====")
feedback_memory(
feedback_content="错啦,我今天没有吃拉面",
history=[
{"role": "user", "content": "我刚和你说什么了呢"},
{"role": "assistant", "content": "你今天吃了好吃的拉面"},
],
)
41 changes: 39 additions & 2 deletions src/memos/api/handlers/add_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"""

from memos.api.handlers.base_handler import BaseHandler, HandlerDependencies
from memos.api.product_models import APIADDRequest, MemoryResponse
from memos.api.product_models import APIADDRequest, APIFeedbackRequest, MemoryResponse
from memos.memories.textual.item import (
list_all_fields,
)
Expand All @@ -30,7 +30,9 @@ def __init__(self, dependencies: HandlerDependencies):
dependencies: HandlerDependencies instance
"""
super().__init__(dependencies)
self._validate_dependencies("naive_mem_cube", "mem_reader", "mem_scheduler")
self._validate_dependencies(
"naive_mem_cube", "mem_reader", "mem_scheduler", "feedback_server"
)

def handle_add_memories(self, add_req: APIADDRequest) -> MemoryResponse:
"""
Expand All @@ -56,6 +58,39 @@ def handle_add_memories(self, add_req: APIADDRequest) -> MemoryResponse:

cube_view = self._build_cube_view(add_req)

if add_req.is_feedback:
chat_history = add_req.chat_history
messages = add_req.messages
if chat_history is None:
chat_history = []
if messages is None:
messages = []
concatenate_chat = chat_history + messages

last_user_index = max(i for i, d in enumerate(concatenate_chat) if d["role"] == "user")
feedback_content = concatenate_chat[last_user_index]["content"]
feedback_history = concatenate_chat[:last_user_index]

feedback_req = APIFeedbackRequest(
user_id=add_req.user_id,
session_id=add_req.session_id,
task_id=add_req.task_id,
history=feedback_history,
feedback_content=feedback_content,
writable_cube_ids=add_req.writable_cube_ids,
async_mode=add_req.async_mode,
)
process_record = cube_view.feedback_memories(feedback_req)

self.logger.info(
f"[FeedbackHandler] Final feedback results count={len(process_record)}"
)

return MemoryResponse(
message="Memory feedback successfully",
data=[process_record],
)

results = cube_view.add_memories(add_req)

self.logger.info(f"[AddHandler] Final add results count={len(results)}")
Expand Down Expand Up @@ -88,6 +123,7 @@ def _build_cube_view(self, add_req: APIADDRequest) -> MemCubeView:
mem_reader=self.mem_reader,
mem_scheduler=self.mem_scheduler,
logger=self.logger,
feedback_server=self.feedback_server,
searcher=None,
)
else:
Expand All @@ -98,6 +134,7 @@ def _build_cube_view(self, add_req: APIADDRequest) -> MemCubeView:
mem_reader=self.mem_reader,
mem_scheduler=self.mem_scheduler,
logger=self.logger,
feedback_server=self.feedback_server,
searcher=None,
)
for cube_id in cube_ids
Expand Down
7 changes: 7 additions & 0 deletions src/memos/api/handlers/base_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ def __init__(
internet_retriever: Any | None = None,
memory_manager: Any | None = None,
mos_server: Any | None = None,
feedback_server: Any | None = None,
**kwargs,
):
"""
Expand Down Expand Up @@ -68,6 +69,7 @@ def __init__(
self.internet_retriever = internet_retriever
self.memory_manager = memory_manager
self.mos_server = mos_server
self.feedback_server = feedback_server

# Store any additional dependencies
for key, value in kwargs.items():
Expand Down Expand Up @@ -166,6 +168,11 @@ def deepsearch_agent(self):
"""Get deepsearch agent instance."""
return self.deps.deepsearch_agent

@property
def feedback_server(self):
"""Get feedback server instance."""
return self.deps.feedback_server

def _validate_dependencies(self, *required_deps: str) -> None:
"""
Validate that required dependencies are available.
Expand Down
16 changes: 15 additions & 1 deletion src/memos/api/handlers/component_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
from memos.llms.factory import LLMFactory
from memos.log import get_logger
from memos.mem_cube.navie import NaiveMemCube
from memos.mem_feedback.simple_feedback import SimpleMemFeedback
from memos.mem_os.product_server import MOSServer
from memos.mem_reader.factory import MemReaderFactory
from memos.mem_scheduler.orm_modules.base_model import BaseDBManager
Expand Down Expand Up @@ -295,6 +296,16 @@ def init_server() -> dict[str, Any]:
)
logger.debug("Searcher created")

# Initialize feedback server
feedback_server = SimpleMemFeedback(
llm=llm,
embedder=embedder,
graph_store=graph_db,
memory_manager=memory_manager,
mem_reader=mem_reader,
searcher=searcher,
)

# Initialize Scheduler
scheduler_config_dict = APIConfig.get_scheduler_config()
scheduler_config = SchedulerConfigFactory(
Expand All @@ -308,7 +319,9 @@ def init_server() -> dict[str, Any]:
mem_reader=mem_reader,
redis_client=redis_client,
)
mem_scheduler.init_mem_cube(mem_cube=naive_mem_cube, searcher=searcher)
mem_scheduler.init_mem_cube(
mem_cube=naive_mem_cube, searcher=searcher, feedback_server=feedback_server
)
logger.debug("Scheduler initialized")

# Initialize SchedulerAPIModule
Expand Down Expand Up @@ -356,6 +369,7 @@ def init_server() -> dict[str, Any]:
"text_mem": text_mem,
"pref_mem": pref_mem,
"online_bot": online_bot,
"feedback_server": feedback_server,
"redis_client": redis_client,
"deepsearch_agent": deepsearch_agent,
}
93 changes: 93 additions & 0 deletions src/memos/api/handlers/feedback_handler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
"""
Feeback handler for memory add/update functionality.
"""

from memos.api.handlers.base_handler import BaseHandler, HandlerDependencies
from memos.api.product_models import APIFeedbackRequest, MemoryResponse
from memos.log import get_logger
from memos.multi_mem_cube.composite_cube import CompositeCubeView
from memos.multi_mem_cube.single_cube import SingleCubeView
from memos.multi_mem_cube.views import MemCubeView


logger = get_logger(__name__)


class FeedbackHandler(BaseHandler):
"""
Handler for memory feedback operations.

Provides fast, fine-grained, and mixture-based feedback modes.
"""

def __init__(self, dependencies: HandlerDependencies):
"""
Initialize feedback handler.

Args:
dependencies: HandlerDependencies instance
"""
super().__init__(dependencies)
self._validate_dependencies("mem_reader", "mem_scheduler", "searcher")

def handle_feedback_memories(self, feedback_req: APIFeedbackRequest) -> MemoryResponse:
"""
Main handler for feedback memories endpoint.

Args:
feedback_req: feedback request containing content and parameters

Returns:
MemoryResponse with formatted results
"""
cube_view = self._build_cube_view(feedback_req)

process_record = cube_view.feedback_memories(feedback_req)

self.logger.info(f"[FeedbackHandler] Final feedback results count={len(process_record)}")

return MemoryResponse(
message="Memory feedback successfully",
data=[process_record],
)

def _resolve_cube_ids(self, feedback_req: APIFeedbackRequest) -> list[str]:
"""
Normalize target cube ids from feedback_req.
"""
if feedback_req.writable_cube_ids:
return list(dict.fromkeys(feedback_req.writable_cube_ids))

return [feedback_req.user_id]

def _build_cube_view(self, feedback_req: APIFeedbackRequest) -> MemCubeView:
cube_ids = self._resolve_cube_ids(feedback_req)

if len(cube_ids) == 1:
cube_id = cube_ids[0]
return SingleCubeView(
cube_id=cube_id,
naive_mem_cube=None,
mem_reader=None,
mem_scheduler=self.mem_scheduler,
logger=self.logger,
searcher=None,
feedback_server=self.feedback_server,
)
else:
single_views = [
SingleCubeView(
cube_id=cube_id,
naive_mem_cube=None,
mem_reader=None,
mem_scheduler=self.mem_scheduler,
logger=self.logger,
searcher=None,
feedback_server=self.feedback_server,
)
for cube_id in cube_ids
]
return CompositeCubeView(
cube_views=single_views,
logger=self.logger,
)
34 changes: 33 additions & 1 deletion src/memos/api/product_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

# Import message types from core types module
from memos.log import get_logger
from memos.types import PermissionDict, SearchMode
from memos.types import MessageDict, MessageList, MessagesType, PermissionDict, SearchMode


logger = get_logger(__name__)
Expand Down Expand Up @@ -628,6 +628,38 @@ def _convert_deprecated_fields(self) -> "APIADDRequest":
return self


class APIFeedbackRequest(BaseRequest):
"""Request model for processing feedback info."""

user_id: str = Field(..., description="User ID")
session_id: str | None = Field(
"default_session", description="Session ID for soft-filtering memories"
)
task_id: str | None = Field(None, description="Task ID for monitering async tasks")
history: list[MessageDict] | None = Field(..., description="Chat history")
retrieved_memory_ids: list[str] | None = Field(
None, description="Retrieved memory ids at last turn"
)
feedback_content: str | None = Field(..., description="Feedback content to process")
feedback_time: str | None = Field(None, description="Feedback time")
# ==== Multi-cube writing ====
writable_cube_ids: list[str] | None = Field(
None, description="List of cube IDs user can write for multi-cube add"
)
async_mode: Literal["sync", "async"] = Field(
"async", description="feedback mode: sync or async"
)
corrected_answer: bool = Field(False, description="Whether need return corrected answer")
# ==== Backward compatibility ====
mem_cube_id: str | None = Field(
None,
description=(
"(Deprecated) Single cube ID to search in. "
"Prefer `readable_cube_ids` for multi-cube search."
),
)


class APIChatCompleteRequest(BaseRequest):
"""Request model for chat operations."""

Expand Down
19 changes: 18 additions & 1 deletion src/memos/api/routers/server_router.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,12 @@
from memos.api.handlers.add_handler import AddHandler
from memos.api.handlers.base_handler import HandlerDependencies
from memos.api.handlers.chat_handler import ChatHandler
from memos.api.handlers.feedback_handler import FeedbackHandler
from memos.api.handlers.search_handler import SearchHandler
from memos.api.product_models import (
APIADDRequest,
APIChatCompleteRequest,
APIFeedbackRequest,
APISearchRequest,
ChatRequest,
DeleteMemoryRequest,
Expand Down Expand Up @@ -66,7 +68,7 @@
add_handler,
online_bot=components.get("online_bot"),
)

feedback_handler = FeedbackHandler(dependencies)
# Extract commonly used components for function-based handlers
# (These can be accessed from the components dict without unpacking all of them)
mem_scheduler: BaseScheduler = components["mem_scheduler"]
Expand Down Expand Up @@ -265,3 +267,18 @@ def delete_memories(memory_req: DeleteMemoryRequest):
return handlers.memory_handler.handle_delete_memories(
delete_mem_req=memory_req, naive_mem_cube=naive_mem_cube
)


# =============================================================================
# Feedback API Endpoints
# =============================================================================


@router.post("/feedback", summary="Feedback memories", response_model=MemoryResponse)
def feedback_memories(feedback_req: APIFeedbackRequest):
"""
Feedback memories for a specific user.

This endpoint uses the class-based FeedbackHandler for better code organization.
"""
return feedback_handler.handle_feedback_memories(feedback_req)
Loading
Loading