mirror of
https://github.com/agentscope-ai/ReMe.git
synced 2026-09-29 01:41:38 +00:00
docs(profiles): add comprehensive docstrings for profile backend and handler methods
- Added documentation for get_all_sync, get_by_sync, delete_sync, delete_all_sync methods - Documented add_sync and add_batch_sync functionality with deduping behavior - Added docstrings for update_sync and search_sync operations - Updated ProfileHandler.format_node method with proper documentation - Refactored private _format_node to public format_node method - Added comprehensive documentation for profile vector handler operations - Documented _vector_profile_matches, _get_by_profile_id, _get_by_profile_key helper methods - Added docstrings for retrieve_profile functionality and formatting methods
This commit is contained in:
parent
03259729c1
commit
26017e4044
4 changed files with 62 additions and 28 deletions
|
|
@ -42,11 +42,13 @@ class FileProfileBackend(BaseProfileBackend):
|
|||
logger.info(f"Saved {len(nodes)} profiles to {self.cache_key}")
|
||||
|
||||
def get_all_sync(self) -> list[MemoryNode]:
|
||||
"""Load all profile nodes from cache, ordered by ``message_time``."""
|
||||
nodes = self._load_nodes()
|
||||
nodes.sort(key=lambda n: n.message_time)
|
||||
return nodes
|
||||
|
||||
def get_by_sync(self, *, profile_id: str | None = None, profile_key: str | None = None) -> MemoryNode | None:
|
||||
"""Return the first node matching ``profile_id`` or ``profile_key``."""
|
||||
if not profile_id and not profile_key:
|
||||
raise ValueError("Must provide either profile_id or profile_key")
|
||||
|
||||
|
|
@ -58,6 +60,7 @@ class FileProfileBackend(BaseProfileBackend):
|
|||
return None
|
||||
|
||||
def delete_sync(self, profile_id: str | list[str]) -> bool | int:
|
||||
"""Remove one id, many ids, or none; returns bool, count, or 0/false if nothing removed."""
|
||||
nodes = self._load_nodes()
|
||||
original_count = len(nodes)
|
||||
|
||||
|
|
@ -83,6 +86,7 @@ class FileProfileBackend(BaseProfileBackend):
|
|||
return True
|
||||
|
||||
def delete_all_sync(self) -> int:
|
||||
"""Clear every cached profile for this target; returns how many were stored."""
|
||||
nodes = self._load_nodes()
|
||||
count = len(nodes)
|
||||
self._save_nodes([], apply_limits=False)
|
||||
|
|
@ -90,6 +94,7 @@ class FileProfileBackend(BaseProfileBackend):
|
|||
return count
|
||||
|
||||
def add_sync(self, message_time: str, profile_key: str, profile_value: str, ref_memory_id: str = "") -> MemoryNode:
|
||||
"""Append a profile row, replacing any existing row with the same key."""
|
||||
nodes = self._load_nodes()
|
||||
|
||||
new_node = MemoryNode(
|
||||
|
|
@ -112,6 +117,7 @@ class FileProfileBackend(BaseProfileBackend):
|
|||
return new_node
|
||||
|
||||
def add_batch_sync(self, profiles: list[dict], ref_memory_id: str = "") -> list[MemoryNode]:
|
||||
"""Insert many profiles in one write, deduping by key against existing rows."""
|
||||
if not profiles:
|
||||
return []
|
||||
|
||||
|
|
@ -146,6 +152,7 @@ class FileProfileBackend(BaseProfileBackend):
|
|||
profile_key: str,
|
||||
profile_value: str,
|
||||
) -> MemoryNode | None:
|
||||
"""Update fields for ``profile_id``; return ``None`` if that id is missing."""
|
||||
nodes = self._load_nodes()
|
||||
target_node = None
|
||||
for node in nodes:
|
||||
|
|
@ -165,6 +172,7 @@ class FileProfileBackend(BaseProfileBackend):
|
|||
return target_node
|
||||
|
||||
def search_sync(self, query: str | list[str], limit: int = 5) -> list[MemoryNode]:
|
||||
"""Simple substring/token match over key and content, best matches first."""
|
||||
queries = [query] if isinstance(query, str) else query
|
||||
query_terms = [q.strip().lower() for q in queries if q and q.strip()]
|
||||
if not query_terms:
|
||||
|
|
|
|||
|
|
@ -115,7 +115,8 @@ class ProfileHandler:
|
|||
return await self.backend.search(query=query, limit=limit)
|
||||
|
||||
@staticmethod
|
||||
def _format_node(node: MemoryNode, add_profile_id: bool = False, add_history_id: bool = False) -> str:
|
||||
def format_node(node: MemoryNode, add_profile_id: bool = False, add_history_id: bool = False) -> str:
|
||||
"""Render a profile ``MemoryNode`` as a single-line string for tools/logs."""
|
||||
parts = []
|
||||
profile_key = str(node.metadata.get("profile_key", node.when_to_use))
|
||||
|
||||
|
|
@ -134,7 +135,7 @@ class ProfileHandler:
|
|||
|
||||
async def aread_all(self, add_profile_id: bool = False, add_history_id: bool = False) -> str:
|
||||
nodes = await self.aget_all()
|
||||
formatted_profiles = [self._format_node(node, add_profile_id, add_history_id) for node in nodes]
|
||||
formatted_profiles = [self.format_node(node, add_profile_id, add_history_id) for node in nodes]
|
||||
logger.info(f"Read {len(formatted_profiles)} profiles from {self.cache_key}")
|
||||
return "\n".join(formatted_profiles).strip()
|
||||
|
||||
|
|
@ -146,7 +147,7 @@ class ProfileHandler:
|
|||
add_history_id: bool = False,
|
||||
) -> tuple[list[MemoryNode], str]:
|
||||
nodes = await self.asearch(query=query, limit=limit)
|
||||
formatted_profiles = [self._format_node(node, add_profile_id, add_history_id) for node in nodes]
|
||||
formatted_profiles = [self.format_node(node, add_profile_id, add_history_id) for node in nodes]
|
||||
return nodes, "\n".join(formatted_profiles).strip()
|
||||
|
||||
def delete(self, profile_id: str | list[str]) -> bool | int:
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ class ProfileVectorHandler:
|
|||
return hash_obj.hexdigest()[:16]
|
||||
|
||||
def _base_filters(self) -> dict:
|
||||
"""Filters shared by all profile rows in the vector collection."""
|
||||
return {
|
||||
"memory_type": MemoryType.IDENTITY.value,
|
||||
"memory_target": self.memory_target,
|
||||
|
|
@ -46,6 +47,7 @@ class ProfileVectorHandler:
|
|||
}
|
||||
|
||||
def _build_profile_node(self, profile: dict, ref_memory_id: str = "") -> MemoryNode:
|
||||
"""Turn a profile dict into a ``MemoryNode`` for upsert into the vector store."""
|
||||
profile_key = profile.get("profile_key", "").strip()
|
||||
profile_value = profile.get("profile_value", "").strip()
|
||||
message_time = profile.get("message_time", "")
|
||||
|
|
@ -69,7 +71,41 @@ class ProfileVectorHandler:
|
|||
metadata=metadata,
|
||||
)
|
||||
|
||||
def _vector_profile_matches(self, memory_node: MemoryNode) -> bool:
|
||||
"""True if ``memory_node`` belongs to this handler's target and profile kind."""
|
||||
if memory_node.memory_target != self.memory_target:
|
||||
return False
|
||||
if memory_node.memory_type is not MemoryType.IDENTITY:
|
||||
return False
|
||||
if memory_node.metadata.get("profile_kind") != self.PROFILE_KIND:
|
||||
return False
|
||||
return True
|
||||
|
||||
async def _get_by_profile_id(self, profile_id: str) -> MemoryNode | None:
|
||||
"""Load by vector id and validate filters."""
|
||||
try:
|
||||
vector_node = await self.vector_store.get(profile_id)
|
||||
except KeyError:
|
||||
logger.warning(f"Profile {profile_id} not found in vector store")
|
||||
return None
|
||||
if vector_node is None:
|
||||
logger.warning(f"Profile {profile_id} not found in vector store")
|
||||
return None
|
||||
memory_node = MemoryNode.from_vector_node(vector_node)
|
||||
if not self._vector_profile_matches(memory_node):
|
||||
return None
|
||||
return memory_node
|
||||
|
||||
async def _get_by_profile_key(self, profile_key: str) -> MemoryNode | None:
|
||||
"""Load the single row matching ``profile_key`` under base filters."""
|
||||
filters = {**self._base_filters(), "profile_key": profile_key}
|
||||
vector_nodes = await self.vector_store.list(filters=filters, limit=1)
|
||||
if not vector_nodes:
|
||||
return None
|
||||
return MemoryNode.from_vector_node(vector_nodes[0])
|
||||
|
||||
async def get_all(self) -> list[MemoryNode]:
|
||||
"""List every profile row for this memory target, sorted by store."""
|
||||
vector_nodes = await self.vector_store.list(
|
||||
filters=self._base_filters(),
|
||||
sort_key="message_time",
|
||||
|
|
@ -78,34 +114,15 @@ class ProfileVectorHandler:
|
|||
return [MemoryNode.from_vector_node(node) for node in vector_nodes]
|
||||
|
||||
async def get_by(self, *, profile_id: str | None = None, profile_key: str | None = None) -> MemoryNode | None:
|
||||
"""Return one profile by stable id or by logical profile key."""
|
||||
if not profile_id and not profile_key:
|
||||
raise ValueError("Must provide either profile_id or profile_key")
|
||||
|
||||
if profile_id:
|
||||
try:
|
||||
vector_node = await self.vector_store.get(profile_id)
|
||||
except KeyError:
|
||||
logger.warning(f"Profile {profile_id} not found in vector store")
|
||||
return None
|
||||
if vector_node is None:
|
||||
logger.warning(f"Profile {profile_id} not found in vector store")
|
||||
return None
|
||||
memory_node = MemoryNode.from_vector_node(vector_node)
|
||||
if memory_node.memory_target != self.memory_target:
|
||||
return None
|
||||
if memory_node.memory_type is not MemoryType.IDENTITY:
|
||||
return None
|
||||
if memory_node.metadata.get("profile_kind") != self.PROFILE_KIND:
|
||||
return None
|
||||
return memory_node
|
||||
|
||||
profile_key = profile_key or ""
|
||||
vector_nodes = await self.vector_store.list(filters={**self._base_filters(), "profile_key": profile_key}, limit=1)
|
||||
if not vector_nodes:
|
||||
return None
|
||||
return MemoryNode.from_vector_node(vector_nodes[0])
|
||||
return await self._get_by_profile_id(profile_id)
|
||||
return await self._get_by_profile_key(profile_key or "")
|
||||
|
||||
async def delete(self, profile_id: str | list[str]) -> bool | int:
|
||||
"""Delete one id, many ids, or report zero/false when nothing matched."""
|
||||
if isinstance(profile_id, list):
|
||||
profile_ids = list(dict.fromkeys(pid for pid in profile_id if pid))
|
||||
if not profile_ids:
|
||||
|
|
@ -127,6 +144,7 @@ class ProfileVectorHandler:
|
|||
return True
|
||||
|
||||
async def delete_all(self) -> int:
|
||||
"""Remove all profile vectors for this target; returns how many were deleted."""
|
||||
nodes = await self.get_all()
|
||||
if not nodes:
|
||||
return 0
|
||||
|
|
@ -134,6 +152,7 @@ class ProfileVectorHandler:
|
|||
return len(nodes)
|
||||
|
||||
async def add_batch(self, profiles: list[dict], ref_memory_id: str = "") -> list[MemoryNode]:
|
||||
"""Upsert many profiles at once (last dict wins per key), then enforce capacity."""
|
||||
if not profiles:
|
||||
return []
|
||||
|
||||
|
|
@ -144,7 +163,9 @@ class ProfileVectorHandler:
|
|||
continue
|
||||
deduped_profiles[profile_key] = profile
|
||||
|
||||
new_nodes = [self._build_profile_node(profile, ref_memory_id=ref_memory_id) for profile in deduped_profiles.values()]
|
||||
new_nodes = [
|
||||
self._build_profile_node(profile, ref_memory_id=ref_memory_id) for profile in deduped_profiles.values()
|
||||
]
|
||||
if not new_nodes:
|
||||
return []
|
||||
|
||||
|
|
@ -154,6 +175,7 @@ class ProfileVectorHandler:
|
|||
return new_nodes
|
||||
|
||||
async def add(self, message_time: str, profile_key: str, profile_value: str, ref_memory_id: str = "") -> MemoryNode:
|
||||
"""Insert or replace a single profile row."""
|
||||
nodes = await self.add_batch(
|
||||
[
|
||||
{
|
||||
|
|
@ -173,6 +195,7 @@ class ProfileVectorHandler:
|
|||
profile_key: str,
|
||||
profile_value: str,
|
||||
) -> MemoryNode | None:
|
||||
"""Replace content and key for ``profile_id``; return ``None`` if missing."""
|
||||
existing_node = await self.get_by(profile_id=profile_id)
|
||||
if existing_node is None:
|
||||
return None
|
||||
|
|
@ -197,6 +220,7 @@ class ProfileVectorHandler:
|
|||
return new_node
|
||||
|
||||
async def search(self, query: str | list[str], limit: int = 5) -> list[MemoryNode]:
|
||||
"""Semantic search with de-duplication across multiple query strings."""
|
||||
queries = [query] if isinstance(query, str) else query
|
||||
seen_nodes: dict[str, MemoryNode] = {}
|
||||
for item in queries:
|
||||
|
|
@ -211,6 +235,7 @@ class ProfileVectorHandler:
|
|||
return nodes[:limit]
|
||||
|
||||
async def enforce_capacity(self):
|
||||
"""Drop oldest rows when count exceeds ``max_capacity``."""
|
||||
nodes = await self.get_all()
|
||||
overflow = len(nodes) - self.max_capacity
|
||||
if overflow <= 0:
|
||||
|
|
|
|||
|
|
@ -95,7 +95,7 @@ class RetrieveProfile(BaseMemoryTool):
|
|||
output = "No new profiles found."
|
||||
else:
|
||||
output = "\n".join(
|
||||
[ProfileHandler._format_node(node, add_profile_id=True, add_history_id=True) for node in new_nodes],
|
||||
[ProfileHandler.format_node(node, add_profile_id=True, add_history_id=True) for node in new_nodes],
|
||||
)
|
||||
|
||||
logger.info(f"Retrieved {len(profile_nodes)} profiles, {len(new_nodes)} new after deduplication")
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue