"""Supermemory tools for OpenAI function calling.""" import json from typing import Dict, List, Optional, TypedDict, Union import supermemory from openai.types.chat import ( ChatCompletionFunctionToolParam, ChatCompletionMessageToolCall, ChatCompletionToolMessageParam, ) from supermemory.types import ( MemoryAddResponse, MemoryGetResponse, SearchExecuteResponse, ) from supermemory.types.search_execute_response import Result from .exceptions import ( SupermemoryConfigurationError, SupermemoryMemoryOperationError, SupermemoryNetworkError, ) class SupermemoryToolsConfig(TypedDict, total=False): """Configuration for Supermemory tools. Only one of `project_id` or `container_tags` can be provided. """ base_url: Optional[str] container_tags: Optional[List[str]] project_id: Optional[str] # Type aliases using inferred types from supermemory package MemoryObject = Union[MemoryGetResponse, MemoryAddResponse] class MemorySearchResult(TypedDict, total=False): """Result type for memory search operations.""" success: bool results: Optional[List[Result]] count: Optional[int] error: Optional[str] class MemoryAddResult(TypedDict, total=False): """Result type for memory add operations.""" success: bool memory: Optional[MemoryAddResponse] error: Optional[str] # Function schemas for OpenAI function calling MEMORY_TOOL_SCHEMAS: Dict[str, ChatCompletionFunctionToolParam] = { "search_memories": { "name": "search_memories", "description": ( "Search (recall) memories/details/information about the user or other facts or entities. Run when explicitly asked or when context about user's past choices would be helpful." ), "parameters": { "type": "object", "properties": { "information_to_get": { "type": "string", "description": "Terms to search for in the user's memories", }, "include_full_docs": { "type": "boolean", "description": ( "Whether to include the full document content in the response. " "Defaults to true for better AI context." ), "default": True, }, "limit": { "type": "number", "description": "Maximum number of results to return", "default": 10, }, }, "required": ["information_to_get"], }, }, "add_memory": { "name": "add_memory", "description": ( "Add (remember) memories/details/information about the user or other facts or entities. Run when explicitly asked or when the user mentions any information generalizable beyond the context of the current conversation." ), "parameters": { "type": "object", "properties": { "memory": { "type": "string", "description": ( "The text content of the memory to add. This should be a " "single sentence or a short paragraph." ), }, }, "required": ["memory"], }, }, } class SupermemoryTools: """Create memory tool handlers for OpenAI function calling.""" def __init__(self, api_key: str, config: Optional[SupermemoryToolsConfig] = None): """Initialize SupermemoryTools. Args: api_key: Supermemory API key config: Optional configuration """ config = config or {} # Initialize Supermemory client client_kwargs = {"api_key": api_key} if config.get("base_url"): client_kwargs["base_url"] = config["base_url"] self.client = supermemory.Supermemory(**client_kwargs) # Set container tags if config.get("project_id"): self.container_tags = [f"sm_project_{config['project_id']}"] elif config.get("container_tags"): self.container_tags = config["container_tags"] else: self.container_tags = ["sm_project_default"] def get_tool_definitions(self) -> List[ChatCompletionFunctionToolParam]: """Get OpenAI function definitions for all memory tools. Returns: List of ChatCompletionToolParam definitions """ return [ {"type": "function", "function": MEMORY_TOOL_SCHEMAS["search_memories"]}, {"type": "function", "function": MEMORY_TOOL_SCHEMAS["add_memory"]}, ] async def execute_tool_call(self, tool_call: ChatCompletionMessageToolCall) -> str: """Execute a tool call based on the function name and arguments. Args: tool_call: The tool call from OpenAI Returns: JSON string result """ function_name = tool_call.function.name args = json.loads(tool_call.function.arguments) if function_name == "search_memories": result = await self.search_memories(**args) elif function_name == "add_memory": result = await self.add_memory(**args) else: result = { "success": False, "error": f"Unknown function: {function_name}", } return json.dumps(result) async def search_memories( self, information_to_get: str, include_full_docs: bool = True, limit: int = 10, ) -> MemorySearchResult: """Search memories. Args: information_to_get: Terms to search for include_full_docs: Whether to include full document content limit: Maximum number of results Returns: MemorySearchResult """ try: response: SearchExecuteResponse = await self.client.search.execute( q=information_to_get, container_tags=self.container_tags, limit=limit, chunk_threshold=0.6, include_full_docs=include_full_docs, ) return MemorySearchResult( success=True, results=response.results, count=len(response.results), ) except (OSError, ConnectionError) as network_error: return MemorySearchResult( success=False, error=f"Network error: {network_error}", ) except Exception as error: return MemorySearchResult( success=False, error=f"Memory search failed: {error}", ) async def add_memory(self, memory: str) -> MemoryAddResult: """Add a memory. Args: memory: The memory content to add Returns: MemoryAddResult """ try: metadata: Dict[str, object] = {} add_params = { "content": memory, "container_tags": self.container_tags, } if metadata: add_params["metadata"] = metadata response: MemoryAddResponse = await self.client.add(**add_params) return MemoryAddResult( success=True, memory=response, ) except (OSError, ConnectionError) as network_error: return MemoryAddResult( success=False, error=f"Network error: {network_error}", ) except Exception as error: return MemoryAddResult( success=False, error=f"Memory add failed: {error}", ) def create_supermemory_tools( api_key: str, config: Optional[SupermemoryToolsConfig] = None ) -> SupermemoryTools: """Helper function to create SupermemoryTools instance. Args: api_key: Supermemory API key config: Optional configuration Returns: SupermemoryTools instance """ return SupermemoryTools(api_key, config) def get_memory_tool_definitions() -> List[ChatCompletionFunctionToolParam]: """Get OpenAI function definitions for memory tools. Returns: List of ChatCompletionToolParam definitions """ return [ {"type": "function", "function": MEMORY_TOOL_SCHEMAS["search_memories"]}, {"type": "function", "function": MEMORY_TOOL_SCHEMAS["add_memory"]}, ] async def execute_memory_tool_calls( api_key: str, tool_calls: List[ChatCompletionMessageToolCall], config: Optional[SupermemoryToolsConfig] = None, ) -> List[ChatCompletionToolMessageParam]: """Execute tool calls from OpenAI function calling. Args: api_key: Supermemory API key tool_calls: List of tool calls from OpenAI config: Optional configuration Returns: List of tool message parameters """ tools = SupermemoryTools(api_key, config) async def execute_single_call( tool_call: ChatCompletionMessageToolCall, ) -> ChatCompletionToolMessageParam: result = await tools.execute_tool_call(tool_call) return ChatCompletionToolMessageParam( tool_call_id=tool_call.id, role="tool", content=result, ) # Execute all tool calls concurrently import asyncio results = await asyncio.gather( *[execute_single_call(tool_call) for tool_call in tool_calls] ) return results # Individual tool creators for more granular control class SearchMemoriesTool: """Individual search memories tool.""" def __init__(self, api_key: str, config: Optional[SupermemoryToolsConfig] = None): self.tools = SupermemoryTools(api_key, config) self.definition: ChatCompletionToolParam = { "type": "function", "function": MEMORY_TOOL_SCHEMAS["search_memories"], } async def execute( self, information_to_get: str, include_full_docs: bool = True, limit: int = 10, ) -> MemorySearchResult: """Execute search memories.""" return await self.tools.search_memories( information_to_get=information_to_get, include_full_docs=include_full_docs, limit=limit, ) class AddMemoryTool: """Individual add memory tool.""" def __init__(self, api_key: str, config: Optional[SupermemoryToolsConfig] = None): self.tools = SupermemoryTools(api_key, config) self.definition: ChatCompletionToolParam = { "type": "function", "function": MEMORY_TOOL_SCHEMAS["add_memory"], } async def execute(self, memory: str) -> MemoryAddResult: """Execute add memory.""" return await self.tools.add_memory(memory=memory) def create_search_memories_tool( api_key: str, config: Optional[SupermemoryToolsConfig] = None ) -> SearchMemoriesTool: """Create individual search memories tool. Args: api_key: Supermemory API key config: Optional configuration Returns: SearchMemoriesTool instance """ return SearchMemoriesTool(api_key, config) def create_add_memory_tool( api_key: str, config: Optional[SupermemoryToolsConfig] = None ) -> AddMemoryTool: """Create individual add memory tool. Args: api_key: Supermemory API key config: Optional configuration Returns: AddMemoryTool instance """ return AddMemoryTool(api_key, config)