better:优化工作记忆
This commit is contained in:
@@ -18,30 +18,28 @@ class WorkingMemoryInfo(InfoBase):
|
|||||||
self.data["talking_message"] = message
|
self.data["talking_message"] = message
|
||||||
|
|
||||||
def set_working_memory(self, working_memory: List[str]) -> None:
|
def set_working_memory(self, working_memory: List[str]) -> None:
|
||||||
"""设置工作记忆
|
"""设置工作记忆列表
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
working_memory (str): 工作记忆内容
|
working_memory (List[str]): 工作记忆内容列表
|
||||||
"""
|
"""
|
||||||
self.data["working_memory"] = working_memory
|
self.data["working_memory"] = working_memory
|
||||||
|
|
||||||
def add_working_memory(self, working_memory: str) -> None:
|
def add_working_memory(self, working_memory: str) -> None:
|
||||||
"""添加工作记忆
|
"""添加一条工作记忆
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
working_memory (str): 工作记忆内容
|
working_memory (str): 工作记忆内容,格式为"记忆要点:xxx"
|
||||||
"""
|
"""
|
||||||
working_memory_list = self.data.get("working_memory", [])
|
working_memory_list = self.data.get("working_memory", [])
|
||||||
# print(f"working_memory_list: {working_memory_list}")
|
|
||||||
working_memory_list.append(working_memory)
|
working_memory_list.append(working_memory)
|
||||||
# print(f"working_memory_list: {working_memory_list}")
|
|
||||||
self.data["working_memory"] = working_memory_list
|
self.data["working_memory"] = working_memory_list
|
||||||
|
|
||||||
def get_working_memory(self) -> List[str]:
|
def get_working_memory(self) -> List[str]:
|
||||||
"""获取工作记忆
|
"""获取所有工作记忆
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
List[str]: 工作记忆内容
|
List[str]: 工作记忆内容列表,每条记忆格式为"记忆要点:xxx"
|
||||||
"""
|
"""
|
||||||
return self.data.get("working_memory", [])
|
return self.data.get("working_memory", [])
|
||||||
|
|
||||||
@@ -53,33 +51,32 @@ class WorkingMemoryInfo(InfoBase):
|
|||||||
"""
|
"""
|
||||||
return self.type
|
return self.type
|
||||||
|
|
||||||
def get_data(self) -> Dict[str, str]:
|
def get_data(self) -> Dict[str, List[str]]:
|
||||||
"""获取所有信息数据
|
"""获取所有信息数据
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Dict[str, str]: 包含所有信息数据的字典
|
Dict[str, List[str]]: 包含所有信息数据的字典
|
||||||
"""
|
"""
|
||||||
return self.data
|
return self.data
|
||||||
|
|
||||||
def get_info(self, key: str) -> Optional[str]:
|
def get_info(self, key: str) -> Optional[List[str]]:
|
||||||
"""获取特定属性的信息
|
"""获取特定属性的信息
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
key: 要获取的属性键名
|
key: 要获取的属性键名
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Optional[str]: 属性值,如果键不存在则返回 None
|
Optional[List[str]]: 属性值,如果键不存在则返回 None
|
||||||
"""
|
"""
|
||||||
return self.data.get(key)
|
return self.data.get(key)
|
||||||
|
|
||||||
def get_processed_info(self) -> Dict[str, str]:
|
def get_processed_info(self) -> str:
|
||||||
"""获取处理后的信息
|
"""获取处理后的信息
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Dict[str, str]: 处理后的信息数据
|
str: 处理后的信息数据,所有记忆要点按行拼接
|
||||||
"""
|
"""
|
||||||
all_memory = self.get_working_memory()
|
all_memory = self.get_working_memory()
|
||||||
# print(f"all_memory: {all_memory}")
|
|
||||||
memory_str = ""
|
memory_str = ""
|
||||||
for memory in all_memory:
|
for memory in all_memory:
|
||||||
memory_str += f"{memory}\n"
|
memory_str += f"{memory}\n"
|
||||||
|
|||||||
@@ -45,7 +45,6 @@ def init_prompt():
|
|||||||
"selected_memory_ids": ["id1", "id2", ...],
|
"selected_memory_ids": ["id1", "id2", ...],
|
||||||
"new_memory": "true" or "false",
|
"new_memory": "true" or "false",
|
||||||
"merge_memory": [["id1", "id2"], ["id3", "id4"],...]
|
"merge_memory": [["id1", "id2"], ["id3", "id4"],...]
|
||||||
|
|
||||||
}}
|
}}
|
||||||
```
|
```
|
||||||
"""
|
"""
|
||||||
@@ -104,11 +103,10 @@ class WorkingMemoryProcessor(BaseProcessor):
|
|||||||
all_memory = working_memory.get_all_memories()
|
all_memory = working_memory.get_all_memories()
|
||||||
memory_prompts = []
|
memory_prompts = []
|
||||||
for memory in all_memory:
|
for memory in all_memory:
|
||||||
# memory_content = memory.data
|
|
||||||
memory_summary = memory.summary
|
memory_summary = memory.summary
|
||||||
memory_id = memory.id
|
memory_id = memory.id
|
||||||
memory_brief = memory_summary.get("brief")
|
memory_brief = memory_summary.get("brief")
|
||||||
memory_keypoints = memory_summary.get("key_points", [])
|
memory_points = memory_summary.get("points", [])
|
||||||
memory_single_prompt = f"记忆id:{memory_id},记忆摘要:{memory_brief}\n"
|
memory_single_prompt = f"记忆id:{memory_id},记忆摘要:{memory_brief}\n"
|
||||||
memory_prompts.append(memory_single_prompt)
|
memory_prompts.append(memory_single_prompt)
|
||||||
|
|
||||||
@@ -122,11 +120,11 @@ class WorkingMemoryProcessor(BaseProcessor):
|
|||||||
memory_str=memory_choose_str,
|
memory_str=memory_choose_str,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
print(f"prompt: {prompt}")
|
||||||
|
|
||||||
# 调用LLM处理记忆
|
# 调用LLM处理记忆
|
||||||
content = ""
|
content = ""
|
||||||
try:
|
try:
|
||||||
# logger.debug(f"{self.log_prefix} 处理工作记忆的prompt: {prompt}")
|
|
||||||
|
|
||||||
content, _ = await self.llm_model.generate_response_async(prompt=prompt)
|
content, _ = await self.llm_model.generate_response_async(prompt=prompt)
|
||||||
if not content:
|
if not content:
|
||||||
logger.warning(f"{self.log_prefix} LLM返回空结果,处理工作记忆失败。")
|
logger.warning(f"{self.log_prefix} LLM返回空结果,处理工作记忆失败。")
|
||||||
@@ -159,13 +157,12 @@ class WorkingMemoryProcessor(BaseProcessor):
|
|||||||
for memory_id in selected_memory_ids:
|
for memory_id in selected_memory_ids:
|
||||||
memory = await working_memory.retrieve_memory(memory_id)
|
memory = await working_memory.retrieve_memory(memory_id)
|
||||||
if memory:
|
if memory:
|
||||||
# memory_content = memory.data
|
|
||||||
memory_summary = memory.summary
|
memory_summary = memory.summary
|
||||||
memory_id = memory.id
|
memory_id = memory.id
|
||||||
memory_brief = memory_summary.get("brief")
|
memory_brief = memory_summary.get("brief")
|
||||||
memory_keypoints = memory_summary.get("key_points", [])
|
memory_points = memory_summary.get("points", [])
|
||||||
for keypoint in memory_keypoints:
|
for point in memory_points:
|
||||||
memory_str += f"记忆要点:{keypoint}\n"
|
memory_str += f"记忆要点:{point}\n"
|
||||||
|
|
||||||
working_memory_info = WorkingMemoryInfo()
|
working_memory_info = WorkingMemoryInfo()
|
||||||
if memory_str:
|
if memory_str:
|
||||||
@@ -216,9 +213,7 @@ class WorkingMemoryProcessor(BaseProcessor):
|
|||||||
merged_memory = await working_memory.merge_memory(memory_id1, memory_id2)
|
merged_memory = await working_memory.merge_memory(memory_id1, memory_id2)
|
||||||
logger.debug(f"{self.log_prefix} 异步合并记忆成功: {memory_id1} 和 {memory_id2}...")
|
logger.debug(f"{self.log_prefix} 异步合并记忆成功: {memory_id1} 和 {memory_id2}...")
|
||||||
logger.debug(f"{self.log_prefix} 合并后的记忆梗概: {merged_memory.summary.get('brief')}")
|
logger.debug(f"{self.log_prefix} 合并后的记忆梗概: {merged_memory.summary.get('brief')}")
|
||||||
logger.debug(f"{self.log_prefix} 合并后的记忆详情: {merged_memory.summary.get('detailed')}")
|
logger.debug(f"{self.log_prefix} 合并后的记忆要点: {merged_memory.summary.get('points')}")
|
||||||
logger.debug(f"{self.log_prefix} 合并后的记忆要点: {merged_memory.summary.get('key_points')}")
|
|
||||||
logger.debug(f"{self.log_prefix} 合并后的记忆事件: {merged_memory.summary.get('events')}")
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"{self.log_prefix} 异步合并记忆失败: {e}")
|
logger.error(f"{self.log_prefix} 异步合并记忆失败: {e}")
|
||||||
|
|||||||
@@ -7,14 +7,14 @@ import string
|
|||||||
class MemoryItem:
|
class MemoryItem:
|
||||||
"""记忆项类,用于存储单个记忆的所有相关信息"""
|
"""记忆项类,用于存储单个记忆的所有相关信息"""
|
||||||
|
|
||||||
def __init__(self, data: Any, from_source: str = "", tags: Optional[List[str]] = None):
|
def __init__(self, data: Any, from_source: str = "", brief: str = ""):
|
||||||
"""
|
"""
|
||||||
初始化记忆项
|
初始化记忆项
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
data: 记忆数据
|
data: 记忆数据
|
||||||
from_source: 数据来源
|
from_source: 数据来源
|
||||||
tags: 数据标签列表
|
brief: 记忆内容主题
|
||||||
"""
|
"""
|
||||||
# 生成可读ID:时间戳_随机字符串
|
# 生成可读ID:时间戳_随机字符串
|
||||||
timestamp = int(time.time())
|
timestamp = int(time.time())
|
||||||
@@ -23,11 +23,10 @@ class MemoryItem:
|
|||||||
self.data = data
|
self.data = data
|
||||||
self.data_type = type(data)
|
self.data_type = type(data)
|
||||||
self.from_source = from_source
|
self.from_source = from_source
|
||||||
self.tags = set(tags) if tags else set()
|
self.brief = brief
|
||||||
self.timestamp = time.time()
|
self.timestamp = time.time()
|
||||||
# 修改summary的结构说明,用于存储可能的总结信息
|
# 修改summary的结构说明,用于存储可能的总结信息
|
||||||
# summary结构:{
|
# summary结构:{
|
||||||
# "brief": "记忆内容主题",
|
|
||||||
# "detailed": "记忆内容概括",
|
# "detailed": "记忆内容概括",
|
||||||
# "keypoints": ["关键概念1", "关键概念2"],
|
# "keypoints": ["关键概念1", "关键概念2"],
|
||||||
# "events": ["事件1", "事件2"]
|
# "events": ["事件1", "事件2"]
|
||||||
@@ -47,23 +46,6 @@ class MemoryItem:
|
|||||||
# 格式: [(操作类型, 时间戳, 当时精简次数, 当时强度), ...]
|
# 格式: [(操作类型, 时间戳, 当时精简次数, 当时强度), ...]
|
||||||
self.history = [("create", self.timestamp, self.compress_count, self.memory_strength)]
|
self.history = [("create", self.timestamp, self.compress_count, self.memory_strength)]
|
||||||
|
|
||||||
def add_tag(self, tag: str) -> None:
|
|
||||||
"""添加标签"""
|
|
||||||
self.tags.add(tag)
|
|
||||||
|
|
||||||
def remove_tag(self, tag: str) -> None:
|
|
||||||
"""移除标签"""
|
|
||||||
if tag in self.tags:
|
|
||||||
self.tags.remove(tag)
|
|
||||||
|
|
||||||
def has_tag(self, tag: str) -> bool:
|
|
||||||
"""检查是否有特定标签"""
|
|
||||||
return tag in self.tags
|
|
||||||
|
|
||||||
def has_all_tags(self, tags: List[str]) -> bool:
|
|
||||||
"""检查是否有所有指定的标签"""
|
|
||||||
return all(tag in self.tags for tag in tags)
|
|
||||||
|
|
||||||
def matches_source(self, source: str) -> bool:
|
def matches_source(self, source: str) -> bool:
|
||||||
"""检查来源是否匹配"""
|
"""检查来源是否匹配"""
|
||||||
return self.from_source == source
|
return self.from_source == source
|
||||||
@@ -103,9 +85,9 @@ class MemoryItem:
|
|||||||
current_time = time.time()
|
current_time = time.time()
|
||||||
self.history.append((operation_type, current_time, self.compress_count, self.memory_strength))
|
self.history.append((operation_type, current_time, self.compress_count, self.memory_strength))
|
||||||
|
|
||||||
def to_tuple(self) -> Tuple[Any, str, Set[str], float, str]:
|
def to_tuple(self) -> Tuple[Any, str, float, str]:
|
||||||
"""转换为元组格式(为了兼容性)"""
|
"""转换为元组格式(为了兼容性)"""
|
||||||
return (self.data, self.from_source, self.tags, self.timestamp, self.id)
|
return (self.data, self.from_source, self.timestamp, self.id)
|
||||||
|
|
||||||
def is_memory_valid(self) -> bool:
|
def is_memory_valid(self) -> bool:
|
||||||
"""检查记忆是否有效(强度是否大于等于1)"""
|
"""检查记忆是否有效(强度是否大于等于1)"""
|
||||||
|
|||||||
@@ -71,14 +71,13 @@ class MemoryManager:
|
|||||||
|
|
||||||
return memory_item.id
|
return memory_item.id
|
||||||
|
|
||||||
async def push_with_summary(self, data: T, from_source: str = "", tags: Optional[List[str]] = None) -> MemoryItem:
|
async def push_with_summary(self, data: T, from_source: str = "") -> MemoryItem:
|
||||||
"""
|
"""
|
||||||
推送一段有类型的信息到工作记忆中,并自动生成总结
|
推送一段有类型的信息到工作记忆中,并自动生成总结
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
data: 要存储的数据
|
data: 要存储的数据
|
||||||
from_source: 数据来源
|
from_source: 数据来源
|
||||||
tags: 数据标签列表
|
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
包含原始数据和总结信息的字典
|
包含原始数据和总结信息的字典
|
||||||
@@ -88,11 +87,8 @@ class MemoryManager:
|
|||||||
# 先生成总结
|
# 先生成总结
|
||||||
summary = await self.summarize_memory_item(data)
|
summary = await self.summarize_memory_item(data)
|
||||||
|
|
||||||
# 准备标签
|
|
||||||
memory_tags = list(tags) if tags else []
|
|
||||||
|
|
||||||
# 创建记忆项
|
# 创建记忆项
|
||||||
memory_item = MemoryItem(data, from_source, memory_tags)
|
memory_item = MemoryItem(data, from_source, brief=summary.get("brief", ""))
|
||||||
|
|
||||||
# 将总结信息保存到记忆项中
|
# 将总结信息保存到记忆项中
|
||||||
memory_item.set_summary(summary)
|
memory_item.set_summary(summary)
|
||||||
@@ -103,7 +99,7 @@ class MemoryManager:
|
|||||||
return memory_item
|
return memory_item
|
||||||
else:
|
else:
|
||||||
# 非字符串类型,直接创建并推送记忆项
|
# 非字符串类型,直接创建并推送记忆项
|
||||||
memory_item = MemoryItem(data, from_source, tags)
|
memory_item = MemoryItem(data, from_source)
|
||||||
self.push_item(memory_item)
|
self.push_item(memory_item)
|
||||||
|
|
||||||
return memory_item
|
return memory_item
|
||||||
@@ -136,7 +132,6 @@ class MemoryManager:
|
|||||||
self,
|
self,
|
||||||
data_type: Optional[Type] = None,
|
data_type: Optional[Type] = None,
|
||||||
source: Optional[str] = None,
|
source: Optional[str] = None,
|
||||||
tags: Optional[List[str]] = None,
|
|
||||||
start_time: Optional[float] = None,
|
start_time: Optional[float] = None,
|
||||||
end_time: Optional[float] = None,
|
end_time: Optional[float] = None,
|
||||||
memory_id: Optional[str] = None,
|
memory_id: Optional[str] = None,
|
||||||
@@ -150,7 +145,6 @@ class MemoryManager:
|
|||||||
Args:
|
Args:
|
||||||
data_type: 要查找的数据类型
|
data_type: 要查找的数据类型
|
||||||
source: 数据来源
|
source: 数据来源
|
||||||
tags: 必须包含的标签列表
|
|
||||||
start_time: 开始时间戳
|
start_time: 开始时间戳
|
||||||
end_time: 结束时间戳
|
end_time: 结束时间戳
|
||||||
memory_id: 特定记忆项ID
|
memory_id: 特定记忆项ID
|
||||||
@@ -191,10 +185,6 @@ class MemoryManager:
|
|||||||
if source is not None and not item.matches_source(source):
|
if source is not None and not item.matches_source(source):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# 检查标签是否匹配
|
|
||||||
if tags is not None and not item.has_all_tags(tags):
|
|
||||||
continue
|
|
||||||
|
|
||||||
# 检查时间范围
|
# 检查时间范围
|
||||||
if start_time is not None and item.timestamp < start_time:
|
if start_time is not None and item.timestamp < start_time:
|
||||||
continue
|
continue
|
||||||
@@ -491,9 +481,6 @@ class MemoryManager:
|
|||||||
if not memory_item1 or not memory_item2:
|
if not memory_item1 or not memory_item2:
|
||||||
raise ValueError("无法找到指定的记忆项")
|
raise ValueError("无法找到指定的记忆项")
|
||||||
|
|
||||||
content1 = memory_item1.data
|
|
||||||
content2 = memory_item2.data
|
|
||||||
|
|
||||||
# 获取记忆的摘要信息(如果有)
|
# 获取记忆的摘要信息(如果有)
|
||||||
summary1 = memory_item1.summary
|
summary1 = memory_item1.summary
|
||||||
summary2 = memory_item2.summary
|
summary2 = memory_item2.summary
|
||||||
@@ -515,31 +502,22 @@ class MemoryManager:
|
|||||||
prompt += f"记忆2主题:{summary2['brief']}\n"
|
prompt += f"记忆2主题:{summary2['brief']}\n"
|
||||||
prompt += "记忆2关键要点:\n" + "\n".join([f"- {point}" for point in summary2.get("points", [])]) + "\n\n"
|
prompt += "记忆2关键要点:\n" + "\n".join([f"- {point}" for point in summary2.get("points", [])]) + "\n\n"
|
||||||
|
|
||||||
# 添加记忆原始内容
|
prompt += """
|
||||||
prompt += f"""
|
|
||||||
记忆1原始内容:
|
|
||||||
{content1}
|
|
||||||
|
|
||||||
记忆2原始内容:
|
|
||||||
{content2}
|
|
||||||
|
|
||||||
请按以下JSON格式输出合并结果:
|
请按以下JSON格式输出合并结果:
|
||||||
```json
|
```json
|
||||||
{{
|
{
|
||||||
"content": "合并后的记忆内容文本(尽可能保留原信息,但去除重复)",
|
|
||||||
"brief": "合并后的主题(20字以内)",
|
"brief": "合并后的主题(20字以内)",
|
||||||
"points": [
|
"points": [
|
||||||
"合并后的要点",
|
"合并后的要点",
|
||||||
"合并后的要点"
|
"合并后的要点"
|
||||||
]
|
]
|
||||||
}}
|
}
|
||||||
```
|
```
|
||||||
请确保输出是有效的JSON格式,不要添加任何额外的说明或解释。
|
请确保输出是有效的JSON格式,不要添加任何额外的说明或解释。
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# 默认合并结果
|
# 默认合并结果
|
||||||
default_merged = {
|
default_merged = {
|
||||||
"content": f"{content1}\n\n{content2}",
|
|
||||||
"brief": f"合并:{summary1['brief']} + {summary2['brief']}",
|
"brief": f"合并:{summary1['brief']} + {summary2['brief']}",
|
||||||
"points": [],
|
"points": [],
|
||||||
}
|
}
|
||||||
@@ -579,10 +557,6 @@ class MemoryManager:
|
|||||||
logger.error(f"修复后的JSON不是字典类型: {type(merged_data)}")
|
logger.error(f"修复后的JSON不是字典类型: {type(merged_data)}")
|
||||||
merged_data = default_merged
|
merged_data = default_merged
|
||||||
|
|
||||||
# 确保所有必要字段都存在且类型正确
|
|
||||||
if "content" not in merged_data or not isinstance(merged_data["content"], str):
|
|
||||||
merged_data["content"] = default_merged["content"]
|
|
||||||
|
|
||||||
if "brief" not in merged_data or not isinstance(merged_data["brief"], str):
|
if "brief" not in merged_data or not isinstance(merged_data["brief"], str):
|
||||||
merged_data["brief"] = default_merged["brief"]
|
merged_data["brief"] = default_merged["brief"]
|
||||||
|
|
||||||
@@ -605,9 +579,6 @@ class MemoryManager:
|
|||||||
merged_data = default_merged
|
merged_data = default_merged
|
||||||
|
|
||||||
# 创建新的记忆项
|
# 创建新的记忆项
|
||||||
# 合并记忆项的标签
|
|
||||||
merged_tags = memory_item1.tags.union(memory_item2.tags)
|
|
||||||
|
|
||||||
# 取两个记忆项中更强的来源
|
# 取两个记忆项中更强的来源
|
||||||
merged_source = (
|
merged_source = (
|
||||||
memory_item1.from_source
|
memory_item1.from_source
|
||||||
@@ -615,8 +586,8 @@ class MemoryManager:
|
|||||||
else memory_item2.from_source
|
else memory_item2.from_source
|
||||||
)
|
)
|
||||||
|
|
||||||
# 创建新的记忆项
|
# 创建新的记忆项,使用空字符串作为data
|
||||||
merged_memory = MemoryItem(data=merged_data["content"], from_source=merged_source, tags=list(merged_tags))
|
merged_memory = MemoryItem(data="", from_source=merged_source, brief=merged_data["brief"])
|
||||||
|
|
||||||
# 设置合并后的摘要
|
# 设置合并后的摘要
|
||||||
summary = {
|
summary = {
|
||||||
|
|||||||
@@ -51,19 +51,18 @@ class WorkingMemory:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"自动衰减记忆时出错: {str(e)}")
|
print(f"自动衰减记忆时出错: {str(e)}")
|
||||||
|
|
||||||
async def add_memory(self, content: Any, from_source: str = "", tags: Optional[List[str]] = None):
|
async def add_memory(self, content: Any, from_source: str = ""):
|
||||||
"""
|
"""
|
||||||
添加一段记忆到指定聊天
|
添加一段记忆到指定聊天
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
content: 记忆内容
|
content: 记忆内容
|
||||||
from_source: 数据来源
|
from_source: 数据来源
|
||||||
tags: 数据标签列表
|
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
包含记忆信息的字典
|
包含记忆信息的字典
|
||||||
"""
|
"""
|
||||||
memory = await self.memory_manager.push_with_summary(content, from_source, tags)
|
memory = await self.memory_manager.push_with_summary(content, from_source)
|
||||||
if len(self.memory_manager.get_all_items()) > self.max_memories_per_chat:
|
if len(self.memory_manager.get_all_items()) > self.max_memories_per_chat:
|
||||||
self.remove_earliest_memory()
|
self.remove_earliest_memory()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user