mirror of
https://github.com/supermemoryai/supermemory.git
synced 2026-08-28 05:25:33 +00:00
fix(openai-sdk-python): harden v4 migration
This commit is contained in:
parent
f68bd30262
commit
183e9fba93
6 changed files with 52 additions and 36 deletions
|
|
@ -245,8 +245,7 @@ tools = SupermemoryTools(
|
|||
# Search memories
|
||||
result = await tools.search_memories(
|
||||
information_to_get="user preferences",
|
||||
limit=10,
|
||||
include_full_docs=True
|
||||
limit=10
|
||||
)
|
||||
|
||||
# Add memory
|
||||
|
|
@ -260,6 +259,10 @@ result = await tools.fetch_memory(
|
|||
)
|
||||
```
|
||||
|
||||
`include_full_docs` is retained as a deprecated Python argument for compatibility,
|
||||
but v4 search returns relevant memories and chunks instead of full source documents.
|
||||
It is no longer exposed in the OpenAI tool schema.
|
||||
|
||||
### Individual Tools
|
||||
|
||||
```python
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|||
|
||||
[project]
|
||||
name = "supermemory-openai-sdk"
|
||||
version = "1.0.5"
|
||||
version = "1.0.6"
|
||||
description = "Memory tools for OpenAI function calling with supermemory"
|
||||
readme = "README.md"
|
||||
license = "MIT"
|
||||
|
|
@ -15,7 +15,6 @@ classifiers = [
|
|||
"Intended Audience :: Developers",
|
||||
"License :: OSI Approved :: MIT License",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Programming Language :: Python :: 3.8",
|
||||
"Programming Language :: Python :: 3.9",
|
||||
"Programming Language :: Python :: 3.10",
|
||||
"Programming Language :: Python :: 3.11",
|
||||
|
|
@ -62,7 +61,7 @@ multi_line_output = 3
|
|||
line_length = 88
|
||||
|
||||
[tool.mypy]
|
||||
python_version = "3.8"
|
||||
python_version = "3.9"
|
||||
warn_return_any = true
|
||||
warn_unused_configs = true
|
||||
disallow_untyped_defs = true
|
||||
|
|
|
|||
|
|
@ -222,15 +222,15 @@ async def add_memory_tool(
|
|||
) -> None:
|
||||
"""Add a new memory to the SuperMemory system."""
|
||||
try:
|
||||
add_params = {
|
||||
"content": content,
|
||||
"container_tag": container_tag,
|
||||
}
|
||||
if custom_id is not None:
|
||||
add_params["custom_id"] = custom_id
|
||||
|
||||
# Handle both sync and async supermemory clients
|
||||
result = client.add(**add_params)
|
||||
if custom_id is None:
|
||||
result = client.add(content=content, container_tag=container_tag)
|
||||
else:
|
||||
result = client.add(
|
||||
content=content,
|
||||
container_tag=container_tag,
|
||||
custom_id=custom_id,
|
||||
)
|
||||
if inspect.isawaitable(result):
|
||||
response = await result
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
"""Supermemory tools for OpenAI function calling."""
|
||||
|
||||
import json
|
||||
from typing import Dict, List, Optional, TypedDict, Union
|
||||
import warnings
|
||||
from typing import Dict, List, Optional, TypedDict
|
||||
|
||||
import supermemory
|
||||
from openai.types.chat import (
|
||||
|
|
@ -10,7 +11,6 @@ from openai.types.chat import (
|
|||
ChatCompletionToolMessageParam,
|
||||
)
|
||||
from supermemory.types import AddResponse, SearchMemoriesResponse
|
||||
from supermemory.types.search_memories_response import Result
|
||||
|
||||
from .exceptions import (
|
||||
SupermemoryConfigurationError,
|
||||
|
|
@ -23,6 +23,8 @@ class SupermemoryToolsConfig(TypedDict, total=False):
|
|||
"""Configuration for Supermemory tools.
|
||||
|
||||
Only one of `project_id` or `container_tags` can be provided.
|
||||
The first container tag is the primary v4 search scope; all configured tags
|
||||
are applied when adding a memory.
|
||||
"""
|
||||
|
||||
base_url: Optional[str]
|
||||
|
|
@ -38,7 +40,7 @@ class MemorySearchResult(TypedDict, total=False):
|
|||
"""Result type for memory search operations."""
|
||||
|
||||
success: bool
|
||||
results: Optional[List[Result]]
|
||||
results: Optional[List[Dict[str, object]]]
|
||||
count: Optional[int]
|
||||
error: Optional[str]
|
||||
|
||||
|
|
@ -65,14 +67,6 @@ MEMORY_TOOL_SCHEMAS: Dict[str, ChatCompletionFunctionToolParam] = {
|
|||
"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",
|
||||
|
|
@ -169,23 +163,32 @@ class SupermemoryTools:
|
|||
async def search_memories(
|
||||
self,
|
||||
information_to_get: str,
|
||||
include_full_docs: bool = True,
|
||||
include_full_docs: Optional[bool] = None,
|
||||
limit: int = 10,
|
||||
) -> MemorySearchResult:
|
||||
"""Search memories.
|
||||
|
||||
Args:
|
||||
information_to_get: Terms to search for
|
||||
include_full_docs: Whether to include full document content
|
||||
include_full_docs: Deprecated compatibility argument. V4 search
|
||||
returns relevant memories and chunks, not full source documents.
|
||||
limit: Maximum number of results
|
||||
|
||||
Returns:
|
||||
MemorySearchResult
|
||||
"""
|
||||
if include_full_docs is not None:
|
||||
warnings.warn(
|
||||
"include_full_docs is deprecated and ignored because v4 search "
|
||||
"does not return full source documents",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
|
||||
try:
|
||||
response: SearchMemoriesResponse = await self.client.search.memories(
|
||||
q=information_to_get,
|
||||
container_tags=self.container_tags,
|
||||
container_tag=self.container_tags[0],
|
||||
limit=limit,
|
||||
threshold=0.6,
|
||||
search_mode="hybrid",
|
||||
|
|
@ -317,7 +320,7 @@ class SearchMemoriesTool:
|
|||
async def execute(
|
||||
self,
|
||||
information_to_get: str,
|
||||
include_full_docs: bool = True,
|
||||
include_full_docs: Optional[bool] = None,
|
||||
limit: int = 10,
|
||||
) -> MemorySearchResult:
|
||||
"""Execute search memories."""
|
||||
|
|
|
|||
|
|
@ -159,6 +159,10 @@ class TestToolDefinitions:
|
|||
assert search_tool is not None
|
||||
assert search_tool["type"] == "function"
|
||||
assert "information_to_get" in search_tool["function"]["parameters"]["required"]
|
||||
assert (
|
||||
"include_full_docs"
|
||||
not in search_tool["function"]["parameters"]["properties"]
|
||||
)
|
||||
|
||||
# Check addMemory
|
||||
add_tool = next(
|
||||
|
|
@ -206,25 +210,32 @@ class TestMemoryOperationsUnit:
|
|||
|
||||
@pytest.mark.asyncio
|
||||
async def test_search_memories_uses_search_memories_hybrid(self):
|
||||
"""search_memories must call client.search.memories with hybrid mode."""
|
||||
"""V4 search must use the primary singular tag and hybrid mode."""
|
||||
from types import SimpleNamespace
|
||||
from unittest.mock import AsyncMock
|
||||
|
||||
tools = SupermemoryTools("test-key", {"container_tags": ["unit-tag"]})
|
||||
tools = SupermemoryTools(
|
||||
"test-key", {"container_tags": ["primary-tag", "secondary-tag"]}
|
||||
)
|
||||
tools.client.search.memories = AsyncMock(
|
||||
return_value=SimpleNamespace(
|
||||
results=[SimpleNamespace(model_dump=lambda: {"memory": "likes tea"})]
|
||||
)
|
||||
)
|
||||
|
||||
result = await tools.search_memories("tea", limit=3)
|
||||
with pytest.warns(DeprecationWarning, match="include_full_docs"):
|
||||
result = await tools.search_memories(
|
||||
"tea", include_full_docs=False, limit=3
|
||||
)
|
||||
|
||||
assert result["success"] is True
|
||||
assert result["count"] == 1
|
||||
tools.client.search.memories.assert_awaited_once()
|
||||
kwargs = tools.client.search.memories.await_args.kwargs
|
||||
assert kwargs["q"] == "tea"
|
||||
assert kwargs["container_tags"] == ["unit-tag"]
|
||||
assert kwargs["container_tag"] == "primary-tag"
|
||||
assert "container_tags" not in kwargs
|
||||
assert "include_full_docs" not in kwargs
|
||||
assert kwargs["limit"] == 3
|
||||
assert kwargs["search_mode"] == "hybrid"
|
||||
|
||||
|
|
|
|||
8
packages/openai-sdk-python/uv.lock
generated
8
packages/openai-sdk-python/uv.lock
generated
|
|
@ -377,7 +377,7 @@ resolution-markers = [
|
|||
"python_full_version < '3.10'",
|
||||
]
|
||||
dependencies = [
|
||||
{ name = "colorama", marker = "python_full_version < '3.10' and sys_platform == 'win32'" },
|
||||
{ name = "colorama", marker = "sys_platform == 'win32'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593, upload-time = "2024-12-21T18:38:44.339Z" }
|
||||
wheels = [
|
||||
|
|
@ -392,7 +392,7 @@ resolution-markers = [
|
|||
"python_full_version >= '3.10'",
|
||||
]
|
||||
dependencies = [
|
||||
{ name = "colorama", marker = "python_full_version >= '3.10' and sys_platform == 'win32'" },
|
||||
{ name = "colorama", marker = "sys_platform == 'win32'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/60/6c/8ca2efa64cf75a977a0d7fac081354553ebe483345c734fb6b6515d96bbc/click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202", size = 286342, upload-time = "2025-05-20T23:19:49.832Z" }
|
||||
wheels = [
|
||||
|
|
@ -422,7 +422,7 @@ name = "exceptiongroup"
|
|||
version = "1.3.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "typing-extensions", marker = "python_full_version < '3.13'" },
|
||||
{ name = "typing-extensions" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/0b/9f/a65090624ecf468cdca03533906e7c69ed7588582240cfe7cc9e770b50eb/exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88", size = 29749, upload-time = "2025-05-10T17:42:51.123Z" }
|
||||
wheels = [
|
||||
|
|
@ -1372,7 +1372,7 @@ wheels = [
|
|||
|
||||
[[package]]
|
||||
name = "supermemory-openai-sdk"
|
||||
version = "1.0.4"
|
||||
version = "1.0.6"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "openai" },
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue