mirror of
https://github.com/supermemoryai/supermemory.git
synced 2026-10-07 02:58:11 +00:00
fix deduplication in python sdk (#626)
done in a similar way to the ai sdk
This commit is contained in:
parent
5bef1b2bbc
commit
10d8f92439
3 changed files with 99 additions and 25 deletions
|
|
@ -28,6 +28,8 @@ from .utils import (
|
|||
get_last_user_message,
|
||||
get_conversation_content,
|
||||
convert_profile_to_markdown,
|
||||
deduplicate_memories,
|
||||
DeduplicatedMemories,
|
||||
)
|
||||
|
||||
from .exceptions import (
|
||||
|
|
@ -64,6 +66,8 @@ __all__ = [
|
|||
"get_last_user_message",
|
||||
"get_conversation_content",
|
||||
"convert_profile_to_markdown",
|
||||
"deduplicate_memories",
|
||||
"DeduplicatedMemories",
|
||||
# Exceptions
|
||||
"SupermemoryError",
|
||||
"SupermemoryConfigurationError",
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ from .utils import (
|
|||
get_last_user_message,
|
||||
get_conversation_content,
|
||||
convert_profile_to_markdown,
|
||||
deduplicate_memories,
|
||||
)
|
||||
from .exceptions import (
|
||||
SupermemoryConfigurationError,
|
||||
|
|
@ -119,8 +120,11 @@ async def add_system_prompt(
|
|||
container_tag, query_text, api_key
|
||||
)
|
||||
|
||||
memory_count_static = len(memories_response.profile.get("static", []))
|
||||
memory_count_dynamic = len(memories_response.profile.get("dynamic", []))
|
||||
profile = memories_response.profile or {}
|
||||
search_results_data = memories_response.search_results or {}
|
||||
memory_count_static = len(profile.get("static", []))
|
||||
memory_count_dynamic = len(profile.get("dynamic", []))
|
||||
memory_count_search = len(search_results_data.get("results", []))
|
||||
|
||||
logger.info(
|
||||
"Memory search completed",
|
||||
|
|
@ -133,39 +137,39 @@ async def add_system_prompt(
|
|||
},
|
||||
)
|
||||
|
||||
deduplicated = deduplicate_memories(
|
||||
static=profile.get("static", []),
|
||||
dynamic=profile.get("dynamic", []),
|
||||
search_results=search_results_data.get("results", []),
|
||||
)
|
||||
|
||||
logger.debug(
|
||||
"Memory deduplication completed",
|
||||
{
|
||||
"static": {"original": memory_count_static, "deduplicated": len(deduplicated.static)},
|
||||
"dynamic": {"original": memory_count_dynamic, "deduplicated": len(deduplicated.dynamic)},
|
||||
"search_results": {"original": memory_count_search, "deduplicated": len(deduplicated.search_results)},
|
||||
},
|
||||
)
|
||||
|
||||
profile_data = ""
|
||||
if mode != "query":
|
||||
profile_data = convert_profile_to_markdown(
|
||||
{
|
||||
"profile": {
|
||||
"static": [
|
||||
item.get("memory", "") if isinstance(item, dict) else str(item)
|
||||
for item in memories_response.profile.get("static", [])
|
||||
],
|
||||
"dynamic": [
|
||||
item.get("memory", "") if isinstance(item, dict) else str(item)
|
||||
for item in memories_response.profile.get("dynamic", [])
|
||||
],
|
||||
},
|
||||
"searchResults": {
|
||||
"results": [
|
||||
{"memory": item.get("memory", "") if isinstance(item, dict) else str(item)}
|
||||
for item in memories_response.search_results.get("results", [])
|
||||
],
|
||||
"static": deduplicated.static,
|
||||
"dynamic": deduplicated.dynamic,
|
||||
},
|
||||
"searchResults": {"results": []},
|
||||
}
|
||||
)
|
||||
|
||||
search_results_memories = ""
|
||||
if mode != "profile":
|
||||
search_results = memories_response.search_results.get("results", [])
|
||||
if search_results:
|
||||
search_results_memories = (
|
||||
f"Search results for user's recent message: \n"
|
||||
+ "\n".join(
|
||||
f"- {result.get('memory', '') if isinstance(result, dict) else str(result)}" for result in search_results
|
||||
)
|
||||
)
|
||||
if mode != "profile" and deduplicated.search_results:
|
||||
search_results_memories = (
|
||||
"Search results for user's recent message: \n"
|
||||
+ "\n".join(f"- {memory}" for memory in deduplicated.search_results)
|
||||
)
|
||||
|
||||
memories = f"{profile_data}\n{search_results_memories}".strip()
|
||||
|
||||
|
|
|
|||
|
|
@ -187,6 +187,72 @@ def get_conversation_content(
|
|||
return "\n\n".join(conversation_parts)
|
||||
|
||||
|
||||
class DeduplicatedMemories:
|
||||
"""Deduplicated memory strings organized by source."""
|
||||
|
||||
def __init__(self, static: list[str], dynamic: list[str], search_results: list[str]):
|
||||
self.static = static
|
||||
self.dynamic = dynamic
|
||||
self.search_results = search_results
|
||||
|
||||
|
||||
def deduplicate_memories(
|
||||
static: Optional[list[Any]] = None,
|
||||
dynamic: Optional[list[Any]] = None,
|
||||
search_results: Optional[list[Any]] = None,
|
||||
) -> DeduplicatedMemories:
|
||||
"""
|
||||
Deduplicates memory items across sources. Priority: Static > Dynamic > Search Results.
|
||||
Same memory appearing in multiple sources is kept only in the highest-priority source.
|
||||
"""
|
||||
static_items = static or []
|
||||
dynamic_items = dynamic or []
|
||||
search_items = search_results or []
|
||||
|
||||
def extract_memory_text(item: Any) -> Optional[str]:
|
||||
if item is None:
|
||||
return None
|
||||
if isinstance(item, dict):
|
||||
memory = item.get("memory")
|
||||
if isinstance(memory, str):
|
||||
trimmed = memory.strip()
|
||||
return trimmed if trimmed else None
|
||||
return None
|
||||
if isinstance(item, str):
|
||||
trimmed = item.strip()
|
||||
return trimmed if trimmed else None
|
||||
return None
|
||||
|
||||
static_memories: list[str] = []
|
||||
seen_memories: set[str] = set()
|
||||
|
||||
for item in static_items:
|
||||
memory = extract_memory_text(item)
|
||||
if memory is not None:
|
||||
static_memories.append(memory)
|
||||
seen_memories.add(memory)
|
||||
|
||||
dynamic_memories: list[str] = []
|
||||
for item in dynamic_items:
|
||||
memory = extract_memory_text(item)
|
||||
if memory is not None and memory not in seen_memories:
|
||||
dynamic_memories.append(memory)
|
||||
seen_memories.add(memory)
|
||||
|
||||
search_memories: list[str] = []
|
||||
for item in search_items:
|
||||
memory = extract_memory_text(item)
|
||||
if memory is not None and memory not in seen_memories:
|
||||
search_memories.append(memory)
|
||||
seen_memories.add(memory)
|
||||
|
||||
return DeduplicatedMemories(
|
||||
static=static_memories,
|
||||
dynamic=dynamic_memories,
|
||||
search_results=search_memories,
|
||||
)
|
||||
|
||||
|
||||
def convert_profile_to_markdown(data: dict[str, Any]) -> str:
|
||||
"""
|
||||
Convert profile data to markdown based on profile.static and profile.dynamic properties.
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue