From a1403da98836ac4215ee81f168ff044dcd83fc83 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 28 Nov 2025 03:19:41 +0000 Subject: [PATCH 01/11] fix: ensure streams are always closed --- src/supermemory/_streaming.py | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/supermemory/_streaming.py b/src/supermemory/_streaming.py index 5e12f66d..2ab794ff 100644 --- a/src/supermemory/_streaming.py +++ b/src/supermemory/_streaming.py @@ -54,11 +54,12 @@ def __stream__(self) -> Iterator[_T]: process_data = self._client._process_response_data iterator = self._iter_events() - for sse in iterator: - yield process_data(data=sse.json(), cast_to=cast_to, response=response) - - # As we might not fully consume the response stream, we need to close it explicitly - response.close() + try: + for sse in iterator: + yield process_data(data=sse.json(), cast_to=cast_to, response=response) + finally: + # Ensure the response is closed even if the consumer doesn't read all data + response.close() def __enter__(self) -> Self: return self @@ -117,11 +118,12 @@ async def __stream__(self) -> AsyncIterator[_T]: process_data = self._client._process_response_data iterator = self._iter_events() - async for sse in iterator: - yield process_data(data=sse.json(), cast_to=cast_to, response=response) - - # As we might not fully consume the response stream, we need to close it explicitly - await response.aclose() + try: + async for sse in iterator: + yield process_data(data=sse.json(), cast_to=cast_to, response=response) + finally: + # Ensure the response is closed even if the consumer doesn't read all data + await response.aclose() async def __aenter__(self) -> Self: return self From 2fc3d2a92410192a31bb1c4a7b85094c2ea55f4a Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 28 Nov 2025 03:20:47 +0000 Subject: [PATCH 02/11] chore(deps): mypy 1.18.1 has a regression, pin to 1.17 --- pyproject.toml | 2 +- requirements-dev.lock | 4 +++- requirements.lock | 8 ++++---- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index c1c60c59..7c8743eb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -46,7 +46,7 @@ managed = true # version pins are in requirements-dev.lock dev-dependencies = [ "pyright==1.1.399", - "mypy", + "mypy==1.17", "respx", "pytest", "pytest-asyncio", diff --git a/requirements-dev.lock b/requirements-dev.lock index ffa6c8d8..77373c2a 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -72,7 +72,7 @@ mdurl==0.1.2 multidict==6.4.4 # via aiohttp # via yarl -mypy==1.14.1 +mypy==1.17.0 mypy-extensions==1.0.0 # via mypy nodeenv==1.8.0 @@ -81,6 +81,8 @@ nox==2023.4.22 packaging==23.2 # via nox # via pytest +pathspec==0.12.1 + # via mypy platformdirs==3.11.0 # via virtualenv pluggy==1.5.0 diff --git a/requirements.lock b/requirements.lock index 4563dece..db7b68d7 100644 --- a/requirements.lock +++ b/requirements.lock @@ -55,21 +55,21 @@ multidict==6.4.4 propcache==0.3.1 # via aiohttp # via yarl -pydantic==2.11.9 +pydantic==2.12.5 # via supermemory -pydantic-core==2.33.2 +pydantic-core==2.41.5 # via pydantic sniffio==1.3.0 # via anyio # via supermemory -typing-extensions==4.12.2 +typing-extensions==4.15.0 # via anyio # via multidict # via pydantic # via pydantic-core # via supermemory # via typing-inspection -typing-inspection==0.4.1 +typing-inspection==0.4.2 # via pydantic yarl==1.20.0 # via aiohttp From 37b9f74097fdc2e3395714ccb72c0f470d162ac4 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 3 Dec 2025 04:50:18 +0000 Subject: [PATCH 03/11] chore: update lockfile --- pyproject.toml | 14 +++--- requirements-dev.lock | 108 +++++++++++++++++++++++------------------- requirements.lock | 31 ++++++------ 3 files changed, 83 insertions(+), 70 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 7c8743eb..4956c29d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,14 +7,16 @@ license = "Apache-2.0" authors = [ { name = "Supermemory", email = "dhravya@supermemory.com" }, ] + dependencies = [ - "httpx>=0.23.0, <1", - "pydantic>=1.9.0, <3", - "typing-extensions>=4.10, <5", - "anyio>=3.5.0, <5", - "distro>=1.7.0, <2", - "sniffio", + "httpx>=0.23.0, <1", + "pydantic>=1.9.0, <3", + "typing-extensions>=4.10, <5", + "anyio>=3.5.0, <5", + "distro>=1.7.0, <2", + "sniffio", ] + requires-python = ">= 3.9" classifiers = [ "Typing :: Typed", diff --git a/requirements-dev.lock b/requirements-dev.lock index 77373c2a..8d84f0ab 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -12,40 +12,45 @@ -e file:. aiohappyeyeballs==2.6.1 # via aiohttp -aiohttp==3.12.8 +aiohttp==3.13.2 # via httpx-aiohttp # via supermemory -aiosignal==1.3.2 +aiosignal==1.4.0 # via aiohttp -annotated-types==0.6.0 +annotated-types==0.7.0 # via pydantic -anyio==4.4.0 +anyio==4.12.0 # via httpx # via supermemory -argcomplete==3.1.2 +argcomplete==3.6.3 # via nox async-timeout==5.0.1 # via aiohttp -attrs==25.3.0 +attrs==25.4.0 # via aiohttp -certifi==2023.7.22 + # via nox +backports-asyncio-runner==1.2.0 + # via pytest-asyncio +certifi==2025.11.12 # via httpcore # via httpx -colorlog==6.7.0 +colorlog==6.10.1 + # via nox +dependency-groups==1.3.1 # via nox -dirty-equals==0.6.0 -distlib==0.3.7 +dirty-equals==0.11 +distlib==0.4.0 # via virtualenv -distro==1.8.0 +distro==1.9.0 # via supermemory -exceptiongroup==1.2.2 +exceptiongroup==1.3.1 # via anyio # via pytest -execnet==2.1.1 +execnet==2.1.2 # via pytest-xdist -filelock==3.12.4 +filelock==3.19.1 # via virtualenv -frozenlist==1.6.2 +frozenlist==1.8.0 # via aiohttp # via aiosignal h11==0.16.0 @@ -58,82 +63,87 @@ httpx==0.28.1 # via supermemory httpx-aiohttp==0.1.9 # via supermemory -idna==3.4 +humanize==4.13.0 + # via nox +idna==3.11 # via anyio # via httpx # via yarl -importlib-metadata==7.0.0 -iniconfig==2.0.0 +importlib-metadata==8.7.0 +iniconfig==2.1.0 # via pytest markdown-it-py==3.0.0 # via rich mdurl==0.1.2 # via markdown-it-py -multidict==6.4.4 +multidict==6.7.0 # via aiohttp # via yarl mypy==1.17.0 -mypy-extensions==1.0.0 +mypy-extensions==1.1.0 # via mypy -nodeenv==1.8.0 +nodeenv==1.9.1 # via pyright -nox==2023.4.22 -packaging==23.2 +nox==2025.11.12 +packaging==25.0 + # via dependency-groups # via nox # via pytest pathspec==0.12.1 # via mypy -platformdirs==3.11.0 +platformdirs==4.4.0 # via virtualenv -pluggy==1.5.0 +pluggy==1.6.0 # via pytest -propcache==0.3.1 +propcache==0.4.1 # via aiohttp # via yarl -pydantic==2.11.9 +pydantic==2.12.5 # via supermemory -pydantic-core==2.33.2 +pydantic-core==2.41.5 # via pydantic -pygments==2.18.0 +pygments==2.19.2 + # via pytest # via rich pyright==1.1.399 -pytest==8.3.3 +pytest==8.4.2 # via pytest-asyncio # via pytest-xdist -pytest-asyncio==0.24.0 -pytest-xdist==3.7.0 -python-dateutil==2.8.2 +pytest-asyncio==1.2.0 +pytest-xdist==3.8.0 +python-dateutil==2.9.0.post0 # via time-machine -pytz==2023.3.post1 - # via dirty-equals respx==0.22.0 -rich==13.7.1 -ruff==0.9.4 -setuptools==68.2.2 - # via nodeenv -six==1.16.0 +rich==14.2.0 +ruff==0.14.7 +six==1.17.0 # via python-dateutil -sniffio==1.3.0 - # via anyio +sniffio==1.3.1 # via supermemory -time-machine==2.9.0 -tomli==2.0.2 +time-machine==2.19.0 +tomli==2.3.0 + # via dependency-groups # via mypy + # via nox # via pytest -typing-extensions==4.12.2 +typing-extensions==4.15.0 + # via aiosignal # via anyio + # via exceptiongroup # via multidict # via mypy # via pydantic # via pydantic-core # via pyright + # via pytest-asyncio # via supermemory # via typing-inspection -typing-inspection==0.4.1 + # via virtualenv +typing-inspection==0.4.2 # via pydantic -virtualenv==20.24.5 +virtualenv==20.35.4 # via nox -yarl==1.20.0 +yarl==1.22.0 # via aiohttp -zipp==3.17.0 +zipp==3.23.0 # via importlib-metadata diff --git a/requirements.lock b/requirements.lock index db7b68d7..ba5d1502 100644 --- a/requirements.lock +++ b/requirements.lock @@ -12,28 +12,28 @@ -e file:. aiohappyeyeballs==2.6.1 # via aiohttp -aiohttp==3.12.8 +aiohttp==3.13.2 # via httpx-aiohttp # via supermemory -aiosignal==1.3.2 +aiosignal==1.4.0 # via aiohttp -annotated-types==0.6.0 +annotated-types==0.7.0 # via pydantic -anyio==4.4.0 +anyio==4.12.0 # via httpx # via supermemory async-timeout==5.0.1 # via aiohttp -attrs==25.3.0 +attrs==25.4.0 # via aiohttp -certifi==2023.7.22 +certifi==2025.11.12 # via httpcore # via httpx -distro==1.8.0 +distro==1.9.0 # via supermemory -exceptiongroup==1.2.2 +exceptiongroup==1.3.1 # via anyio -frozenlist==1.6.2 +frozenlist==1.8.0 # via aiohttp # via aiosignal h11==0.16.0 @@ -45,25 +45,26 @@ httpx==0.28.1 # via supermemory httpx-aiohttp==0.1.9 # via supermemory -idna==3.4 +idna==3.11 # via anyio # via httpx # via yarl -multidict==6.4.4 +multidict==6.7.0 # via aiohttp # via yarl -propcache==0.3.1 +propcache==0.4.1 # via aiohttp # via yarl pydantic==2.12.5 # via supermemory pydantic-core==2.41.5 # via pydantic -sniffio==1.3.0 - # via anyio +sniffio==1.3.1 # via supermemory typing-extensions==4.15.0 + # via aiosignal # via anyio + # via exceptiongroup # via multidict # via pydantic # via pydantic-core @@ -71,5 +72,5 @@ typing-extensions==4.15.0 # via typing-inspection typing-inspection==0.4.2 # via pydantic -yarl==1.20.0 +yarl==1.22.0 # via aiohttp From 442b9fdc7e8b7b07dc82cc087ffbc6fbe60bd12f Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 3 Dec 2025 05:00:57 +0000 Subject: [PATCH 04/11] chore(docs): use environment variables for authentication in code snippets --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 7cc684a6..f634745d 100644 --- a/README.md +++ b/README.md @@ -83,6 +83,7 @@ pip install supermemory[aiohttp] Then you can enable it by instantiating the client with `http_client=DefaultAioHttpClient()`: ```python +import os import asyncio from supermemory import DefaultAioHttpClient from supermemory import AsyncSupermemory @@ -90,7 +91,7 @@ from supermemory import AsyncSupermemory async def main() -> None: async with AsyncSupermemory( - api_key="My API Key", + api_key=os.environ.get("SUPERMEMORY_API_KEY"), # This is the default and can be omitted http_client=DefaultAioHttpClient(), ) as client: response = await client.search.documents( From 3042cbfd221d52291480c6df1c1fca40eb97a61f Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 3 Dec 2025 19:21:16 +0000 Subject: [PATCH 05/11] feat(api): api update --- .stats.yml | 4 ++-- src/supermemory/resources/connections.py | 20 +++++++++---------- src/supermemory/resources/settings.py | 12 +++++++++++ .../types/document_get_response.py | 1 + .../types/document_list_response.py | 1 + src/supermemory/types/memory_get_response.py | 1 + src/supermemory/types/memory_list_response.py | 1 + src/supermemory/types/setting_get_response.py | 6 ++++++ .../types/setting_update_params.py | 6 ++++++ .../types/setting_update_response.py | 6 ++++++ tests/api_resources/test_settings.py | 6 ++++++ 11 files changed, 52 insertions(+), 12 deletions(-) diff --git a/.stats.yml b/.stats.yml index 6e2570c9..fe8a4066 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 19 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/supermemory--inc%2Fsupermemory-new-8f3109ed6b4537d66c1d430e552cb6d90323c2b495487132f5c5fc7fb0f91478.yml -openapi_spec_hash: e2f06a2c0e73fb52262924ecd4027a6c +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/supermemory--inc%2Fsupermemory-new-16b01076f65e994ba603cd91ce14ba5a2920d0d5b27b8c84d60b8a7fcf57900b.yml +openapi_spec_hash: bfaa2815be92e4baad6118efc09b606d config_hash: ec08a36e60458b4d83e71798a8043484 diff --git a/src/supermemory/resources/connections.py b/src/supermemory/resources/connections.py index cec839e0..f6dc1b75 100644 --- a/src/supermemory/resources/connections.py +++ b/src/supermemory/resources/connections.py @@ -59,7 +59,7 @@ def with_streaming_response(self) -> ConnectionsResourceWithStreamingResponse: def create( self, - provider: Literal["notion", "google-drive", "onedrive", "web-crawler"], + provider: Literal["notion", "google-drive", "onedrive", "github", "web-crawler"], *, container_tags: SequenceNotStr[str] | Omit = omit, document_limit: int | Omit = omit, @@ -172,7 +172,7 @@ def delete_by_id( def delete_by_provider( self, - provider: Literal["notion", "google-drive", "onedrive", "web-crawler"], + provider: Literal["notion", "google-drive", "onedrive", "github", "web-crawler"], *, container_tags: SequenceNotStr[str], # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -245,7 +245,7 @@ def get_by_id( def get_by_tags( self, - provider: Literal["notion", "google-drive", "onedrive", "web-crawler"], + provider: Literal["notion", "google-drive", "onedrive", "github", "web-crawler"], *, container_tags: SequenceNotStr[str], # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -284,7 +284,7 @@ def get_by_tags( def import_( self, - provider: Literal["notion", "google-drive", "onedrive", "web-crawler"], + provider: Literal["notion", "google-drive", "onedrive", "github", "web-crawler"], *, container_tags: SequenceNotStr[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -322,7 +322,7 @@ def import_( def list_documents( self, - provider: Literal["notion", "google-drive", "onedrive", "web-crawler"], + provider: Literal["notion", "google-drive", "onedrive", "github", "web-crawler"], *, container_tags: SequenceNotStr[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -382,7 +382,7 @@ def with_streaming_response(self) -> AsyncConnectionsResourceWithStreamingRespon async def create( self, - provider: Literal["notion", "google-drive", "onedrive", "web-crawler"], + provider: Literal["notion", "google-drive", "onedrive", "github", "web-crawler"], *, container_tags: SequenceNotStr[str] | Omit = omit, document_limit: int | Omit = omit, @@ -497,7 +497,7 @@ async def delete_by_id( async def delete_by_provider( self, - provider: Literal["notion", "google-drive", "onedrive", "web-crawler"], + provider: Literal["notion", "google-drive", "onedrive", "github", "web-crawler"], *, container_tags: SequenceNotStr[str], # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -570,7 +570,7 @@ async def get_by_id( async def get_by_tags( self, - provider: Literal["notion", "google-drive", "onedrive", "web-crawler"], + provider: Literal["notion", "google-drive", "onedrive", "github", "web-crawler"], *, container_tags: SequenceNotStr[str], # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -609,7 +609,7 @@ async def get_by_tags( async def import_( self, - provider: Literal["notion", "google-drive", "onedrive", "web-crawler"], + provider: Literal["notion", "google-drive", "onedrive", "github", "web-crawler"], *, container_tags: SequenceNotStr[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -649,7 +649,7 @@ async def import_( async def list_documents( self, - provider: Literal["notion", "google-drive", "onedrive", "web-crawler"], + provider: Literal["notion", "google-drive", "onedrive", "github", "web-crawler"], *, container_tags: SequenceNotStr[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. diff --git a/src/supermemory/resources/settings.py b/src/supermemory/resources/settings.py index 7d5f25d6..1ff2b01c 100644 --- a/src/supermemory/resources/settings.py +++ b/src/supermemory/resources/settings.py @@ -50,6 +50,9 @@ def update( chunk_size: Optional[int] | Omit = omit, exclude_items: Union[str, float, bool, Dict[str, object], Iterable[object], None] | Omit = omit, filter_prompt: Optional[str] | Omit = omit, + github_client_id: Optional[str] | Omit = omit, + github_client_secret: Optional[str] | Omit = omit, + github_custom_key_enabled: Optional[bool] | Omit = omit, google_drive_client_id: Optional[str] | Omit = omit, google_drive_client_secret: Optional[str] | Omit = omit, google_drive_custom_key_enabled: Optional[bool] | Omit = omit, @@ -87,6 +90,9 @@ def update( "chunk_size": chunk_size, "exclude_items": exclude_items, "filter_prompt": filter_prompt, + "github_client_id": github_client_id, + "github_client_secret": github_client_secret, + "github_custom_key_enabled": github_custom_key_enabled, "google_drive_client_id": google_drive_client_id, "google_drive_client_secret": google_drive_client_secret, "google_drive_custom_key_enabled": google_drive_custom_key_enabled, @@ -153,6 +159,9 @@ async def update( chunk_size: Optional[int] | Omit = omit, exclude_items: Union[str, float, bool, Dict[str, object], Iterable[object], None] | Omit = omit, filter_prompt: Optional[str] | Omit = omit, + github_client_id: Optional[str] | Omit = omit, + github_client_secret: Optional[str] | Omit = omit, + github_custom_key_enabled: Optional[bool] | Omit = omit, google_drive_client_id: Optional[str] | Omit = omit, google_drive_client_secret: Optional[str] | Omit = omit, google_drive_custom_key_enabled: Optional[bool] | Omit = omit, @@ -190,6 +199,9 @@ async def update( "chunk_size": chunk_size, "exclude_items": exclude_items, "filter_prompt": filter_prompt, + "github_client_id": github_client_id, + "github_client_secret": github_client_secret, + "github_custom_key_enabled": github_custom_key_enabled, "google_drive_client_id": google_drive_client_id, "google_drive_client_secret": google_drive_client_secret, "google_drive_custom_key_enabled": google_drive_custom_key_enabled, diff --git a/src/supermemory/types/document_get_response.py b/src/supermemory/types/document_get_response.py index 595e19a5..e8e2bc37 100644 --- a/src/supermemory/types/document_get_response.py +++ b/src/supermemory/types/document_get_response.py @@ -86,6 +86,7 @@ class DocumentGetResponse(BaseModel): "notion_doc", "webpage", "onedrive", + "github_markdown", ] """Type of the document""" diff --git a/src/supermemory/types/document_list_response.py b/src/supermemory/types/document_list_response.py index d91063b8..6e34ee17 100644 --- a/src/supermemory/types/document_list_response.py +++ b/src/supermemory/types/document_list_response.py @@ -60,6 +60,7 @@ class Memory(BaseModel): "notion_doc", "webpage", "onedrive", + "github_markdown", ] """Type of the document""" diff --git a/src/supermemory/types/memory_get_response.py b/src/supermemory/types/memory_get_response.py index a70b0dd9..99610a0a 100644 --- a/src/supermemory/types/memory_get_response.py +++ b/src/supermemory/types/memory_get_response.py @@ -86,6 +86,7 @@ class MemoryGetResponse(BaseModel): "notion_doc", "webpage", "onedrive", + "github_markdown", ] """Type of the document""" diff --git a/src/supermemory/types/memory_list_response.py b/src/supermemory/types/memory_list_response.py index 9f9dc5d6..9502bba1 100644 --- a/src/supermemory/types/memory_list_response.py +++ b/src/supermemory/types/memory_list_response.py @@ -60,6 +60,7 @@ class Memory(BaseModel): "notion_doc", "webpage", "onedrive", + "github_markdown", ] """Type of the document""" diff --git a/src/supermemory/types/setting_get_response.py b/src/supermemory/types/setting_get_response.py index ef447bbc..4debad85 100644 --- a/src/supermemory/types/setting_get_response.py +++ b/src/supermemory/types/setting_get_response.py @@ -18,6 +18,12 @@ class SettingGetResponse(BaseModel): filter_prompt: Optional[str] = FieldInfo(alias="filterPrompt", default=None) + github_client_id: Optional[str] = FieldInfo(alias="githubClientId", default=None) + + github_client_secret: Optional[str] = FieldInfo(alias="githubClientSecret", default=None) + + github_custom_key_enabled: Optional[bool] = FieldInfo(alias="githubCustomKeyEnabled", default=None) + google_drive_client_id: Optional[str] = FieldInfo(alias="googleDriveClientId", default=None) google_drive_client_secret: Optional[str] = FieldInfo(alias="googleDriveClientSecret", default=None) diff --git a/src/supermemory/types/setting_update_params.py b/src/supermemory/types/setting_update_params.py index 6a252277..36ea3c60 100644 --- a/src/supermemory/types/setting_update_params.py +++ b/src/supermemory/types/setting_update_params.py @@ -19,6 +19,12 @@ class SettingUpdateParams(TypedDict, total=False): filter_prompt: Annotated[Optional[str], PropertyInfo(alias="filterPrompt")] + github_client_id: Annotated[Optional[str], PropertyInfo(alias="githubClientId")] + + github_client_secret: Annotated[Optional[str], PropertyInfo(alias="githubClientSecret")] + + github_custom_key_enabled: Annotated[Optional[bool], PropertyInfo(alias="githubCustomKeyEnabled")] + google_drive_client_id: Annotated[Optional[str], PropertyInfo(alias="googleDriveClientId")] google_drive_client_secret: Annotated[Optional[str], PropertyInfo(alias="googleDriveClientSecret")] diff --git a/src/supermemory/types/setting_update_response.py b/src/supermemory/types/setting_update_response.py index b2f70791..7fd1e58c 100644 --- a/src/supermemory/types/setting_update_response.py +++ b/src/supermemory/types/setting_update_response.py @@ -18,6 +18,12 @@ class Updated(BaseModel): filter_prompt: Optional[str] = FieldInfo(alias="filterPrompt", default=None) + github_client_id: Optional[str] = FieldInfo(alias="githubClientId", default=None) + + github_client_secret: Optional[str] = FieldInfo(alias="githubClientSecret", default=None) + + github_custom_key_enabled: Optional[bool] = FieldInfo(alias="githubCustomKeyEnabled", default=None) + google_drive_client_id: Optional[str] = FieldInfo(alias="googleDriveClientId", default=None) google_drive_client_secret: Optional[str] = FieldInfo(alias="googleDriveClientSecret", default=None) diff --git a/tests/api_resources/test_settings.py b/tests/api_resources/test_settings.py index 3b9f98e9..1ae0ac22 100644 --- a/tests/api_resources/test_settings.py +++ b/tests/api_resources/test_settings.py @@ -30,6 +30,9 @@ def test_method_update_with_all_params(self, client: Supermemory) -> None: chunk_size=-2147483648, exclude_items="string", filter_prompt="filterPrompt", + github_client_id="githubClientId", + github_client_secret="githubClientSecret", + github_custom_key_enabled=True, google_drive_client_id="googleDriveClientId", google_drive_client_secret="googleDriveClientSecret", google_drive_custom_key_enabled=True, @@ -113,6 +116,9 @@ async def test_method_update_with_all_params(self, async_client: AsyncSupermemor chunk_size=-2147483648, exclude_items="string", filter_prompt="filterPrompt", + github_client_id="githubClientId", + github_client_secret="githubClientSecret", + github_custom_key_enabled=True, google_drive_client_id="googleDriveClientId", google_drive_client_secret="googleDriveClientSecret", google_drive_custom_key_enabled=True, From 005241b5a183614dc5968458e9fd6d6a6e9d6ef7 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 5 Dec 2025 23:36:35 +0000 Subject: [PATCH 06/11] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index fe8a4066..81b59d84 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 19 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/supermemory--inc%2Fsupermemory-new-16b01076f65e994ba603cd91ce14ba5a2920d0d5b27b8c84d60b8a7fcf57900b.yml -openapi_spec_hash: bfaa2815be92e4baad6118efc09b606d +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/supermemory--inc%2Fsupermemory-new-fd5dd581776ee4e279cbb53653add9b3e827509993736dc57ceacb014b2c8d58.yml +openapi_spec_hash: 447ba1829dbe51bc95f558abe96b8a12 config_hash: ec08a36e60458b4d83e71798a8043484 From 575c09e064317c9e86cbe52cc87655f3d9690776 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 9 Dec 2025 01:22:47 +0000 Subject: [PATCH 07/11] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 81b59d84..f9adb98b 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 19 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/supermemory--inc%2Fsupermemory-new-fd5dd581776ee4e279cbb53653add9b3e827509993736dc57ceacb014b2c8d58.yml -openapi_spec_hash: 447ba1829dbe51bc95f558abe96b8a12 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/supermemory--inc%2Fsupermemory-new-3856961a8959b60dbf0b6912d69c23390fbbca1c5e31028dd0decc85234dd285.yml +openapi_spec_hash: 988a7d6d7cef81ceb4acda3ef9f8c21e config_hash: ec08a36e60458b4d83e71798a8043484 From 4c1365d2bc02b2d81eb99ef93ac56f1f1b6aba3c Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 9 Dec 2025 04:16:36 +0000 Subject: [PATCH 08/11] fix(types): allow pyright to infer TypedDict types within SequenceNotStr --- src/supermemory/_types.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/supermemory/_types.py b/src/supermemory/_types.py index 9a02d83d..ca39616a 100644 --- a/src/supermemory/_types.py +++ b/src/supermemory/_types.py @@ -243,6 +243,9 @@ class HttpxSendArgs(TypedDict, total=False): if TYPE_CHECKING: # This works because str.__contains__ does not accept object (either in typeshed or at runtime) # https://github.com/hauntsaninja/useful_types/blob/5e9710f3875107d068e7679fd7fec9cfab0eff3b/useful_types/__init__.py#L285 + # + # Note: index() and count() methods are intentionally omitted to allow pyright to properly + # infer TypedDict types when dict literals are used in lists assigned to SequenceNotStr. class SequenceNotStr(Protocol[_T_co]): @overload def __getitem__(self, index: SupportsIndex, /) -> _T_co: ... @@ -251,8 +254,6 @@ def __getitem__(self, index: slice, /) -> Sequence[_T_co]: ... def __contains__(self, value: object, /) -> bool: ... def __len__(self) -> int: ... def __iter__(self) -> Iterator[_T_co]: ... - def index(self, value: Any, start: int = 0, stop: int = ..., /) -> int: ... - def count(self, value: Any, /) -> int: ... def __reversed__(self) -> Iterator[_T_co]: ... else: # just point this to a normal `Sequence` at runtime to avoid having to special case From bdb32bc4a13ae9a73409f827082a1c5637dc9860 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 9 Dec 2025 04:18:44 +0000 Subject: [PATCH 09/11] chore: add missing docstrings --- src/supermemory/types/document_get_response.py | 2 ++ src/supermemory/types/document_list_params.py | 4 ++++ src/supermemory/types/document_list_response.py | 4 ++++ src/supermemory/types/memory_get_response.py | 2 ++ src/supermemory/types/memory_list_params.py | 4 ++++ src/supermemory/types/memory_list_response.py | 4 ++++ src/supermemory/types/profile_response.py | 2 ++ src/supermemory/types/search_documents_params.py | 4 ++++ src/supermemory/types/search_documents_response.py | 2 ++ src/supermemory/types/search_execute_params.py | 4 ++++ src/supermemory/types/search_execute_response.py | 2 ++ src/supermemory/types/search_memories_params.py | 4 ++++ src/supermemory/types/search_memories_response.py | 2 ++ 13 files changed, 40 insertions(+) diff --git a/src/supermemory/types/document_get_response.py b/src/supermemory/types/document_get_response.py index e8e2bc37..ac7138a2 100644 --- a/src/supermemory/types/document_get_response.py +++ b/src/supermemory/types/document_get_response.py @@ -11,6 +11,8 @@ class DocumentGetResponse(BaseModel): + """Document object""" + id: str """Unique identifier of the document.""" diff --git a/src/supermemory/types/document_list_params.py b/src/supermemory/types/document_list_params.py index 3b62b1a6..c3aab05b 100644 --- a/src/supermemory/types/document_list_params.py +++ b/src/supermemory/types/document_list_params.py @@ -42,10 +42,14 @@ class DocumentListParams(TypedDict, total=False): class FiltersOr(TypedDict, total=False): + """OR""" + or_: Required[Annotated[Iterable[object], PropertyInfo(alias="OR")]] class FiltersAnd(TypedDict, total=False): + """AND""" + and_: Required[Annotated[Iterable[object], PropertyInfo(alias="AND")]] diff --git a/src/supermemory/types/document_list_response.py b/src/supermemory/types/document_list_response.py index 6e34ee17..46cc95ac 100644 --- a/src/supermemory/types/document_list_response.py +++ b/src/supermemory/types/document_list_response.py @@ -79,6 +79,8 @@ class Memory(BaseModel): class Pagination(BaseModel): + """Pagination metadata""" + current_page: float = FieldInfo(alias="currentPage") total_items: float = FieldInfo(alias="totalItems") @@ -89,6 +91,8 @@ class Pagination(BaseModel): class DocumentListResponse(BaseModel): + """List of documents""" + memories: List[Memory] pagination: Pagination diff --git a/src/supermemory/types/memory_get_response.py b/src/supermemory/types/memory_get_response.py index 99610a0a..a8ec8b0c 100644 --- a/src/supermemory/types/memory_get_response.py +++ b/src/supermemory/types/memory_get_response.py @@ -11,6 +11,8 @@ class MemoryGetResponse(BaseModel): + """Document object""" + id: str """Unique identifier of the document.""" diff --git a/src/supermemory/types/memory_list_params.py b/src/supermemory/types/memory_list_params.py index 48eb7f6d..31ac58f4 100644 --- a/src/supermemory/types/memory_list_params.py +++ b/src/supermemory/types/memory_list_params.py @@ -42,10 +42,14 @@ class MemoryListParams(TypedDict, total=False): class FiltersOr(TypedDict, total=False): + """OR""" + or_: Required[Annotated[Iterable[object], PropertyInfo(alias="OR")]] class FiltersAnd(TypedDict, total=False): + """AND""" + and_: Required[Annotated[Iterable[object], PropertyInfo(alias="AND")]] diff --git a/src/supermemory/types/memory_list_response.py b/src/supermemory/types/memory_list_response.py index 9502bba1..d6dbc697 100644 --- a/src/supermemory/types/memory_list_response.py +++ b/src/supermemory/types/memory_list_response.py @@ -79,6 +79,8 @@ class Memory(BaseModel): class Pagination(BaseModel): + """Pagination metadata""" + current_page: float = FieldInfo(alias="currentPage") total_items: float = FieldInfo(alias="totalItems") @@ -89,6 +91,8 @@ class Pagination(BaseModel): class MemoryListResponse(BaseModel): + """List of documents""" + memories: List[Memory] pagination: Pagination diff --git a/src/supermemory/types/profile_response.py b/src/supermemory/types/profile_response.py index 3eb5e2b0..0dc06cc0 100644 --- a/src/supermemory/types/profile_response.py +++ b/src/supermemory/types/profile_response.py @@ -18,6 +18,8 @@ class Profile(BaseModel): class SearchResults(BaseModel): + """Search results if a search query was provided""" + results: List[object] """Search results for the provided query""" diff --git a/src/supermemory/types/search_documents_params.py b/src/supermemory/types/search_documents_params.py index 2b88d453..441ce04e 100644 --- a/src/supermemory/types/search_documents_params.py +++ b/src/supermemory/types/search_documents_params.py @@ -83,10 +83,14 @@ class SearchDocumentsParams(TypedDict, total=False): class FiltersOr(TypedDict, total=False): + """OR""" + or_: Required[Annotated[Iterable[object], PropertyInfo(alias="OR")]] class FiltersAnd(TypedDict, total=False): + """AND""" + and_: Required[Annotated[Iterable[object], PropertyInfo(alias="AND")]] diff --git a/src/supermemory/types/search_documents_response.py b/src/supermemory/types/search_documents_response.py index a8769aec..61bba12e 100644 --- a/src/supermemory/types/search_documents_response.py +++ b/src/supermemory/types/search_documents_response.py @@ -10,6 +10,8 @@ class ResultChunk(BaseModel): + """Matching content chunk""" + content: str """Content of the matching chunk""" diff --git a/src/supermemory/types/search_execute_params.py b/src/supermemory/types/search_execute_params.py index 9a49c448..0a1290a6 100644 --- a/src/supermemory/types/search_execute_params.py +++ b/src/supermemory/types/search_execute_params.py @@ -83,10 +83,14 @@ class SearchExecuteParams(TypedDict, total=False): class FiltersOr(TypedDict, total=False): + """OR""" + or_: Required[Annotated[Iterable[object], PropertyInfo(alias="OR")]] class FiltersAnd(TypedDict, total=False): + """AND""" + and_: Required[Annotated[Iterable[object], PropertyInfo(alias="AND")]] diff --git a/src/supermemory/types/search_execute_response.py b/src/supermemory/types/search_execute_response.py index c6518999..14e06e97 100644 --- a/src/supermemory/types/search_execute_response.py +++ b/src/supermemory/types/search_execute_response.py @@ -10,6 +10,8 @@ class ResultChunk(BaseModel): + """Matching content chunk""" + content: str """Content of the matching chunk""" diff --git a/src/supermemory/types/search_memories_params.py b/src/supermemory/types/search_memories_params.py index e41511a7..1d5320b2 100644 --- a/src/supermemory/types/search_memories_params.py +++ b/src/supermemory/types/search_memories_params.py @@ -50,10 +50,14 @@ class SearchMemoriesParams(TypedDict, total=False): class FiltersOr(TypedDict, total=False): + """OR""" + or_: Required[Annotated[Iterable[object], PropertyInfo(alias="OR")]] class FiltersAnd(TypedDict, total=False): + """AND""" + and_: Required[Annotated[Iterable[object], PropertyInfo(alias="AND")]] diff --git a/src/supermemory/types/search_memories_response.py b/src/supermemory/types/search_memories_response.py index cc0e6896..70c725df 100644 --- a/src/supermemory/types/search_memories_response.py +++ b/src/supermemory/types/search_memories_response.py @@ -73,6 +73,8 @@ class ResultContextParent(BaseModel): class ResultContext(BaseModel): + """Object containing arrays of parent and child contextual memories""" + children: Optional[List[ResultContextChild]] = None parents: Optional[List[ResultContextParent]] = None From 96b34d0a371e0d9c9866e1dbbfd70329d9400850 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 9 Dec 2025 21:09:25 +0000 Subject: [PATCH 10/11] feat(api): manual updates --- .stats.yml | 4 +- README.md | 2 +- api.md | 17 +- src/supermemory/_client.py | 155 ++++++++++- src/supermemory/resources/connections.py | 228 +++++++++++++-- src/supermemory/resources/documents.py | 107 +++++++- src/supermemory/resources/memories.py | 259 +++++++++++++++++- src/supermemory/types/__init__.py | 16 +- src/supermemory/types/client_search_params.py | 85 ++++++ .../types/connection_configure_params.py | 12 + .../types/connection_configure_response.py | 17 ++ ...ams.py => connection_get_by_tag_params.py} | 4 +- ...e.py => connection_get_by_tag_response.py} | 4 +- .../types/connection_resources_params.py | 13 + .../types/connection_resources_response.py | 13 + .../types/document_delete_bulk_params.py | 18 ++ .../types/document_delete_bulk_response.py | 37 +++ src/supermemory/types/memory_forget_params.py | 26 ++ .../types/memory_forget_response.py | 15 + .../types/memory_update_memory_params.py | 31 +++ .../types/memory_update_memory_response.py | 31 +++ src/supermemory/types/search_response.py | 143 ++++++++++ tests/api_resources/test_client.py | 116 +++++++- tests/api_resources/test_connections.py | 236 ++++++++++++++-- tests/api_resources/test_documents.py | 75 +++++ tests/api_resources/test_memories.py | 190 +++++++++++++ 26 files changed, 1800 insertions(+), 54 deletions(-) create mode 100644 src/supermemory/types/client_search_params.py create mode 100644 src/supermemory/types/connection_configure_params.py create mode 100644 src/supermemory/types/connection_configure_response.py rename src/supermemory/types/{connection_get_by_tags_params.py => connection_get_by_tag_params.py} (81%) rename src/supermemory/types/{connection_get_by_tags_response.py => connection_get_by_tag_response.py} (85%) create mode 100644 src/supermemory/types/connection_resources_params.py create mode 100644 src/supermemory/types/connection_resources_response.py create mode 100644 src/supermemory/types/document_delete_bulk_params.py create mode 100644 src/supermemory/types/document_delete_bulk_response.py create mode 100644 src/supermemory/types/memory_forget_params.py create mode 100644 src/supermemory/types/memory_forget_response.py create mode 100644 src/supermemory/types/memory_update_memory_params.py create mode 100644 src/supermemory/types/memory_update_memory_response.py create mode 100644 src/supermemory/types/search_response.py diff --git a/.stats.yml b/.stats.yml index f9adb98b..b53bbe45 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 19 +configured_endpoints: 24 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/supermemory--inc%2Fsupermemory-new-3856961a8959b60dbf0b6912d69c23390fbbca1c5e31028dd0decc85234dd285.yml openapi_spec_hash: 988a7d6d7cef81ceb4acda3ef9f8c21e -config_hash: ec08a36e60458b4d83e71798a8043484 +config_hash: 7dd9730225e87663fd62814dcfe62ba7 diff --git a/README.md b/README.md index f634745d..23c3f8c0 100644 --- a/README.md +++ b/README.md @@ -121,7 +121,7 @@ from supermemory import Supermemory client = Supermemory() -response = client.search.memories( +response = client.search( q="machine learning concepts", include={}, ) diff --git a/api.md b/api.md index 43179a60..aca2b3b7 100644 --- a/api.md +++ b/api.md @@ -3,13 +3,14 @@ Types: ```python -from supermemory.types import AddResponse, ProfileResponse +from supermemory.types import AddResponse, ProfileResponse, SearchResponse ``` Methods: - client.add(\*\*params) -> AddResponse - client.profile(\*\*params) -> ProfileResponse +- client.search(\*\*params) -> SearchResponse # Memories @@ -20,7 +21,9 @@ from supermemory.types import ( MemoryUpdateResponse, MemoryListResponse, MemoryAddResponse, + MemoryForgetResponse, MemoryGetResponse, + MemoryUpdateMemoryResponse, MemoryUploadFileResponse, ) ``` @@ -31,7 +34,9 @@ Methods: - client.memories.list(\*\*params) -> MemoryListResponse - client.memories.delete(id) -> None - client.memories.add(\*\*params) -> MemoryAddResponse +- client.memories.forget(\*\*params) -> MemoryForgetResponse - client.memories.get(id) -> MemoryGetResponse +- client.memories.update_memory(\*\*params) -> MemoryUpdateMemoryResponse - client.memories.upload_file(\*\*params) -> MemoryUploadFileResponse # Documents @@ -43,6 +48,7 @@ from supermemory.types import ( DocumentUpdateResponse, DocumentListResponse, DocumentAddResponse, + DocumentDeleteBulkResponse, DocumentGetResponse, DocumentUploadFileResponse, ) @@ -54,6 +60,7 @@ Methods: - client.documents.list(\*\*params) -> DocumentListResponse - client.documents.delete(id) -> None - client.documents.add(\*\*params) -> DocumentAddResponse +- client.documents.delete_bulk(\*\*params) -> DocumentDeleteBulkResponse - client.documents.get(id) -> DocumentGetResponse - client.documents.upload_file(\*\*params) -> DocumentUploadFileResponse @@ -92,12 +99,14 @@ Types: from supermemory.types import ( ConnectionCreateResponse, ConnectionListResponse, + ConnectionConfigureResponse, ConnectionDeleteByIDResponse, ConnectionDeleteByProviderResponse, ConnectionGetByIDResponse, - ConnectionGetByTagsResponse, + ConnectionGetByTagResponse, ConnectionImportResponse, ConnectionListDocumentsResponse, + ConnectionResourcesResponse, ) ``` @@ -105,9 +114,11 @@ Methods: - client.connections.create(provider, \*\*params) -> ConnectionCreateResponse - client.connections.list(\*\*params) -> ConnectionListResponse +- client.connections.configure(connection_id, \*\*params) -> ConnectionConfigureResponse - client.connections.delete_by_id(connection_id) -> ConnectionDeleteByIDResponse - client.connections.delete_by_provider(provider, \*\*params) -> ConnectionDeleteByProviderResponse - client.connections.get_by_id(connection_id) -> ConnectionGetByIDResponse -- client.connections.get_by_tags(provider, \*\*params) -> ConnectionGetByTagsResponse +- client.connections.get_by_tag(provider, \*\*params) -> ConnectionGetByTagResponse - client.connections.import\_(provider, \*\*params) -> str - client.connections.list_documents(provider, \*\*params) -> ConnectionListDocumentsResponse +- client.connections.resources(connection_id, \*\*params) -> ConnectionResourcesResponse diff --git a/src/supermemory/_client.py b/src/supermemory/_client.py index e9070a3b..044da7dc 100644 --- a/src/supermemory/_client.py +++ b/src/supermemory/_client.py @@ -10,7 +10,7 @@ from . import _exceptions from ._qs import Querystring -from .types import client_add_params, client_profile_params +from .types import client_add_params, client_search_params, client_profile_params from ._types import ( Body, Omit, @@ -48,6 +48,7 @@ make_request_options, ) from .types.add_response import AddResponse +from .types.search_response import SearchResponse from .types.profile_response import ProfileResponse __all__ = [ @@ -304,6 +305,76 @@ def profile( cast_to=ProfileResponse, ) + def search( + self, + *, + q: str, + container_tag: str | Omit = omit, + filters: client_search_params.Filters | Omit = omit, + include: client_search_params.Include | Omit = omit, + limit: int | Omit = omit, + rerank: bool | Omit = omit, + rewrite_query: bool | Omit = omit, + threshold: float | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SearchResponse: + """ + Search memory entries - Low latency for conversational + + Args: + q: Search query string + + container_tag: Optional tag this search should be containerized by. This can be an ID for your + user, a project ID, or any other identifier you wish to use to filter memories. + + filters: Optional filters to apply to the search. Can be a JSON string or Query object. + + limit: Maximum number of results to return + + rerank: If true, rerank the results based on the query. This is helpful if you want to + ensure the most relevant results are returned. + + rewrite_query: If true, rewrites the query to make it easier to find documents. This increases + the latency by about 400ms + + threshold: Threshold / sensitivity for memories selection. 0 is least sensitive (returns + most memories, more results), 1 is most sensitive (returns lesser memories, + accurate results) + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self.post( + "/v4/search", + body=maybe_transform( + { + "q": q, + "container_tag": container_tag, + "filters": filters, + "include": include, + "limit": limit, + "rerank": rerank, + "rewrite_query": rewrite_query, + "threshold": threshold, + }, + client_search_params.ClientSearchParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=SearchResponse, + ) + @override def _make_status_error( self, @@ -580,6 +651,76 @@ async def profile( cast_to=ProfileResponse, ) + async def search( + self, + *, + q: str, + container_tag: str | Omit = omit, + filters: client_search_params.Filters | Omit = omit, + include: client_search_params.Include | Omit = omit, + limit: int | Omit = omit, + rerank: bool | Omit = omit, + rewrite_query: bool | Omit = omit, + threshold: float | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SearchResponse: + """ + Search memory entries - Low latency for conversational + + Args: + q: Search query string + + container_tag: Optional tag this search should be containerized by. This can be an ID for your + user, a project ID, or any other identifier you wish to use to filter memories. + + filters: Optional filters to apply to the search. Can be a JSON string or Query object. + + limit: Maximum number of results to return + + rerank: If true, rerank the results based on the query. This is helpful if you want to + ensure the most relevant results are returned. + + rewrite_query: If true, rewrites the query to make it easier to find documents. This increases + the latency by about 400ms + + threshold: Threshold / sensitivity for memories selection. 0 is least sensitive (returns + most memories, more results), 1 is most sensitive (returns lesser memories, + accurate results) + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self.post( + "/v4/search", + body=await async_maybe_transform( + { + "q": q, + "container_tag": container_tag, + "filters": filters, + "include": include, + "limit": limit, + "rerank": rerank, + "rewrite_query": rewrite_query, + "threshold": threshold, + }, + client_search_params.ClientSearchParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=SearchResponse, + ) + @override def _make_status_error( self, @@ -628,6 +769,9 @@ def __init__(self, client: Supermemory) -> None: self.profile = to_raw_response_wrapper( client.profile, ) + self.search = to_raw_response_wrapper( + client.search, + ) class AsyncSupermemoryWithRawResponse: @@ -644,6 +788,9 @@ def __init__(self, client: AsyncSupermemory) -> None: self.profile = async_to_raw_response_wrapper( client.profile, ) + self.search = async_to_raw_response_wrapper( + client.search, + ) class SupermemoryWithStreamedResponse: @@ -660,6 +807,9 @@ def __init__(self, client: Supermemory) -> None: self.profile = to_streamed_response_wrapper( client.profile, ) + self.search = to_streamed_response_wrapper( + client.search, + ) class AsyncSupermemoryWithStreamedResponse: @@ -676,6 +826,9 @@ def __init__(self, client: AsyncSupermemory) -> None: self.profile = async_to_streamed_response_wrapper( client.profile, ) + self.search = async_to_streamed_response_wrapper( + client.search, + ) Client = Supermemory diff --git a/src/supermemory/resources/connections.py b/src/supermemory/resources/connections.py index f6dc1b75..7c72d927 100644 --- a/src/supermemory/resources/connections.py +++ b/src/supermemory/resources/connections.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import Dict, Union, Optional +from typing import Dict, Union, Iterable, Optional from typing_extensions import Literal import httpx @@ -11,7 +11,9 @@ connection_list_params, connection_create_params, connection_import_params, - connection_get_by_tags_params, + connection_configure_params, + connection_resources_params, + connection_get_by_tag_params, connection_list_documents_params, connection_delete_by_provider_params, ) @@ -28,8 +30,10 @@ from .._base_client import make_request_options from ..types.connection_list_response import ConnectionListResponse from ..types.connection_create_response import ConnectionCreateResponse +from ..types.connection_configure_response import ConnectionConfigureResponse from ..types.connection_get_by_id_response import ConnectionGetByIDResponse -from ..types.connection_get_by_tags_response import ConnectionGetByTagsResponse +from ..types.connection_resources_response import ConnectionResourcesResponse +from ..types.connection_get_by_tag_response import ConnectionGetByTagResponse from ..types.connection_delete_by_id_response import ConnectionDeleteByIDResponse from ..types.connection_list_documents_response import ConnectionListDocumentsResponse from ..types.connection_delete_by_provider_response import ConnectionDeleteByProviderResponse @@ -137,6 +141,41 @@ def list( cast_to=ConnectionListResponse, ) + def configure( + self, + connection_id: str, + *, + resources: Iterable[Dict[str, object]], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ConnectionConfigureResponse: + """ + Configure resources for a connection (supported providers: GitHub for now) + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not connection_id: + raise ValueError(f"Expected a non-empty value for `connection_id` but received {connection_id!r}") + return self._post( + f"/v3/connections/{connection_id}/configure", + body=maybe_transform({"resources": resources}, connection_configure_params.ConnectionConfigureParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ConnectionConfigureResponse, + ) + def delete_by_id( self, connection_id: str, @@ -243,7 +282,7 @@ def get_by_id( cast_to=ConnectionGetByIDResponse, ) - def get_by_tags( + def get_by_tag( self, provider: Literal["notion", "google-drive", "onedrive", "github", "web-crawler"], *, @@ -254,7 +293,7 @@ def get_by_tags( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ConnectionGetByTagsResponse: + ) -> ConnectionGetByTagResponse: """ Get connection details with provider and container tags @@ -274,12 +313,12 @@ def get_by_tags( return self._post( f"/v3/connections/{provider}/connection", body=maybe_transform( - {"container_tags": container_tags}, connection_get_by_tags_params.ConnectionGetByTagsParams + {"container_tags": container_tags}, connection_get_by_tag_params.ConnectionGetByTagParams ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=ConnectionGetByTagsResponse, + cast_to=ConnectionGetByTagResponse, ) def import_( @@ -359,6 +398,51 @@ def list_documents( cast_to=ConnectionListDocumentsResponse, ) + def resources( + self, + connection_id: str, + *, + page: float | Omit = omit, + per_page: float | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ConnectionResourcesResponse: + """ + Fetch resources for a connection (supported providers: GitHub for now) + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not connection_id: + raise ValueError(f"Expected a non-empty value for `connection_id` but received {connection_id!r}") + return self._get( + f"/v3/connections/{connection_id}/resources", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "page": page, + "per_page": per_page, + }, + connection_resources_params.ConnectionResourcesParams, + ), + ), + cast_to=ConnectionResourcesResponse, + ) + class AsyncConnectionsResource(AsyncAPIResource): @cached_property @@ -462,6 +546,43 @@ async def list( cast_to=ConnectionListResponse, ) + async def configure( + self, + connection_id: str, + *, + resources: Iterable[Dict[str, object]], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ConnectionConfigureResponse: + """ + Configure resources for a connection (supported providers: GitHub for now) + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not connection_id: + raise ValueError(f"Expected a non-empty value for `connection_id` but received {connection_id!r}") + return await self._post( + f"/v3/connections/{connection_id}/configure", + body=await async_maybe_transform( + {"resources": resources}, connection_configure_params.ConnectionConfigureParams + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ConnectionConfigureResponse, + ) + async def delete_by_id( self, connection_id: str, @@ -568,7 +689,7 @@ async def get_by_id( cast_to=ConnectionGetByIDResponse, ) - async def get_by_tags( + async def get_by_tag( self, provider: Literal["notion", "google-drive", "onedrive", "github", "web-crawler"], *, @@ -579,7 +700,7 @@ async def get_by_tags( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ConnectionGetByTagsResponse: + ) -> ConnectionGetByTagResponse: """ Get connection details with provider and container tags @@ -599,12 +720,12 @@ async def get_by_tags( return await self._post( f"/v3/connections/{provider}/connection", body=await async_maybe_transform( - {"container_tags": container_tags}, connection_get_by_tags_params.ConnectionGetByTagsParams + {"container_tags": container_tags}, connection_get_by_tag_params.ConnectionGetByTagParams ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=ConnectionGetByTagsResponse, + cast_to=ConnectionGetByTagResponse, ) async def import_( @@ -686,6 +807,51 @@ async def list_documents( cast_to=ConnectionListDocumentsResponse, ) + async def resources( + self, + connection_id: str, + *, + page: float | Omit = omit, + per_page: float | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ConnectionResourcesResponse: + """ + Fetch resources for a connection (supported providers: GitHub for now) + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not connection_id: + raise ValueError(f"Expected a non-empty value for `connection_id` but received {connection_id!r}") + return await self._get( + f"/v3/connections/{connection_id}/resources", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "page": page, + "per_page": per_page, + }, + connection_resources_params.ConnectionResourcesParams, + ), + ), + cast_to=ConnectionResourcesResponse, + ) + class ConnectionsResourceWithRawResponse: def __init__(self, connections: ConnectionsResource) -> None: @@ -697,6 +863,9 @@ def __init__(self, connections: ConnectionsResource) -> None: self.list = to_raw_response_wrapper( connections.list, ) + self.configure = to_raw_response_wrapper( + connections.configure, + ) self.delete_by_id = to_raw_response_wrapper( connections.delete_by_id, ) @@ -706,8 +875,8 @@ def __init__(self, connections: ConnectionsResource) -> None: self.get_by_id = to_raw_response_wrapper( connections.get_by_id, ) - self.get_by_tags = to_raw_response_wrapper( - connections.get_by_tags, + self.get_by_tag = to_raw_response_wrapper( + connections.get_by_tag, ) self.import_ = to_raw_response_wrapper( connections.import_, @@ -715,6 +884,9 @@ def __init__(self, connections: ConnectionsResource) -> None: self.list_documents = to_raw_response_wrapper( connections.list_documents, ) + self.resources = to_raw_response_wrapper( + connections.resources, + ) class AsyncConnectionsResourceWithRawResponse: @@ -727,6 +899,9 @@ def __init__(self, connections: AsyncConnectionsResource) -> None: self.list = async_to_raw_response_wrapper( connections.list, ) + self.configure = async_to_raw_response_wrapper( + connections.configure, + ) self.delete_by_id = async_to_raw_response_wrapper( connections.delete_by_id, ) @@ -736,8 +911,8 @@ def __init__(self, connections: AsyncConnectionsResource) -> None: self.get_by_id = async_to_raw_response_wrapper( connections.get_by_id, ) - self.get_by_tags = async_to_raw_response_wrapper( - connections.get_by_tags, + self.get_by_tag = async_to_raw_response_wrapper( + connections.get_by_tag, ) self.import_ = async_to_raw_response_wrapper( connections.import_, @@ -745,6 +920,9 @@ def __init__(self, connections: AsyncConnectionsResource) -> None: self.list_documents = async_to_raw_response_wrapper( connections.list_documents, ) + self.resources = async_to_raw_response_wrapper( + connections.resources, + ) class ConnectionsResourceWithStreamingResponse: @@ -757,6 +935,9 @@ def __init__(self, connections: ConnectionsResource) -> None: self.list = to_streamed_response_wrapper( connections.list, ) + self.configure = to_streamed_response_wrapper( + connections.configure, + ) self.delete_by_id = to_streamed_response_wrapper( connections.delete_by_id, ) @@ -766,8 +947,8 @@ def __init__(self, connections: ConnectionsResource) -> None: self.get_by_id = to_streamed_response_wrapper( connections.get_by_id, ) - self.get_by_tags = to_streamed_response_wrapper( - connections.get_by_tags, + self.get_by_tag = to_streamed_response_wrapper( + connections.get_by_tag, ) self.import_ = to_streamed_response_wrapper( connections.import_, @@ -775,6 +956,9 @@ def __init__(self, connections: ConnectionsResource) -> None: self.list_documents = to_streamed_response_wrapper( connections.list_documents, ) + self.resources = to_streamed_response_wrapper( + connections.resources, + ) class AsyncConnectionsResourceWithStreamingResponse: @@ -787,6 +971,9 @@ def __init__(self, connections: AsyncConnectionsResource) -> None: self.list = async_to_streamed_response_wrapper( connections.list, ) + self.configure = async_to_streamed_response_wrapper( + connections.configure, + ) self.delete_by_id = async_to_streamed_response_wrapper( connections.delete_by_id, ) @@ -796,8 +983,8 @@ def __init__(self, connections: AsyncConnectionsResource) -> None: self.get_by_id = async_to_streamed_response_wrapper( connections.get_by_id, ) - self.get_by_tags = async_to_streamed_response_wrapper( - connections.get_by_tags, + self.get_by_tag = async_to_streamed_response_wrapper( + connections.get_by_tag, ) self.import_ = async_to_streamed_response_wrapper( connections.import_, @@ -805,3 +992,6 @@ def __init__(self, connections: AsyncConnectionsResource) -> None: self.list_documents = async_to_streamed_response_wrapper( connections.list_documents, ) + self.resources = async_to_streamed_response_wrapper( + connections.resources, + ) diff --git a/src/supermemory/resources/documents.py b/src/supermemory/resources/documents.py index 638be972..6ae976a1 100644 --- a/src/supermemory/resources/documents.py +++ b/src/supermemory/resources/documents.py @@ -7,7 +7,13 @@ import httpx -from ..types import document_add_params, document_list_params, document_update_params, document_upload_file_params +from ..types import ( + document_add_params, + document_list_params, + document_update_params, + document_delete_bulk_params, + document_upload_file_params, +) from .._types import Body, Omit, Query, Headers, NoneType, NotGiven, FileTypes, SequenceNotStr, omit, not_given from .._utils import extract_files, maybe_transform, deepcopy_minimal, async_maybe_transform from .._compat import cached_property @@ -23,6 +29,7 @@ from ..types.document_get_response import DocumentGetResponse from ..types.document_list_response import DocumentListResponse from ..types.document_update_response import DocumentUpdateResponse +from ..types.document_delete_bulk_response import DocumentDeleteBulkResponse from ..types.document_upload_file_response import DocumentUploadFileResponse __all__ = ["DocumentsResource", "AsyncDocumentsResource"] @@ -278,6 +285,49 @@ def add( cast_to=DocumentAddResponse, ) + def delete_bulk( + self, + *, + container_tags: SequenceNotStr[str] | Omit = omit, + ids: SequenceNotStr[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> DocumentDeleteBulkResponse: + """ + Bulk delete documents by IDs or container tags + + Args: + container_tags: Array of container tags - all documents in these containers will be deleted + + ids: Array of document IDs to delete (max 100 at once) + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._delete( + "/v3/documents/bulk", + body=maybe_transform( + { + "container_tags": container_tags, + "ids": ids, + }, + document_delete_bulk_params.DocumentDeleteBulkParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=DocumentDeleteBulkResponse, + ) + def get( self, id: str, @@ -631,6 +681,49 @@ async def add( cast_to=DocumentAddResponse, ) + async def delete_bulk( + self, + *, + container_tags: SequenceNotStr[str] | Omit = omit, + ids: SequenceNotStr[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> DocumentDeleteBulkResponse: + """ + Bulk delete documents by IDs or container tags + + Args: + container_tags: Array of container tags - all documents in these containers will be deleted + + ids: Array of document IDs to delete (max 100 at once) + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._delete( + "/v3/documents/bulk", + body=await async_maybe_transform( + { + "container_tags": container_tags, + "ids": ids, + }, + document_delete_bulk_params.DocumentDeleteBulkParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=DocumentDeleteBulkResponse, + ) + async def get( self, id: str, @@ -750,6 +843,9 @@ def __init__(self, documents: DocumentsResource) -> None: self.add = to_raw_response_wrapper( documents.add, ) + self.delete_bulk = to_raw_response_wrapper( + documents.delete_bulk, + ) self.get = to_raw_response_wrapper( documents.get, ) @@ -774,6 +870,9 @@ def __init__(self, documents: AsyncDocumentsResource) -> None: self.add = async_to_raw_response_wrapper( documents.add, ) + self.delete_bulk = async_to_raw_response_wrapper( + documents.delete_bulk, + ) self.get = async_to_raw_response_wrapper( documents.get, ) @@ -798,6 +897,9 @@ def __init__(self, documents: DocumentsResource) -> None: self.add = to_streamed_response_wrapper( documents.add, ) + self.delete_bulk = to_streamed_response_wrapper( + documents.delete_bulk, + ) self.get = to_streamed_response_wrapper( documents.get, ) @@ -822,6 +924,9 @@ def __init__(self, documents: AsyncDocumentsResource) -> None: self.add = async_to_streamed_response_wrapper( documents.add, ) + self.delete_bulk = async_to_streamed_response_wrapper( + documents.delete_bulk, + ) self.get = async_to_streamed_response_wrapper( documents.get, ) diff --git a/src/supermemory/resources/memories.py b/src/supermemory/resources/memories.py index 6a96e255..7779d258 100644 --- a/src/supermemory/resources/memories.py +++ b/src/supermemory/resources/memories.py @@ -7,7 +7,14 @@ import httpx -from ..types import memory_add_params, memory_list_params, memory_update_params, memory_upload_file_params +from ..types import ( + memory_add_params, + memory_list_params, + memory_forget_params, + memory_update_params, + memory_upload_file_params, + memory_update_memory_params, +) from .._types import Body, Omit, Query, Headers, NoneType, NotGiven, FileTypes, SequenceNotStr, omit, not_given from .._utils import extract_files, maybe_transform, deepcopy_minimal, async_maybe_transform from .._compat import cached_property @@ -22,8 +29,10 @@ from ..types.memory_add_response import MemoryAddResponse from ..types.memory_get_response import MemoryGetResponse from ..types.memory_list_response import MemoryListResponse +from ..types.memory_forget_response import MemoryForgetResponse from ..types.memory_update_response import MemoryUpdateResponse from ..types.memory_upload_file_response import MemoryUploadFileResponse +from ..types.memory_update_memory_response import MemoryUpdateMemoryResponse __all__ = ["MemoriesResource", "AsyncMemoriesResource"] @@ -278,6 +287,60 @@ def add( cast_to=MemoryAddResponse, ) + def forget( + self, + *, + container_tag: str, + id: str | Omit = omit, + content: str | Omit = omit, + reason: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> MemoryForgetResponse: + """Forget (soft delete) a memory entry. + + The memory is marked as forgotten but not + permanently deleted. + + Args: + container_tag: Container tag / space identifier. Required to scope the operation. + + id: ID of the memory entry to operate on + + content: Exact content match of the memory entry to operate on. Use this when you don't + have the ID. + + reason: Optional reason for forgetting this memory + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._delete( + "/v4/memories", + body=maybe_transform( + { + "container_tag": container_tag, + "id": id, + "content": content, + "reason": reason, + }, + memory_forget_params.MemoryForgetParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=MemoryForgetResponse, + ) + def get( self, id: str, @@ -311,6 +374,64 @@ def get( cast_to=MemoryGetResponse, ) + def update_memory( + self, + *, + container_tag: str, + new_content: str, + id: str | Omit = omit, + content: str | Omit = omit, + metadata: Dict[str, Union[str, float, bool, SequenceNotStr[str]]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> MemoryUpdateMemoryResponse: + """Update a memory by creating a new version. + + The original memory is preserved with + isLatest=false. + + Args: + container_tag: Container tag / space identifier. Required to scope the operation. + + new_content: The new content that will replace the existing memory + + id: ID of the memory entry to operate on + + content: Exact content match of the memory entry to operate on. Use this when you don't + have the ID. + + metadata: Optional metadata. If not provided, inherits from the previous version. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._patch( + "/v4/memories", + body=maybe_transform( + { + "container_tag": container_tag, + "new_content": new_content, + "id": id, + "content": content, + "metadata": metadata, + }, + memory_update_memory_params.MemoryUpdateMemoryParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=MemoryUpdateMemoryResponse, + ) + def upload_file( self, *, @@ -631,6 +752,60 @@ async def add( cast_to=MemoryAddResponse, ) + async def forget( + self, + *, + container_tag: str, + id: str | Omit = omit, + content: str | Omit = omit, + reason: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> MemoryForgetResponse: + """Forget (soft delete) a memory entry. + + The memory is marked as forgotten but not + permanently deleted. + + Args: + container_tag: Container tag / space identifier. Required to scope the operation. + + id: ID of the memory entry to operate on + + content: Exact content match of the memory entry to operate on. Use this when you don't + have the ID. + + reason: Optional reason for forgetting this memory + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._delete( + "/v4/memories", + body=await async_maybe_transform( + { + "container_tag": container_tag, + "id": id, + "content": content, + "reason": reason, + }, + memory_forget_params.MemoryForgetParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=MemoryForgetResponse, + ) + async def get( self, id: str, @@ -664,6 +839,64 @@ async def get( cast_to=MemoryGetResponse, ) + async def update_memory( + self, + *, + container_tag: str, + new_content: str, + id: str | Omit = omit, + content: str | Omit = omit, + metadata: Dict[str, Union[str, float, bool, SequenceNotStr[str]]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> MemoryUpdateMemoryResponse: + """Update a memory by creating a new version. + + The original memory is preserved with + isLatest=false. + + Args: + container_tag: Container tag / space identifier. Required to scope the operation. + + new_content: The new content that will replace the existing memory + + id: ID of the memory entry to operate on + + content: Exact content match of the memory entry to operate on. Use this when you don't + have the ID. + + metadata: Optional metadata. If not provided, inherits from the previous version. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._patch( + "/v4/memories", + body=await async_maybe_transform( + { + "container_tag": container_tag, + "new_content": new_content, + "id": id, + "content": content, + "metadata": metadata, + }, + memory_update_memory_params.MemoryUpdateMemoryParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=MemoryUpdateMemoryResponse, + ) + async def upload_file( self, *, @@ -750,9 +983,15 @@ def __init__(self, memories: MemoriesResource) -> None: self.add = to_raw_response_wrapper( memories.add, ) + self.forget = to_raw_response_wrapper( + memories.forget, + ) self.get = to_raw_response_wrapper( memories.get, ) + self.update_memory = to_raw_response_wrapper( + memories.update_memory, + ) self.upload_file = to_raw_response_wrapper( memories.upload_file, ) @@ -774,9 +1013,15 @@ def __init__(self, memories: AsyncMemoriesResource) -> None: self.add = async_to_raw_response_wrapper( memories.add, ) + self.forget = async_to_raw_response_wrapper( + memories.forget, + ) self.get = async_to_raw_response_wrapper( memories.get, ) + self.update_memory = async_to_raw_response_wrapper( + memories.update_memory, + ) self.upload_file = async_to_raw_response_wrapper( memories.upload_file, ) @@ -798,9 +1043,15 @@ def __init__(self, memories: MemoriesResource) -> None: self.add = to_streamed_response_wrapper( memories.add, ) + self.forget = to_streamed_response_wrapper( + memories.forget, + ) self.get = to_streamed_response_wrapper( memories.get, ) + self.update_memory = to_streamed_response_wrapper( + memories.update_memory, + ) self.upload_file = to_streamed_response_wrapper( memories.upload_file, ) @@ -822,9 +1073,15 @@ def __init__(self, memories: AsyncMemoriesResource) -> None: self.add = async_to_streamed_response_wrapper( memories.add, ) + self.forget = async_to_streamed_response_wrapper( + memories.forget, + ) self.get = async_to_streamed_response_wrapper( memories.get, ) + self.update_memory = async_to_streamed_response_wrapper( + memories.update_memory, + ) self.upload_file = async_to_streamed_response_wrapper( memories.upload_file, ) diff --git a/src/supermemory/types/__init__.py b/src/supermemory/types/__init__.py index 33058382..a60c46c9 100644 --- a/src/supermemory/types/__init__.py +++ b/src/supermemory/types/__init__.py @@ -3,6 +3,7 @@ from __future__ import annotations from .add_response import AddResponse as AddResponse +from .search_response import SearchResponse as SearchResponse from .profile_response import ProfileResponse as ProfileResponse from .client_add_params import ClientAddParams as ClientAddParams from .memory_add_params import MemoryAddParams as MemoryAddParams @@ -10,7 +11,9 @@ from .document_add_params import DocumentAddParams as DocumentAddParams from .memory_add_response import MemoryAddResponse as MemoryAddResponse from .memory_get_response import MemoryGetResponse as MemoryGetResponse +from .client_search_params import ClientSearchParams as ClientSearchParams from .document_list_params import DocumentListParams as DocumentListParams +from .memory_forget_params import MemoryForgetParams as MemoryForgetParams from .memory_list_response import MemoryListResponse as MemoryListResponse from .memory_update_params import MemoryUpdateParams as MemoryUpdateParams from .setting_get_response import SettingGetResponse as SettingGetResponse @@ -22,6 +25,7 @@ from .connection_list_params import ConnectionListParams as ConnectionListParams from .document_list_response import DocumentListResponse as DocumentListResponse from .document_update_params import DocumentUpdateParams as DocumentUpdateParams +from .memory_forget_response import MemoryForgetResponse as MemoryForgetResponse from .memory_update_response import MemoryUpdateResponse as MemoryUpdateResponse from .search_memories_params import SearchMemoriesParams as SearchMemoriesParams from .search_documents_params import SearchDocumentsParams as SearchDocumentsParams @@ -36,12 +40,20 @@ from .search_documents_response import SearchDocumentsResponse as SearchDocumentsResponse from .connection_create_response import ConnectionCreateResponse as ConnectionCreateResponse from .connection_import_response import ConnectionImportResponse as ConnectionImportResponse +from .connection_configure_params import ConnectionConfigureParams as ConnectionConfigureParams +from .connection_resources_params import ConnectionResourcesParams as ConnectionResourcesParams +from .document_delete_bulk_params import DocumentDeleteBulkParams as DocumentDeleteBulkParams from .document_upload_file_params import DocumentUploadFileParams as DocumentUploadFileParams +from .memory_update_memory_params import MemoryUpdateMemoryParams as MemoryUpdateMemoryParams from .memory_upload_file_response import MemoryUploadFileResponse as MemoryUploadFileResponse +from .connection_get_by_tag_params import ConnectionGetByTagParams as ConnectionGetByTagParams +from .connection_configure_response import ConnectionConfigureResponse as ConnectionConfigureResponse from .connection_get_by_id_response import ConnectionGetByIDResponse as ConnectionGetByIDResponse -from .connection_get_by_tags_params import ConnectionGetByTagsParams as ConnectionGetByTagsParams +from .connection_resources_response import ConnectionResourcesResponse as ConnectionResourcesResponse +from .document_delete_bulk_response import DocumentDeleteBulkResponse as DocumentDeleteBulkResponse from .document_upload_file_response import DocumentUploadFileResponse as DocumentUploadFileResponse -from .connection_get_by_tags_response import ConnectionGetByTagsResponse as ConnectionGetByTagsResponse +from .memory_update_memory_response import MemoryUpdateMemoryResponse as MemoryUpdateMemoryResponse +from .connection_get_by_tag_response import ConnectionGetByTagResponse as ConnectionGetByTagResponse from .connection_delete_by_id_response import ConnectionDeleteByIDResponse as ConnectionDeleteByIDResponse from .connection_list_documents_params import ConnectionListDocumentsParams as ConnectionListDocumentsParams from .connection_list_documents_response import ConnectionListDocumentsResponse as ConnectionListDocumentsResponse diff --git a/src/supermemory/types/client_search_params.py b/src/supermemory/types/client_search_params.py new file mode 100644 index 00000000..fbc5e245 --- /dev/null +++ b/src/supermemory/types/client_search_params.py @@ -0,0 +1,85 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable +from typing_extensions import Required, Annotated, TypeAlias, TypedDict + +from .._utils import PropertyInfo + +__all__ = ["ClientSearchParams", "Filters", "FiltersOr", "FiltersAnd", "Include"] + + +class ClientSearchParams(TypedDict, total=False): + q: Required[str] + """Search query string""" + + container_tag: Annotated[str, PropertyInfo(alias="containerTag")] + """Optional tag this search should be containerized by. + + This can be an ID for your user, a project ID, or any other identifier you wish + to use to filter memories. + """ + + filters: Filters + """Optional filters to apply to the search. Can be a JSON string or Query object.""" + + include: Include + + limit: int + """Maximum number of results to return""" + + rerank: bool + """If true, rerank the results based on the query. + + This is helpful if you want to ensure the most relevant results are returned. + """ + + rewrite_query: Annotated[bool, PropertyInfo(alias="rewriteQuery")] + """If true, rewrites the query to make it easier to find documents. + + This increases the latency by about 400ms + """ + + threshold: float + """Threshold / sensitivity for memories selection. + + 0 is least sensitive (returns most memories, more results), 1 is most sensitive + (returns lesser memories, accurate results) + """ + + +class FiltersOr(TypedDict, total=False): + """OR""" + + or_: Required[Annotated[Iterable[object], PropertyInfo(alias="OR")]] + + +class FiltersAnd(TypedDict, total=False): + """AND""" + + and_: Required[Annotated[Iterable[object], PropertyInfo(alias="AND")]] + + +Filters: TypeAlias = Union[FiltersOr, FiltersAnd] + + +class Include(TypedDict, total=False): + chunks: bool + """ + If true, fetch and return chunks from documents associated with the found + memories. Performs vector search on chunks within those documents. + """ + + documents: bool + + forgotten_memories: Annotated[bool, PropertyInfo(alias="forgottenMemories")] + """If true, include forgotten memories in search results. + + Forgotten memories are memories that have been explicitly forgotten or have + passed their expiration date. + """ + + related_memories: Annotated[bool, PropertyInfo(alias="relatedMemories")] + + summaries: bool diff --git a/src/supermemory/types/connection_configure_params.py b/src/supermemory/types/connection_configure_params.py new file mode 100644 index 00000000..224ed1d2 --- /dev/null +++ b/src/supermemory/types/connection_configure_params.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Iterable +from typing_extensions import Required, TypedDict + +__all__ = ["ConnectionConfigureParams"] + + +class ConnectionConfigureParams(TypedDict, total=False): + resources: Required[Iterable[Dict[str, object]]] diff --git a/src/supermemory/types/connection_configure_response.py b/src/supermemory/types/connection_configure_response.py new file mode 100644 index 00000000..065c4a87 --- /dev/null +++ b/src/supermemory/types/connection_configure_response.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = ["ConnectionConfigureResponse"] + + +class ConnectionConfigureResponse(BaseModel): + message: str + + success: bool + + webhooks_registered: Optional[float] = FieldInfo(alias="webhooksRegistered", default=None) diff --git a/src/supermemory/types/connection_get_by_tags_params.py b/src/supermemory/types/connection_get_by_tag_params.py similarity index 81% rename from src/supermemory/types/connection_get_by_tags_params.py rename to src/supermemory/types/connection_get_by_tag_params.py index 51849b90..13a90822 100644 --- a/src/supermemory/types/connection_get_by_tags_params.py +++ b/src/supermemory/types/connection_get_by_tag_params.py @@ -7,9 +7,9 @@ from .._types import SequenceNotStr from .._utils import PropertyInfo -__all__ = ["ConnectionGetByTagsParams"] +__all__ = ["ConnectionGetByTagParams"] -class ConnectionGetByTagsParams(TypedDict, total=False): +class ConnectionGetByTagParams(TypedDict, total=False): container_tags: Required[Annotated[SequenceNotStr[str], PropertyInfo(alias="containerTags")]] """Comma-separated list of container tags to filter connection by""" diff --git a/src/supermemory/types/connection_get_by_tags_response.py b/src/supermemory/types/connection_get_by_tag_response.py similarity index 85% rename from src/supermemory/types/connection_get_by_tags_response.py rename to src/supermemory/types/connection_get_by_tag_response.py index 8f0aaa5a..8ef75ed2 100644 --- a/src/supermemory/types/connection_get_by_tags_response.py +++ b/src/supermemory/types/connection_get_by_tag_response.py @@ -6,10 +6,10 @@ from .._models import BaseModel -__all__ = ["ConnectionGetByTagsResponse"] +__all__ = ["ConnectionGetByTagResponse"] -class ConnectionGetByTagsResponse(BaseModel): +class ConnectionGetByTagResponse(BaseModel): id: str created_at: str = FieldInfo(alias="createdAt") diff --git a/src/supermemory/types/connection_resources_params.py b/src/supermemory/types/connection_resources_params.py new file mode 100644 index 00000000..97ba168a --- /dev/null +++ b/src/supermemory/types/connection_resources_params.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["ConnectionResourcesParams"] + + +class ConnectionResourcesParams(TypedDict, total=False): + page: float + + per_page: float diff --git a/src/supermemory/types/connection_resources_response.py b/src/supermemory/types/connection_resources_response.py new file mode 100644 index 00000000..1d70419a --- /dev/null +++ b/src/supermemory/types/connection_resources_response.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, List, Optional + +from .._models import BaseModel + +__all__ = ["ConnectionResourcesResponse"] + + +class ConnectionResourcesResponse(BaseModel): + resources: List[Dict[str, object]] + + total_count: Optional[float] = None diff --git a/src/supermemory/types/document_delete_bulk_params.py b/src/supermemory/types/document_delete_bulk_params.py new file mode 100644 index 00000000..047f7d67 --- /dev/null +++ b/src/supermemory/types/document_delete_bulk_params.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Annotated, TypedDict + +from .._types import SequenceNotStr +from .._utils import PropertyInfo + +__all__ = ["DocumentDeleteBulkParams"] + + +class DocumentDeleteBulkParams(TypedDict, total=False): + container_tags: Annotated[SequenceNotStr[str], PropertyInfo(alias="containerTags")] + """Array of container tags - all documents in these containers will be deleted""" + + ids: SequenceNotStr[str] + """Array of document IDs to delete (max 100 at once)""" diff --git a/src/supermemory/types/document_delete_bulk_response.py b/src/supermemory/types/document_delete_bulk_response.py new file mode 100644 index 00000000..8af4af02 --- /dev/null +++ b/src/supermemory/types/document_delete_bulk_response.py @@ -0,0 +1,37 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = ["DocumentDeleteBulkResponse", "Error"] + + +class Error(BaseModel): + id: str + + error: str + + +class DocumentDeleteBulkResponse(BaseModel): + """Response for bulk document deletion""" + + deleted_count: float = FieldInfo(alias="deletedCount") + """Number of documents successfully deleted""" + + success: bool + """Whether the bulk deletion was successful""" + + container_tags: Optional[List[str]] = FieldInfo(alias="containerTags", default=None) + """ + Container tags that were processed (only applicable when deleting by container + tags) + """ + + errors: Optional[List[Error]] = None + """ + Array of errors for documents that couldn't be deleted (only applicable when + deleting by IDs) + """ diff --git a/src/supermemory/types/memory_forget_params.py b/src/supermemory/types/memory_forget_params.py new file mode 100644 index 00000000..bea67e5f --- /dev/null +++ b/src/supermemory/types/memory_forget_params.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, Annotated, TypedDict + +from .._utils import PropertyInfo + +__all__ = ["MemoryForgetParams"] + + +class MemoryForgetParams(TypedDict, total=False): + container_tag: Required[Annotated[str, PropertyInfo(alias="containerTag")]] + """Container tag / space identifier. Required to scope the operation.""" + + id: str + """ID of the memory entry to operate on""" + + content: str + """Exact content match of the memory entry to operate on. + + Use this when you don't have the ID. + """ + + reason: str + """Optional reason for forgetting this memory""" diff --git a/src/supermemory/types/memory_forget_response.py b/src/supermemory/types/memory_forget_response.py new file mode 100644 index 00000000..0e000619 --- /dev/null +++ b/src/supermemory/types/memory_forget_response.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .._models import BaseModel + +__all__ = ["MemoryForgetResponse"] + + +class MemoryForgetResponse(BaseModel): + """Response after forgetting a memory""" + + id: str + """ID of the memory that was forgotten""" + + forgotten: bool + """Indicates the memory was successfully forgotten""" diff --git a/src/supermemory/types/memory_update_memory_params.py b/src/supermemory/types/memory_update_memory_params.py new file mode 100644 index 00000000..92f54180 --- /dev/null +++ b/src/supermemory/types/memory_update_memory_params.py @@ -0,0 +1,31 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union +from typing_extensions import Required, Annotated, TypedDict + +from .._types import SequenceNotStr +from .._utils import PropertyInfo + +__all__ = ["MemoryUpdateMemoryParams"] + + +class MemoryUpdateMemoryParams(TypedDict, total=False): + container_tag: Required[Annotated[str, PropertyInfo(alias="containerTag")]] + """Container tag / space identifier. Required to scope the operation.""" + + new_content: Required[Annotated[str, PropertyInfo(alias="newContent")]] + """The new content that will replace the existing memory""" + + id: str + """ID of the memory entry to operate on""" + + content: str + """Exact content match of the memory entry to operate on. + + Use this when you don't have the ID. + """ + + metadata: Dict[str, Union[str, float, bool, SequenceNotStr[str]]] + """Optional metadata. If not provided, inherits from the previous version.""" diff --git a/src/supermemory/types/memory_update_memory_response.py b/src/supermemory/types/memory_update_memory_response.py new file mode 100644 index 00000000..62bd0dc3 --- /dev/null +++ b/src/supermemory/types/memory_update_memory_response.py @@ -0,0 +1,31 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = ["MemoryUpdateMemoryResponse"] + + +class MemoryUpdateMemoryResponse(BaseModel): + """Response after updating a memory""" + + id: str + """ID of the newly created memory version""" + + created_at: str = FieldInfo(alias="createdAt") + """When this memory version was created""" + + memory: str + """The content of the new memory version""" + + parent_memory_id: Optional[str] = FieldInfo(alias="parentMemoryId", default=None) + """ID of the memory this version updates""" + + root_memory_id: Optional[str] = FieldInfo(alias="rootMemoryId", default=None) + """ID of the first memory in this version chain""" + + version: float + """Version number of this memory entry""" diff --git a/src/supermemory/types/search_response.py b/src/supermemory/types/search_response.py new file mode 100644 index 00000000..7637939a --- /dev/null +++ b/src/supermemory/types/search_response.py @@ -0,0 +1,143 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, List, Optional +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = [ + "SearchResponse", + "Result", + "ResultChunk", + "ResultContext", + "ResultContextChild", + "ResultContextParent", + "ResultDocument", +] + + +class ResultChunk(BaseModel): + content: str + """Content of the chunk""" + + document_id: str = FieldInfo(alias="documentId") + """ID of the document this chunk belongs to""" + + position: float + """Position of chunk in the document (0-indexed)""" + + score: float + """Similarity score between the query and chunk""" + + +class ResultContextChild(BaseModel): + memory: str + """The contextual memory content""" + + relation: Literal["updates", "extends", "derives"] + """Relation type between this memory and its parent/child""" + + updated_at: str = FieldInfo(alias="updatedAt") + """Contextual memory last update date""" + + metadata: Optional[Dict[str, object]] = None + """Contextual memory metadata""" + + version: Optional[float] = None + """ + Relative version distance from the primary memory (+1 for direct child, +2 for + grand-child, etc.) + """ + + +class ResultContextParent(BaseModel): + memory: str + """The contextual memory content""" + + relation: Literal["updates", "extends", "derives"] + """Relation type between this memory and its parent/child""" + + updated_at: str = FieldInfo(alias="updatedAt") + """Contextual memory last update date""" + + metadata: Optional[Dict[str, object]] = None + """Contextual memory metadata""" + + version: Optional[float] = None + """ + Relative version distance from the primary memory (-1 for direct parent, -2 for + grand-parent, etc.) + """ + + +class ResultContext(BaseModel): + """Object containing arrays of parent and child contextual memories""" + + children: Optional[List[ResultContextChild]] = None + + parents: Optional[List[ResultContextParent]] = None + + +class ResultDocument(BaseModel): + id: str + """Document ID""" + + created_at: str = FieldInfo(alias="createdAt") + """Document creation date""" + + updated_at: str = FieldInfo(alias="updatedAt") + """Document last update date""" + + metadata: Optional[Dict[str, object]] = None + """Document metadata (only included when documents=true)""" + + summary: Optional[str] = None + """Document summary (only included when summaries=true)""" + + title: Optional[str] = None + """Document title (only included when documents=true)""" + + type: Optional[str] = None + """Document type (only included when documents=true)""" + + +class Result(BaseModel): + id: str + """Memory entry ID""" + + memory: str + """The memory content""" + + metadata: Optional[Dict[str, object]] = None + """Memory metadata""" + + similarity: float + """Similarity score between the query and memory entry""" + + updated_at: str = FieldInfo(alias="updatedAt") + """Memory last update date""" + + chunks: Optional[List[ResultChunk]] = None + """Relevant chunks from associated documents (only included when chunks=true)""" + + context: Optional[ResultContext] = None + """Object containing arrays of parent and child contextual memories""" + + documents: Optional[List[ResultDocument]] = None + """Associated documents for this memory entry""" + + version: Optional[float] = None + """Version number of this memory entry""" + + +class SearchResponse(BaseModel): + results: List[Result] + """Array of matching memory entries with similarity scores""" + + timing: float + """Search execution time in milliseconds""" + + total: float + """Total number of results returned""" diff --git a/tests/api_resources/test_client.py b/tests/api_resources/test_client.py index 37b1c63e..77a5769d 100644 --- a/tests/api_resources/test_client.py +++ b/tests/api_resources/test_client.py @@ -9,7 +9,11 @@ from supermemory import Supermemory, AsyncSupermemory from tests.utils import assert_matches_type -from supermemory.types import AddResponse, ProfileResponse +from supermemory.types import ( + AddResponse, + SearchResponse, + ProfileResponse, +) base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -106,6 +110,61 @@ def test_streaming_response_profile(self, client: Supermemory) -> None: assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_search(self, client: Supermemory) -> None: + client_ = client.search( + q="machine learning concepts", + ) + assert_matches_type(SearchResponse, client_, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_search_with_all_params(self, client: Supermemory) -> None: + client_ = client.search( + q="machine learning concepts", + container_tag="user_123", + filters={"or_": [{}]}, + include={ + "chunks": False, + "documents": True, + "forgotten_memories": False, + "related_memories": True, + "summaries": True, + }, + limit=10, + rerank=False, + rewrite_query=False, + threshold=0.5, + ) + assert_matches_type(SearchResponse, client_, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_search(self, client: Supermemory) -> None: + response = client.with_raw_response.search( + q="machine learning concepts", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + client_ = response.parse() + assert_matches_type(SearchResponse, client_, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_search(self, client: Supermemory) -> None: + with client.with_streaming_response.search( + q="machine learning concepts", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + client_ = response.parse() + assert_matches_type(SearchResponse, client_, path=["response"]) + + assert cast(Any, response.is_closed) is True + class TestAsyncClient: parametrize = pytest.mark.parametrize( @@ -200,3 +259,58 @@ async def test_streaming_response_profile(self, async_client: AsyncSupermemory) assert_matches_type(ProfileResponse, client, path=["response"]) assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_search(self, async_client: AsyncSupermemory) -> None: + client = await async_client.search( + q="machine learning concepts", + ) + assert_matches_type(SearchResponse, client, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_search_with_all_params(self, async_client: AsyncSupermemory) -> None: + client = await async_client.search( + q="machine learning concepts", + container_tag="user_123", + filters={"or_": [{}]}, + include={ + "chunks": False, + "documents": True, + "forgotten_memories": False, + "related_memories": True, + "summaries": True, + }, + limit=10, + rerank=False, + rewrite_query=False, + threshold=0.5, + ) + assert_matches_type(SearchResponse, client, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_search(self, async_client: AsyncSupermemory) -> None: + response = await async_client.with_raw_response.search( + q="machine learning concepts", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + client = await response.parse() + assert_matches_type(SearchResponse, client, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_search(self, async_client: AsyncSupermemory) -> None: + async with async_client.with_streaming_response.search( + q="machine learning concepts", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + client = await response.parse() + assert_matches_type(SearchResponse, client, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/test_connections.py b/tests/api_resources/test_connections.py index 70cf1bc1..eae9ec7a 100644 --- a/tests/api_resources/test_connections.py +++ b/tests/api_resources/test_connections.py @@ -13,7 +13,9 @@ ConnectionListResponse, ConnectionCreateResponse, ConnectionGetByIDResponse, - ConnectionGetByTagsResponse, + ConnectionGetByTagResponse, + ConnectionConfigureResponse, + ConnectionResourcesResponse, ConnectionDeleteByIDResponse, ConnectionListDocumentsResponse, ConnectionDeleteByProviderResponse, @@ -107,6 +109,52 @@ def test_streaming_response_list(self, client: Supermemory) -> None: assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_configure(self, client: Supermemory) -> None: + connection = client.connections.configure( + connection_id="connectionId", + resources=[{"foo": "bar"}], + ) + assert_matches_type(ConnectionConfigureResponse, connection, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_configure(self, client: Supermemory) -> None: + response = client.connections.with_raw_response.configure( + connection_id="connectionId", + resources=[{"foo": "bar"}], + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + connection = response.parse() + assert_matches_type(ConnectionConfigureResponse, connection, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_configure(self, client: Supermemory) -> None: + with client.connections.with_streaming_response.configure( + connection_id="connectionId", + resources=[{"foo": "bar"}], + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + connection = response.parse() + assert_matches_type(ConnectionConfigureResponse, connection, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_configure(self, client: Supermemory) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `connection_id` but received ''"): + client.connections.with_raw_response.configure( + connection_id="", + resources=[{"foo": "bar"}], + ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_delete_by_id(self, client: Supermemory) -> None: @@ -230,17 +278,17 @@ def test_path_params_get_by_id(self, client: Supermemory) -> None: @pytest.mark.skip(reason="Prism tests are disabled") @parametrize - def test_method_get_by_tags(self, client: Supermemory) -> None: - connection = client.connections.get_by_tags( + def test_method_get_by_tag(self, client: Supermemory) -> None: + connection = client.connections.get_by_tag( provider="notion", container_tags=["user_123", "project_123"], ) - assert_matches_type(ConnectionGetByTagsResponse, connection, path=["response"]) + assert_matches_type(ConnectionGetByTagResponse, connection, path=["response"]) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize - def test_raw_response_get_by_tags(self, client: Supermemory) -> None: - response = client.connections.with_raw_response.get_by_tags( + def test_raw_response_get_by_tag(self, client: Supermemory) -> None: + response = client.connections.with_raw_response.get_by_tag( provider="notion", container_tags=["user_123", "project_123"], ) @@ -248,12 +296,12 @@ def test_raw_response_get_by_tags(self, client: Supermemory) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" connection = response.parse() - assert_matches_type(ConnectionGetByTagsResponse, connection, path=["response"]) + assert_matches_type(ConnectionGetByTagResponse, connection, path=["response"]) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize - def test_streaming_response_get_by_tags(self, client: Supermemory) -> None: - with client.connections.with_streaming_response.get_by_tags( + def test_streaming_response_get_by_tag(self, client: Supermemory) -> None: + with client.connections.with_streaming_response.get_by_tag( provider="notion", container_tags=["user_123", "project_123"], ) as response: @@ -261,7 +309,7 @@ def test_streaming_response_get_by_tags(self, client: Supermemory) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" connection = response.parse() - assert_matches_type(ConnectionGetByTagsResponse, connection, path=["response"]) + assert_matches_type(ConnectionGetByTagResponse, connection, path=["response"]) assert cast(Any, response.is_closed) is True @@ -351,6 +399,58 @@ def test_streaming_response_list_documents(self, client: Supermemory) -> None: assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_resources(self, client: Supermemory) -> None: + connection = client.connections.resources( + connection_id="connectionId", + ) + assert_matches_type(ConnectionResourcesResponse, connection, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_resources_with_all_params(self, client: Supermemory) -> None: + connection = client.connections.resources( + connection_id="connectionId", + page=0, + per_page=0, + ) + assert_matches_type(ConnectionResourcesResponse, connection, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_resources(self, client: Supermemory) -> None: + response = client.connections.with_raw_response.resources( + connection_id="connectionId", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + connection = response.parse() + assert_matches_type(ConnectionResourcesResponse, connection, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_resources(self, client: Supermemory) -> None: + with client.connections.with_streaming_response.resources( + connection_id="connectionId", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + connection = response.parse() + assert_matches_type(ConnectionResourcesResponse, connection, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_resources(self, client: Supermemory) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `connection_id` but received ''"): + client.connections.with_raw_response.resources( + connection_id="", + ) + class TestAsyncConnections: parametrize = pytest.mark.parametrize( @@ -439,6 +539,52 @@ async def test_streaming_response_list(self, async_client: AsyncSupermemory) -> assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_configure(self, async_client: AsyncSupermemory) -> None: + connection = await async_client.connections.configure( + connection_id="connectionId", + resources=[{"foo": "bar"}], + ) + assert_matches_type(ConnectionConfigureResponse, connection, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_configure(self, async_client: AsyncSupermemory) -> None: + response = await async_client.connections.with_raw_response.configure( + connection_id="connectionId", + resources=[{"foo": "bar"}], + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + connection = await response.parse() + assert_matches_type(ConnectionConfigureResponse, connection, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_configure(self, async_client: AsyncSupermemory) -> None: + async with async_client.connections.with_streaming_response.configure( + connection_id="connectionId", + resources=[{"foo": "bar"}], + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + connection = await response.parse() + assert_matches_type(ConnectionConfigureResponse, connection, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_configure(self, async_client: AsyncSupermemory) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `connection_id` but received ''"): + await async_client.connections.with_raw_response.configure( + connection_id="", + resources=[{"foo": "bar"}], + ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_delete_by_id(self, async_client: AsyncSupermemory) -> None: @@ -562,17 +708,17 @@ async def test_path_params_get_by_id(self, async_client: AsyncSupermemory) -> No @pytest.mark.skip(reason="Prism tests are disabled") @parametrize - async def test_method_get_by_tags(self, async_client: AsyncSupermemory) -> None: - connection = await async_client.connections.get_by_tags( + async def test_method_get_by_tag(self, async_client: AsyncSupermemory) -> None: + connection = await async_client.connections.get_by_tag( provider="notion", container_tags=["user_123", "project_123"], ) - assert_matches_type(ConnectionGetByTagsResponse, connection, path=["response"]) + assert_matches_type(ConnectionGetByTagResponse, connection, path=["response"]) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize - async def test_raw_response_get_by_tags(self, async_client: AsyncSupermemory) -> None: - response = await async_client.connections.with_raw_response.get_by_tags( + async def test_raw_response_get_by_tag(self, async_client: AsyncSupermemory) -> None: + response = await async_client.connections.with_raw_response.get_by_tag( provider="notion", container_tags=["user_123", "project_123"], ) @@ -580,12 +726,12 @@ async def test_raw_response_get_by_tags(self, async_client: AsyncSupermemory) -> assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" connection = await response.parse() - assert_matches_type(ConnectionGetByTagsResponse, connection, path=["response"]) + assert_matches_type(ConnectionGetByTagResponse, connection, path=["response"]) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize - async def test_streaming_response_get_by_tags(self, async_client: AsyncSupermemory) -> None: - async with async_client.connections.with_streaming_response.get_by_tags( + async def test_streaming_response_get_by_tag(self, async_client: AsyncSupermemory) -> None: + async with async_client.connections.with_streaming_response.get_by_tag( provider="notion", container_tags=["user_123", "project_123"], ) as response: @@ -593,7 +739,7 @@ async def test_streaming_response_get_by_tags(self, async_client: AsyncSupermemo assert response.http_request.headers.get("X-Stainless-Lang") == "python" connection = await response.parse() - assert_matches_type(ConnectionGetByTagsResponse, connection, path=["response"]) + assert_matches_type(ConnectionGetByTagResponse, connection, path=["response"]) assert cast(Any, response.is_closed) is True @@ -682,3 +828,55 @@ async def test_streaming_response_list_documents(self, async_client: AsyncSuperm assert_matches_type(ConnectionListDocumentsResponse, connection, path=["response"]) assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_resources(self, async_client: AsyncSupermemory) -> None: + connection = await async_client.connections.resources( + connection_id="connectionId", + ) + assert_matches_type(ConnectionResourcesResponse, connection, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_resources_with_all_params(self, async_client: AsyncSupermemory) -> None: + connection = await async_client.connections.resources( + connection_id="connectionId", + page=0, + per_page=0, + ) + assert_matches_type(ConnectionResourcesResponse, connection, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_resources(self, async_client: AsyncSupermemory) -> None: + response = await async_client.connections.with_raw_response.resources( + connection_id="connectionId", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + connection = await response.parse() + assert_matches_type(ConnectionResourcesResponse, connection, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_resources(self, async_client: AsyncSupermemory) -> None: + async with async_client.connections.with_streaming_response.resources( + connection_id="connectionId", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + connection = await response.parse() + assert_matches_type(ConnectionResourcesResponse, connection, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_resources(self, async_client: AsyncSupermemory) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `connection_id` but received ''"): + await async_client.connections.with_raw_response.resources( + connection_id="", + ) diff --git a/tests/api_resources/test_documents.py b/tests/api_resources/test_documents.py index 7aecc421..0ef64f86 100644 --- a/tests/api_resources/test_documents.py +++ b/tests/api_resources/test_documents.py @@ -14,6 +14,7 @@ DocumentGetResponse, DocumentListResponse, DocumentUpdateResponse, + DocumentDeleteBulkResponse, DocumentUploadFileResponse, ) @@ -231,6 +232,43 @@ def test_streaming_response_add(self, client: Supermemory) -> None: assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_delete_bulk(self, client: Supermemory) -> None: + document = client.documents.delete_bulk() + assert_matches_type(DocumentDeleteBulkResponse, document, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_delete_bulk_with_all_params(self, client: Supermemory) -> None: + document = client.documents.delete_bulk( + container_tags=["user_123", "project_123"], + ids=["acxV5LHMEsG2hMSNb4umbn", "bxcV5LHMEsG2hMSNb4umbn"], + ) + assert_matches_type(DocumentDeleteBulkResponse, document, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_delete_bulk(self, client: Supermemory) -> None: + response = client.documents.with_raw_response.delete_bulk() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + document = response.parse() + assert_matches_type(DocumentDeleteBulkResponse, document, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_delete_bulk(self, client: Supermemory) -> None: + with client.documents.with_streaming_response.delete_bulk() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + document = response.parse() + assert_matches_type(DocumentDeleteBulkResponse, document, path=["response"]) + + assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_get(self, client: Supermemory) -> None: @@ -533,6 +571,43 @@ async def test_streaming_response_add(self, async_client: AsyncSupermemory) -> N assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_delete_bulk(self, async_client: AsyncSupermemory) -> None: + document = await async_client.documents.delete_bulk() + assert_matches_type(DocumentDeleteBulkResponse, document, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_delete_bulk_with_all_params(self, async_client: AsyncSupermemory) -> None: + document = await async_client.documents.delete_bulk( + container_tags=["user_123", "project_123"], + ids=["acxV5LHMEsG2hMSNb4umbn", "bxcV5LHMEsG2hMSNb4umbn"], + ) + assert_matches_type(DocumentDeleteBulkResponse, document, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_delete_bulk(self, async_client: AsyncSupermemory) -> None: + response = await async_client.documents.with_raw_response.delete_bulk() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + document = await response.parse() + assert_matches_type(DocumentDeleteBulkResponse, document, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_delete_bulk(self, async_client: AsyncSupermemory) -> None: + async with async_client.documents.with_streaming_response.delete_bulk() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + document = await response.parse() + assert_matches_type(DocumentDeleteBulkResponse, document, path=["response"]) + + assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_get(self, async_client: AsyncSupermemory) -> None: diff --git a/tests/api_resources/test_memories.py b/tests/api_resources/test_memories.py index 3ac5c35d..908ba97a 100644 --- a/tests/api_resources/test_memories.py +++ b/tests/api_resources/test_memories.py @@ -13,8 +13,10 @@ MemoryAddResponse, MemoryGetResponse, MemoryListResponse, + MemoryForgetResponse, MemoryUpdateResponse, MemoryUploadFileResponse, + MemoryUpdateMemoryResponse, ) base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -231,6 +233,51 @@ def test_streaming_response_add(self, client: Supermemory) -> None: assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_forget(self, client: Supermemory) -> None: + memory = client.memories.forget( + container_tag="user_123", + ) + assert_matches_type(MemoryForgetResponse, memory, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_forget_with_all_params(self, client: Supermemory) -> None: + memory = client.memories.forget( + container_tag="user_123", + id="mem_abc123", + content="John prefers dark mode", + reason="outdated information", + ) + assert_matches_type(MemoryForgetResponse, memory, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_forget(self, client: Supermemory) -> None: + response = client.memories.with_raw_response.forget( + container_tag="user_123", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + memory = response.parse() + assert_matches_type(MemoryForgetResponse, memory, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_forget(self, client: Supermemory) -> None: + with client.memories.with_streaming_response.forget( + container_tag="user_123", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + memory = response.parse() + assert_matches_type(MemoryForgetResponse, memory, path=["response"]) + + assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_get(self, client: Supermemory) -> None: @@ -273,6 +320,55 @@ def test_path_params_get(self, client: Supermemory) -> None: "", ) + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_update_memory(self, client: Supermemory) -> None: + memory = client.memories.update_memory( + container_tag="user_123", + new_content="John now prefers light mode", + ) + assert_matches_type(MemoryUpdateMemoryResponse, memory, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_update_memory_with_all_params(self, client: Supermemory) -> None: + memory = client.memories.update_memory( + container_tag="user_123", + new_content="John now prefers light mode", + id="mem_abc123", + content="John prefers dark mode", + metadata={"foo": "string"}, + ) + assert_matches_type(MemoryUpdateMemoryResponse, memory, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_update_memory(self, client: Supermemory) -> None: + response = client.memories.with_raw_response.update_memory( + container_tag="user_123", + new_content="John now prefers light mode", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + memory = response.parse() + assert_matches_type(MemoryUpdateMemoryResponse, memory, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_update_memory(self, client: Supermemory) -> None: + with client.memories.with_streaming_response.update_memory( + container_tag="user_123", + new_content="John now prefers light mode", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + memory = response.parse() + assert_matches_type(MemoryUpdateMemoryResponse, memory, path=["response"]) + + assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_upload_file(self, client: Supermemory) -> None: @@ -533,6 +629,51 @@ async def test_streaming_response_add(self, async_client: AsyncSupermemory) -> N assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_forget(self, async_client: AsyncSupermemory) -> None: + memory = await async_client.memories.forget( + container_tag="user_123", + ) + assert_matches_type(MemoryForgetResponse, memory, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_forget_with_all_params(self, async_client: AsyncSupermemory) -> None: + memory = await async_client.memories.forget( + container_tag="user_123", + id="mem_abc123", + content="John prefers dark mode", + reason="outdated information", + ) + assert_matches_type(MemoryForgetResponse, memory, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_forget(self, async_client: AsyncSupermemory) -> None: + response = await async_client.memories.with_raw_response.forget( + container_tag="user_123", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + memory = await response.parse() + assert_matches_type(MemoryForgetResponse, memory, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_forget(self, async_client: AsyncSupermemory) -> None: + async with async_client.memories.with_streaming_response.forget( + container_tag="user_123", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + memory = await response.parse() + assert_matches_type(MemoryForgetResponse, memory, path=["response"]) + + assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_get(self, async_client: AsyncSupermemory) -> None: @@ -575,6 +716,55 @@ async def test_path_params_get(self, async_client: AsyncSupermemory) -> None: "", ) + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_update_memory(self, async_client: AsyncSupermemory) -> None: + memory = await async_client.memories.update_memory( + container_tag="user_123", + new_content="John now prefers light mode", + ) + assert_matches_type(MemoryUpdateMemoryResponse, memory, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_update_memory_with_all_params(self, async_client: AsyncSupermemory) -> None: + memory = await async_client.memories.update_memory( + container_tag="user_123", + new_content="John now prefers light mode", + id="mem_abc123", + content="John prefers dark mode", + metadata={"foo": "string"}, + ) + assert_matches_type(MemoryUpdateMemoryResponse, memory, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_update_memory(self, async_client: AsyncSupermemory) -> None: + response = await async_client.memories.with_raw_response.update_memory( + container_tag="user_123", + new_content="John now prefers light mode", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + memory = await response.parse() + assert_matches_type(MemoryUpdateMemoryResponse, memory, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_update_memory(self, async_client: AsyncSupermemory) -> None: + async with async_client.memories.with_streaming_response.update_memory( + container_tag="user_123", + new_content="John now prefers light mode", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + memory = await response.parse() + assert_matches_type(MemoryUpdateMemoryResponse, memory, path=["response"]) + + assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_upload_file(self, async_client: AsyncSupermemory) -> None: From 79b5e8e06d94c239bb5f05a90c93879419278d4b Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 9 Dec 2025 21:09:43 +0000 Subject: [PATCH 11/11] release: 3.9.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 23 +++++++++++++++++++++++ pyproject.toml | 2 +- src/supermemory/_version.py | 2 +- 4 files changed, 26 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 078b9e28..9127b1bd 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "3.8.0" + ".": "3.9.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c815133..b2e591b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,28 @@ # Changelog +## 3.9.0 (2025-12-09) + +Full Changelog: [v3.8.0...v3.9.0](https://github.com/supermemoryai/python-sdk/compare/v3.8.0...v3.9.0) + +### Features + +* **api:** api update ([3042cbf](https://github.com/supermemoryai/python-sdk/commit/3042cbfd221d52291480c6df1c1fca40eb97a61f)) +* **api:** manual updates ([96b34d0](https://github.com/supermemoryai/python-sdk/commit/96b34d0a371e0d9c9866e1dbbfd70329d9400850)) + + +### Bug Fixes + +* ensure streams are always closed ([a1403da](https://github.com/supermemoryai/python-sdk/commit/a1403da98836ac4215ee81f168ff044dcd83fc83)) +* **types:** allow pyright to infer TypedDict types within SequenceNotStr ([4c1365d](https://github.com/supermemoryai/python-sdk/commit/4c1365d2bc02b2d81eb99ef93ac56f1f1b6aba3c)) + + +### Chores + +* add missing docstrings ([bdb32bc](https://github.com/supermemoryai/python-sdk/commit/bdb32bc4a13ae9a73409f827082a1c5637dc9860)) +* **deps:** mypy 1.18.1 has a regression, pin to 1.17 ([2fc3d2a](https://github.com/supermemoryai/python-sdk/commit/2fc3d2a92410192a31bb1c4a7b85094c2ea55f4a)) +* **docs:** use environment variables for authentication in code snippets ([442b9fd](https://github.com/supermemoryai/python-sdk/commit/442b9fdc7e8b7b07dc82cc087ffbc6fbe60bd12f)) +* update lockfile ([37b9f74](https://github.com/supermemoryai/python-sdk/commit/37b9f74097fdc2e3395714ccb72c0f470d162ac4)) + ## 3.8.0 (2025-11-27) Full Changelog: [v3.7.0...v3.8.0](https://github.com/supermemoryai/python-sdk/compare/v3.7.0...v3.8.0) diff --git a/pyproject.toml b/pyproject.toml index 4956c29d..8cf16414 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "supermemory" -version = "3.8.0" +version = "3.9.0" description = "The official Python library for the supermemory API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/supermemory/_version.py b/src/supermemory/_version.py index 09e03d3e..8afdf66f 100644 --- a/src/supermemory/_version.py +++ b/src/supermemory/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "supermemory" -__version__ = "3.8.0" # x-release-please-version +__version__ = "3.9.0" # x-release-please-version