mirror of
https://github.com/agentscope-ai/ReMe.git
synced 2026-09-07 08:26:06 +00:00
redefine memory chat interface
This commit is contained in:
parent
5d6e6c0f21
commit
149df5ffa6
6 changed files with 170 additions and 129 deletions
|
|
@ -74,7 +74,6 @@ class ApiMemoryChat(BaseMemoryChat):
|
|||
self._memory_service: BaseMemoryService = self.context.memory_service_dict[self._memory_service]
|
||||
# init service & update kwargs
|
||||
self._memory_service.init_service(human_name=self.human_name, assistant_name=self.assistant_name)
|
||||
self._memory_service.start_backend_service()
|
||||
return self._memory_service
|
||||
|
||||
@property
|
||||
|
|
@ -95,62 +94,68 @@ class ApiMemoryChat(BaseMemoryChat):
|
|||
self._generation_model = self.context.model_dict[self._generation_model]
|
||||
return self._generation_model
|
||||
|
||||
def chat_with_memory(self, query: str, role_name: str = "") -> ModelResponse | ModelResponseGen:
|
||||
"""
|
||||
Engages in a conversation with the AI model, utilizing conversation memory.
|
||||
The function sends the user's query, incorporates conversation history and memory,
|
||||
and optionally remembers the AI's response based on the user's preference.
|
||||
|
||||
Args:
|
||||
query (str): The user's input or query for the AI.
|
||||
role_name (str, optional): The user's name, default value is human_name.
|
||||
|
||||
Returns:
|
||||
- ModelResponse: In non-streaming mode, returns a complete AI response.
|
||||
- ModelResponseGen: In streaming mode, returns a generator yielding AI response parts.
|
||||
|
||||
Side Effects:
|
||||
- Updates the conversation memory with the query of user and (optionally) the response of AI.
|
||||
- Retrieves and includes historical messages and memory content in the context of conversation.
|
||||
"""
|
||||
def get_new_message(self, query: str, role_name: str = "") -> Message:
|
||||
if not role_name:
|
||||
role_name = self.human_name
|
||||
new_message: Message = Message(role=MessageRoleEnum.USER.value, role_name=role_name, content=query)
|
||||
self.add_messages(new_message)
|
||||
|
||||
messages: List[Message] = []
|
||||
return Message(role=MessageRoleEnum.USER.value, role_name=role_name, content=query)
|
||||
|
||||
def get_system_message_with_memory(self, memories: str) -> Message:
|
||||
# Incorporate memory into the system prompt if available
|
||||
system_prompt = self.prompt_handler.system_prompt
|
||||
memories: str = self.memory_service.retrieve_memory()
|
||||
if memories:
|
||||
memory_prompt = self.prompt_handler.memory_prompt
|
||||
system_prompt = "\n".join([x.strip() for x in [system_prompt, memory_prompt, memories]])
|
||||
messages.append(Message(role=MessageRoleEnum.SYSTEM, content=system_prompt))
|
||||
return Message(role=MessageRoleEnum.SYSTEM, content=system_prompt)
|
||||
|
||||
def chat_with_memory(self,
|
||||
query: str,
|
||||
role_name: str = "",
|
||||
remember_response: bool = True):
|
||||
|
||||
chat_messages: List[Message] = []
|
||||
|
||||
new_message: Message = self.get_new_message(query=query, role_name=role_name)
|
||||
|
||||
# To retrieve memory, prepare the query timestamp and role name by adding new_message.
|
||||
memories: str = self.memory_service.retrieve_memory(query=new_message.content,
|
||||
role_name=new_message.role_name,
|
||||
timestamp=new_message.time_created)
|
||||
|
||||
# format system_message with memories
|
||||
system_message: Message = self.get_system_message_with_memory(memories=memories)
|
||||
chat_messages.append(system_message)
|
||||
|
||||
# Include past conversation history in the message list
|
||||
history_messages = self.memory_service.read_message()
|
||||
if history_messages:
|
||||
messages.extend(history_messages)
|
||||
chat_messages.extend(history_messages)
|
||||
|
||||
# Append the current user's message to the conversation context
|
||||
messages.append(new_message)
|
||||
self.logger.info(f"messages={messages}")
|
||||
chat_messages.append(new_message)
|
||||
self.logger.info(f"chat_messages={chat_messages}")
|
||||
|
||||
result = self.generation_model.call(messages=messages, stream=self.stream, **self.generation_model_kwargs)
|
||||
resp = self.generation_model.call(messages=chat_messages, stream=self.stream, **self.generation_model_kwargs)
|
||||
|
||||
if self.stream:
|
||||
assert isinstance(result, ModelResponseGen)
|
||||
assert isinstance(resp, ModelResponseGen)
|
||||
model_response: ModelResponse | None = None
|
||||
for model_response in result:
|
||||
for model_response in resp:
|
||||
yield model_response
|
||||
|
||||
if model_response and model_response.message:
|
||||
self.add_messages(model_response.message)
|
||||
if remember_response:
|
||||
if model_response and model_response.message:
|
||||
model_response.message.role_name = self.assistant_name
|
||||
self.memory_service.add_messages([new_message, model_response.message])
|
||||
else:
|
||||
self.logger.info("model_response or model_response.message is empty!")
|
||||
|
||||
else:
|
||||
assert isinstance(result, ModelResponse)
|
||||
model_response: ModelResponse = result
|
||||
if model_response and model_response.message:
|
||||
self.add_messages(model_response.message)
|
||||
assert isinstance(resp, ModelResponse)
|
||||
model_response: ModelResponse = resp
|
||||
if remember_response:
|
||||
if model_response and model_response.message:
|
||||
model_response.message.role_name = self.assistant_name
|
||||
self.memory_service.add_messages([new_message, model_response.message])
|
||||
else:
|
||||
self.logger.info("model_response or model_response.message is empty!")
|
||||
return model_response
|
||||
|
|
|
|||
|
|
@ -18,19 +18,27 @@ class BaseMemoryChat(metaclass=ABCMeta):
|
|||
self.logger = Logger.get_logger()
|
||||
|
||||
@abstractmethod
|
||||
def chat_with_memory(self, query: str, role_name: str = ""):
|
||||
def get_new_message(self, query: str, role_name: str = "") -> Message:
|
||||
raise NotImplementedError
|
||||
|
||||
@abstractmethod
|
||||
def get_system_message_with_memory(self, memories: str) -> Message:
|
||||
raise NotImplementedError
|
||||
|
||||
@abstractmethod
|
||||
def chat_with_memory(self,
|
||||
query: str,
|
||||
role_name: str = "",
|
||||
remember_response: bool = True):
|
||||
"""
|
||||
Initiates a chat interaction using the memory service, with the provided query as input.
|
||||
|
||||
Args:
|
||||
query (str): The user's query or message to start the chat.
|
||||
role_name (str): The role's name.
|
||||
|
||||
Returns:
|
||||
This method should return the chat response generated after processing the query
|
||||
with the associated memory context. The actual return type and content are defined by the implementing
|
||||
subclass.
|
||||
remember_response (bool): whether update memory service.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
@property
|
||||
def memory_service(self) -> BaseMemoryService:
|
||||
|
|
@ -45,6 +53,9 @@ class BaseMemoryChat(metaclass=ABCMeta):
|
|||
def add_messages(self, messages: List[Message] | Message):
|
||||
self.memory_service.add_messages(messages)
|
||||
|
||||
def start_backend_service(self):
|
||||
self.memory_service.start_backend_service()
|
||||
|
||||
def do_memory_operation(self, op_name: str, **kwargs):
|
||||
return self.memory_service.do_operation(op_name=op_name, **kwargs)
|
||||
|
||||
|
|
|
|||
|
|
@ -100,7 +100,6 @@ class CliMemoryChat(BaseMemoryChat):
|
|||
self._memory_service: BaseMemoryService = self.context.memory_service_dict[self._memory_service]
|
||||
# init service & update kwargs
|
||||
self._memory_service.init_service(human_name=self.human_name, assistant_name=self.assistant_name)
|
||||
self._memory_service.start_backend_service()
|
||||
return self._memory_service
|
||||
|
||||
@property
|
||||
|
|
@ -121,53 +120,71 @@ class CliMemoryChat(BaseMemoryChat):
|
|||
self._generation_model = self.context.model_dict[self._generation_model]
|
||||
return self._generation_model
|
||||
|
||||
def chat_with_memory(self, query: str, role_name: str = "") -> ModelResponse | ModelResponseGen:
|
||||
"""
|
||||
Engages in a conversation with the AI model, utilizing conversation memory.
|
||||
The function sends the user's query, incorporates conversation history and memory,
|
||||
and optionally remembers the AI's response based on the user's preference.
|
||||
|
||||
Args:
|
||||
query (str): The user's input or query for the AI.
|
||||
role_name (str, optional): The user's name, default value is human_name.
|
||||
|
||||
Returns:
|
||||
- ModelResponse: In non-streaming mode, returns a complete AI response.
|
||||
- ModelResponseGen: In streaming mode, returns a generator yielding AI response parts.
|
||||
|
||||
Side Effects:
|
||||
- Updates the conversation memory with the query of user and (optionally) the response of AI.
|
||||
- Retrieves and includes historical messages and memory content in the context of conversation.
|
||||
"""
|
||||
def get_new_message(self, query: str, role_name: str = "") -> Message:
|
||||
if not role_name:
|
||||
role_name = self.human_name
|
||||
new_message: Message = Message(role=MessageRoleEnum.USER.value, role_name=role_name, content=query)
|
||||
self.add_messages(new_message)
|
||||
|
||||
messages: List[Message] = []
|
||||
return Message(role=MessageRoleEnum.USER.value, role_name=role_name, content=query)
|
||||
|
||||
def get_system_message_with_memory(self, memories: str) -> Message:
|
||||
# Incorporate memory into the system prompt if available
|
||||
system_prompt = self.prompt_handler.system_prompt
|
||||
memories: str = self.memory_service.retrieve_memory()
|
||||
if memories:
|
||||
memory_prompt = self.prompt_handler.memory_prompt
|
||||
system_prompt = "\n".join([x.strip() for x in [system_prompt, memory_prompt, memories]])
|
||||
messages.append(Message(role=MessageRoleEnum.SYSTEM, content=system_prompt))
|
||||
return Message(role=MessageRoleEnum.SYSTEM, content=system_prompt)
|
||||
|
||||
def chat_with_memory(self,
|
||||
query: str,
|
||||
role_name: str = "",
|
||||
remember_response: bool = True):
|
||||
|
||||
chat_messages: List[Message] = []
|
||||
|
||||
new_message: Message = self.get_new_message(query=query, role_name=role_name)
|
||||
|
||||
# To retrieve memory, prepare the query timestamp and role name by adding new_message.
|
||||
memories: str = self.memory_service.retrieve_memory(query=new_message.content,
|
||||
role_name=new_message.role_name,
|
||||
timestamp=new_message.time_created)
|
||||
|
||||
# format system_message with memories
|
||||
system_message: Message = self.get_system_message_with_memory(memories=memories)
|
||||
chat_messages.append(system_message)
|
||||
|
||||
# Include past conversation history in the message list
|
||||
history_messages = self.memory_service.read_message()
|
||||
if history_messages:
|
||||
messages.extend(history_messages)
|
||||
chat_messages.extend(history_messages)
|
||||
|
||||
# Append the current user's message to the conversation context
|
||||
messages.append(new_message)
|
||||
self.logger.info(f"messages={messages}")
|
||||
chat_messages.append(new_message)
|
||||
self.logger.info(f"chat_messages={chat_messages}")
|
||||
|
||||
# Invoke the Language Model with the constructed message context, respecting streaming setting
|
||||
return self.generation_model.call(messages=messages,
|
||||
resp = self.generation_model.call(messages=chat_messages,
|
||||
stream=self.stream,
|
||||
**self.generation_model_kwargs)
|
||||
|
||||
if self.stream:
|
||||
assert isinstance(resp, ModelResponseGen)
|
||||
model_response: ModelResponse | None = None
|
||||
for model_response in resp:
|
||||
questionary.print(model_response.delta, end="")
|
||||
questionary.print("")
|
||||
|
||||
if remember_response and model_response and model_response.message:
|
||||
model_response.message.role_name = self.assistant_name
|
||||
self.memory_service.add_messages([new_message, model_response.message])
|
||||
|
||||
else:
|
||||
assert isinstance(resp, ModelResponse)
|
||||
model_response: ModelResponse = resp
|
||||
questionary.print(model_response.message.content)
|
||||
|
||||
if remember_response and model_response and model_response.message:
|
||||
model_response.message.role_name = self.assistant_name
|
||||
self.memory_service.add_messages([new_message, model_response.message])
|
||||
|
||||
@staticmethod
|
||||
def parse_query_command(query: str):
|
||||
"""
|
||||
|
|
@ -296,19 +313,8 @@ class CliMemoryChat(BaseMemoryChat):
|
|||
questionary.print(f"{self.assistant_name}: ", end="", style="bold")
|
||||
|
||||
# Fetch and display AI's response
|
||||
self.memory_service.start_backend_service()
|
||||
if self.stream:
|
||||
model_response = None
|
||||
for model_response in self.chat_with_memory(query=query):
|
||||
questionary.print(model_response.delta, end="")
|
||||
questionary.print("")
|
||||
else:
|
||||
model_response = self.chat_with_memory(query=query)
|
||||
questionary.print(model_response.message.content)
|
||||
|
||||
# Append AI's response to the conversation memory
|
||||
model_response.message.role_name = self.assistant_name
|
||||
self.add_messages(model_response.message)
|
||||
self.start_backend_service()
|
||||
self.chat_with_memory(query=query)
|
||||
|
||||
except KeyboardInterrupt:
|
||||
# Handle user interruption and confirm exit
|
||||
|
|
|
|||
|
|
@ -31,25 +31,6 @@ class BaseMemoryService(metaclass=ABCMeta):
|
|||
self._op_description_dict: Dict[str, str] = {}
|
||||
self.logger = Logger.get_logger()
|
||||
|
||||
@abstractmethod
|
||||
def add_messages(self, messages: List[Message] | Message):
|
||||
raise NotImplementedError
|
||||
|
||||
@abstractmethod
|
||||
def do_operation(self, op_name: str, **kwargs):
|
||||
"""
|
||||
Abstract method defining the interface for executing a specific operation by its name.
|
||||
This method must be implemented by subclasses to provide the actual operation logic.
|
||||
|
||||
Args:
|
||||
op_name (str): The name identifying the operation to be performed.
|
||||
**kwargs: Additional keyword arguments required for the operation execution.
|
||||
|
||||
Raises:
|
||||
NotImplementedError: This exception is raised when the method is not overridden in a subclass.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
@property
|
||||
def op_description_dict(self) -> Dict[str, str]:
|
||||
"""
|
||||
|
|
@ -63,27 +44,9 @@ class BaseMemoryService(metaclass=ABCMeta):
|
|||
self._op_description_dict = {k: v.description for k, v in self._operation_dict.items()}
|
||||
return self._op_description_dict
|
||||
|
||||
def retrieve_memory(self):
|
||||
"""
|
||||
Executes the operation associated with retrieved memory.
|
||||
Asserts that the operation for retrieved memory has been initialized.
|
||||
|
||||
Returns:
|
||||
Any: The result of the retrieved memory operation.
|
||||
"""
|
||||
assert self.retrieve_memory_key in self._operation_dict, f"op={self.retrieve_memory_key} is not inited!"
|
||||
return self.do_operation(self.retrieve_memory_key)
|
||||
|
||||
def read_message(self):
|
||||
"""
|
||||
Executes the operation associated with reading messages.
|
||||
Asserts that the operation for reading messages has been initialized.
|
||||
|
||||
Returns:
|
||||
Any: The result of the read message operation.
|
||||
"""
|
||||
assert self.read_message_key in self._operation_dict, f"op={self.read_message_key} is not inited!"
|
||||
return self.do_operation(self.read_message_key)
|
||||
@abstractmethod
|
||||
def add_messages(self, messages: List[Message] | Message):
|
||||
raise NotImplementedError
|
||||
|
||||
@abstractmethod
|
||||
def init_service(self, **kwargs):
|
||||
|
|
@ -94,3 +57,27 @@ class BaseMemoryService(metaclass=ABCMeta):
|
|||
|
||||
def stop_backend_service(self):
|
||||
pass
|
||||
|
||||
def do_operation(self, op_name: str, **kwargs):
|
||||
"""
|
||||
Executes a specific operation by its name with provided keyword arguments.
|
||||
|
||||
Args:
|
||||
op_name (str): The name of the operation to execute.
|
||||
**kwargs: Keyword arguments for the operation's execution.
|
||||
|
||||
Returns:
|
||||
The result of the operation execution, if any. Otherwise, None.
|
||||
|
||||
Raises:
|
||||
Warning: If the operation name is not initialized in `_operation_dict`.
|
||||
"""
|
||||
if op_name not in self._operation_dict:
|
||||
self.logger.warning(f"op_name={op_name} is not inited!")
|
||||
return
|
||||
return self._operation_dict[op_name].run_operation(**kwargs)
|
||||
|
||||
def __getattr__(self, name: str):
|
||||
return lambda **kwargs: self.do_operation(name, **kwargs)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -22,22 +22,39 @@ class SetQueryWorker(MemoryBaseWorker):
|
|||
along with its creation timestamp.
|
||||
"""
|
||||
query = "" # Default query value
|
||||
query_timestamp = int(datetime.datetime.now().timestamp()) # Current timestamp as default
|
||||
timestamp = int(datetime.datetime.now().timestamp()) # Current timestamp as default
|
||||
|
||||
if "query" in self.chat_kwargs:
|
||||
# Check if a specific 'query' has been provided via chat kwargs
|
||||
# set query if exists
|
||||
query = self.chat_kwargs["query"]
|
||||
if not query:
|
||||
query = ""
|
||||
query = query.strip()
|
||||
|
||||
# set ts if exists
|
||||
_timestamp = self.chat_kwargs.get("timestamp")
|
||||
if _timestamp and isinstance(_timestamp, int):
|
||||
timestamp = _timestamp
|
||||
|
||||
# check role_name
|
||||
role_name = self.chat_kwargs.get("role_name")
|
||||
if role_name:
|
||||
assert role_name == self.target_name, \
|
||||
f"role_name={role_name} is not supported in human/assistant memory workflow!"
|
||||
|
||||
elif self.chat_messages:
|
||||
# If no explicit query is given, use the content of the latest chat message
|
||||
chat_messages = [msg for msg in self.chat_messages if msg.role == MessageRoleEnum.USER.value]
|
||||
if chat_messages:
|
||||
message = chat_messages[-1]
|
||||
query = message.content
|
||||
query_timestamp = message.time_created
|
||||
timestamp = message.time_created
|
||||
|
||||
# check role_name
|
||||
role_name = message.role_name
|
||||
if role_name:
|
||||
assert role_name == self.target_name, \
|
||||
f"role_name={role_name} is not supported in human/assistant memory workflow!"
|
||||
|
||||
# Store the determined query and its timestamp in the context
|
||||
self.set_context(QUERY_WITH_TS, (query, query_timestamp))
|
||||
self.set_context(QUERY_WITH_TS, (query, timestamp))
|
||||
|
|
|
|||
15
tests/other/test_attr.py
Normal file
15
tests/other/test_attr.py
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
class MyClass:
|
||||
def __init__(self):
|
||||
self.existing_attribute = "I exist"
|
||||
|
||||
def do(self, name: str, **kwargs):
|
||||
print("do %s %s" % (name, kwargs))
|
||||
|
||||
def __getattr__(self, name):
|
||||
return lambda **kwargs: self.do(name, **kwargs)
|
||||
|
||||
|
||||
# 创建类的实例
|
||||
obj = MyClass()
|
||||
|
||||
obj.haha(a=1, b=2)
|
||||
Loading…
Add table
Reference in a new issue