From 0c627bde6c7f6ef8d55e94993a612f34c75c7f80 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 19 May 2025 06:17:02 +0000 Subject: [PATCH 01/48] feat(api): api update --- .stats.yml | 4 +- api.md | 5 +- src/supermemory/resources/memories.py | 129 ++++++++++++++++-- src/supermemory/types/__init__.py | 1 - src/supermemory/types/memory_add_params.py | 28 ++++ .../types/memory_delete_response.py | 9 -- src/supermemory/types/memory_update_params.py | 31 +++++ .../types/search_execute_response.py | 5 +- src/supermemory/types/setting_get_response.py | 14 +- .../types/setting_update_response.py | 10 +- tests/api_resources/test_memories.py | 59 ++++---- 11 files changed, 236 insertions(+), 59 deletions(-) delete mode 100644 src/supermemory/types/memory_delete_response.py diff --git a/.stats.yml b/.stats.yml index 4be06c0d..322bc106 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 12 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/supermemory--inc%2Fsupermemory-e6735b03c258b382c527550bb78042bdc3aad32a5cf564785dcb9f3fb13a2862.yml -openapi_spec_hash: 8168fb51314d986893554e1cc935ca7d +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/supermemory--inc%2Fsupermemory-101f86a36ccd7f0652cfb6cac5d8f49e45ce3bee2b96692e8decd1eda3419604.yml +openapi_spec_hash: b6c08c22ab103285c7ada4eff95cfd0a config_hash: 8477e3ee6fd596ab6ac911d052e4de79 diff --git a/api.md b/api.md index 66639691..6035906a 100644 --- a/api.md +++ b/api.md @@ -6,7 +6,6 @@ Types: from supermemory.types import ( MemoryUpdateResponse, MemoryListResponse, - MemoryDeleteResponse, MemoryAddResponse, MemoryGetResponse, MemoryUploadFileResponse, @@ -15,9 +14,9 @@ from supermemory.types import ( Methods: -- client.memories.update(id, \*\*params) -> MemoryUpdateResponse +- client.memories.update(path_id, \*\*params) -> MemoryUpdateResponse - client.memories.list(\*\*params) -> MemoryListResponse -- client.memories.delete(id) -> MemoryDeleteResponse +- client.memories.delete(id) -> None - client.memories.add(\*\*params) -> MemoryAddResponse - client.memories.get(id) -> MemoryGetResponse - client.memories.upload_file(\*\*params) -> MemoryUploadFileResponse diff --git a/src/supermemory/resources/memories.py b/src/supermemory/resources/memories.py index 180f4cd6..85be93a4 100644 --- a/src/supermemory/resources/memories.py +++ b/src/supermemory/resources/memories.py @@ -8,7 +8,7 @@ import httpx from ..types import memory_add_params, memory_list_params, memory_update_params, memory_upload_file_params -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven, FileTypes +from .._types import NOT_GIVEN, Body, Query, Headers, NoneType, NotGiven, FileTypes from .._utils import extract_files, maybe_transform, deepcopy_minimal, async_maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource @@ -22,7 +22,6 @@ from ..types.memory_add_response import MemoryAddResponse from ..types.memory_get_response import MemoryGetResponse from ..types.memory_list_response import MemoryListResponse -from ..types.memory_delete_response import MemoryDeleteResponse from ..types.memory_update_response import MemoryUpdateResponse from ..types.memory_upload_file_response import MemoryUploadFileResponse @@ -51,10 +50,12 @@ def with_streaming_response(self) -> MemoriesResourceWithStreamingResponse: def update( self, - id: str, + path_id: str, *, + body_id: str, content: str, container_tags: List[str] | NotGiven = NOT_GIVEN, + custom_id: str | NotGiven = NOT_GIVEN, metadata: Dict[str, Union[str, float, bool]] | NotGiven = NOT_GIVEN, # 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. @@ -67,6 +68,29 @@ def update( Update a memory with any content type (text, url, file, etc.) and metadata Args: + body_id: Unique identifier of the memory. + + content: The content to extract and process into a memory. This can be a URL to a + website, a PDF, an image, or a video. + + Plaintext: Any plaintext format + + URL: A URL to a website, PDF, image, or video + + We automatically detect the content type from the url's response format. + + container_tags: Optional tags this memory should be containerized by. This can be an ID for your + user, a project ID, or any other identifier you wish to use to group memories. + + custom_id: Optional custom ID of the memory. This could be an ID from your database that + will uniquely identify this memory. + + metadata: Optional metadata for the memory. This is used to store additional information + about the memory. You can use this to store any additional information you need + about the memory. Metadata can be filtered through. Keys must be strings and are + case sensitive. Values can be strings, numbers, or booleans. You cannot nest + objects. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -75,14 +99,16 @@ def update( timeout: Override the client-level default timeout for this request, in seconds """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + if not path_id: + raise ValueError(f"Expected a non-empty value for `path_id` but received {path_id!r}") return self._patch( - f"/v3/memories/{id}", + f"/v3/memories/{path_id}", body=maybe_transform( { + "body_id": body_id, "content": content, "container_tags": container_tags, + "custom_id": custom_id, "metadata": metadata, }, memory_update_params.MemoryUpdateParams, @@ -161,7 +187,7 @@ def delete( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> MemoryDeleteResponse: + ) -> None: """ Delete a memory @@ -176,12 +202,13 @@ def delete( """ if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} return self._delete( f"/v3/memories/{id}", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=MemoryDeleteResponse, + cast_to=NoneType, ) def add( @@ -189,6 +216,7 @@ def add( *, content: str, container_tags: List[str] | NotGiven = NOT_GIVEN, + custom_id: str | NotGiven = NOT_GIVEN, metadata: Dict[str, Union[str, float, bool]] | NotGiven = NOT_GIVEN, # 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. @@ -201,6 +229,27 @@ def add( Add a memory with any content type (text, url, file, etc.) and metadata Args: + content: The content to extract and process into a memory. This can be a URL to a + website, a PDF, an image, or a video. + + Plaintext: Any plaintext format + + URL: A URL to a website, PDF, image, or video + + We automatically detect the content type from the url's response format. + + container_tags: Optional tags this memory should be containerized by. This can be an ID for your + user, a project ID, or any other identifier you wish to use to group memories. + + custom_id: Optional custom ID of the memory. This could be an ID from your database that + will uniquely identify this memory. + + metadata: Optional metadata for the memory. This is used to store additional information + about the memory. You can use this to store any additional information you need + about the memory. Metadata can be filtered through. Keys must be strings and are + case sensitive. Values can be strings, numbers, or booleans. You cannot nest + objects. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -215,6 +264,7 @@ def add( { "content": content, "container_tags": container_tags, + "custom_id": custom_id, "metadata": metadata, }, memory_add_params.MemoryAddParams, @@ -320,10 +370,12 @@ def with_streaming_response(self) -> AsyncMemoriesResourceWithStreamingResponse: async def update( self, - id: str, + path_id: str, *, + body_id: str, content: str, container_tags: List[str] | NotGiven = NOT_GIVEN, + custom_id: str | NotGiven = NOT_GIVEN, metadata: Dict[str, Union[str, float, bool]] | NotGiven = NOT_GIVEN, # 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. @@ -336,6 +388,29 @@ async def update( Update a memory with any content type (text, url, file, etc.) and metadata Args: + body_id: Unique identifier of the memory. + + content: The content to extract and process into a memory. This can be a URL to a + website, a PDF, an image, or a video. + + Plaintext: Any plaintext format + + URL: A URL to a website, PDF, image, or video + + We automatically detect the content type from the url's response format. + + container_tags: Optional tags this memory should be containerized by. This can be an ID for your + user, a project ID, or any other identifier you wish to use to group memories. + + custom_id: Optional custom ID of the memory. This could be an ID from your database that + will uniquely identify this memory. + + metadata: Optional metadata for the memory. This is used to store additional information + about the memory. You can use this to store any additional information you need + about the memory. Metadata can be filtered through. Keys must be strings and are + case sensitive. Values can be strings, numbers, or booleans. You cannot nest + objects. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -344,14 +419,16 @@ async def update( timeout: Override the client-level default timeout for this request, in seconds """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + if not path_id: + raise ValueError(f"Expected a non-empty value for `path_id` but received {path_id!r}") return await self._patch( - f"/v3/memories/{id}", + f"/v3/memories/{path_id}", body=await async_maybe_transform( { + "body_id": body_id, "content": content, "container_tags": container_tags, + "custom_id": custom_id, "metadata": metadata, }, memory_update_params.MemoryUpdateParams, @@ -430,7 +507,7 @@ async def delete( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> MemoryDeleteResponse: + ) -> None: """ Delete a memory @@ -445,12 +522,13 @@ async def delete( """ if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} return await self._delete( f"/v3/memories/{id}", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=MemoryDeleteResponse, + cast_to=NoneType, ) async def add( @@ -458,6 +536,7 @@ async def add( *, content: str, container_tags: List[str] | NotGiven = NOT_GIVEN, + custom_id: str | NotGiven = NOT_GIVEN, metadata: Dict[str, Union[str, float, bool]] | NotGiven = NOT_GIVEN, # 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. @@ -470,6 +549,27 @@ async def add( Add a memory with any content type (text, url, file, etc.) and metadata Args: + content: The content to extract and process into a memory. This can be a URL to a + website, a PDF, an image, or a video. + + Plaintext: Any plaintext format + + URL: A URL to a website, PDF, image, or video + + We automatically detect the content type from the url's response format. + + container_tags: Optional tags this memory should be containerized by. This can be an ID for your + user, a project ID, or any other identifier you wish to use to group memories. + + custom_id: Optional custom ID of the memory. This could be an ID from your database that + will uniquely identify this memory. + + metadata: Optional metadata for the memory. This is used to store additional information + about the memory. You can use this to store any additional information you need + about the memory. Metadata can be filtered through. Keys must be strings and are + case sensitive. Values can be strings, numbers, or booleans. You cannot nest + objects. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -484,6 +584,7 @@ async def add( { "content": content, "container_tags": container_tags, + "custom_id": custom_id, "metadata": metadata, }, memory_add_params.MemoryAddParams, diff --git a/src/supermemory/types/__init__.py b/src/supermemory/types/__init__.py index 2bb705ae..6d9b94d2 100644 --- a/src/supermemory/types/__init__.py +++ b/src/supermemory/types/__init__.py @@ -12,7 +12,6 @@ from .search_execute_params import SearchExecuteParams as SearchExecuteParams from .setting_update_params import SettingUpdateParams as SettingUpdateParams from .connection_list_params import ConnectionListParams as ConnectionListParams -from .memory_delete_response import MemoryDeleteResponse as MemoryDeleteResponse from .memory_update_response import MemoryUpdateResponse as MemoryUpdateResponse from .connection_get_response import ConnectionGetResponse as ConnectionGetResponse from .search_execute_response import SearchExecuteResponse as SearchExecuteResponse diff --git a/src/supermemory/types/memory_add_params.py b/src/supermemory/types/memory_add_params.py index a973e952..b010b941 100644 --- a/src/supermemory/types/memory_add_params.py +++ b/src/supermemory/types/memory_add_params.py @@ -12,7 +12,35 @@ class MemoryAddParams(TypedDict, total=False): content: Required[str] + """The content to extract and process into a memory. + + This can be a URL to a website, a PDF, an image, or a video. + + Plaintext: Any plaintext format + + URL: A URL to a website, PDF, image, or video + + We automatically detect the content type from the url's response format. + """ container_tags: Annotated[List[str], PropertyInfo(alias="containerTags")] + """Optional tags this memory should be containerized by. + + This can be an ID for your user, a project ID, or any other identifier you wish + to use to group memories. + """ + + custom_id: Annotated[str, PropertyInfo(alias="customId")] + """Optional custom ID of the memory. + + This could be an ID from your database that will uniquely identify this memory. + """ metadata: Dict[str, Union[str, float, bool]] + """Optional metadata for the memory. + + This is used to store additional information about the memory. You can use this + to store any additional information you need about the memory. Metadata can be + filtered through. Keys must be strings and are case sensitive. Values can be + strings, numbers, or booleans. You cannot nest objects. + """ diff --git a/src/supermemory/types/memory_delete_response.py b/src/supermemory/types/memory_delete_response.py deleted file mode 100644 index 0f17a7e1..00000000 --- a/src/supermemory/types/memory_delete_response.py +++ /dev/null @@ -1,9 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from .._models import BaseModel - -__all__ = ["MemoryDeleteResponse"] - - -class MemoryDeleteResponse(BaseModel): - success: bool diff --git a/src/supermemory/types/memory_update_params.py b/src/supermemory/types/memory_update_params.py index 2e94bfe3..10309d2d 100644 --- a/src/supermemory/types/memory_update_params.py +++ b/src/supermemory/types/memory_update_params.py @@ -11,8 +11,39 @@ class MemoryUpdateParams(TypedDict, total=False): + body_id: Required[Annotated[str, PropertyInfo(alias="id")]] + """Unique identifier of the memory.""" + content: Required[str] + """The content to extract and process into a memory. + + This can be a URL to a website, a PDF, an image, or a video. + + Plaintext: Any plaintext format + + URL: A URL to a website, PDF, image, or video + + We automatically detect the content type from the url's response format. + """ container_tags: Annotated[List[str], PropertyInfo(alias="containerTags")] + """Optional tags this memory should be containerized by. + + This can be an ID for your user, a project ID, or any other identifier you wish + to use to group memories. + """ + + custom_id: Annotated[str, PropertyInfo(alias="customId")] + """Optional custom ID of the memory. + + This could be an ID from your database that will uniquely identify this memory. + """ metadata: Dict[str, Union[str, float, bool]] + """Optional metadata for the memory. + + This is used to store additional information about the memory. You can use this + to store any additional information you need about the memory. Metadata can be + filtered through. Keys must be strings and are case sensitive. Values can be + strings, numbers, or booleans. You cannot nest objects. + """ diff --git a/src/supermemory/types/search_execute_response.py b/src/supermemory/types/search_execute_response.py index 5b2bc4e0..b48b351d 100644 --- a/src/supermemory/types/search_execute_response.py +++ b/src/supermemory/types/search_execute_response.py @@ -37,12 +37,15 @@ class Result(BaseModel): score: float """Relevance score of the match""" - title: str + title: Optional[str] = None """Document title""" updated_at: datetime = FieldInfo(alias="updatedAt") """Document last update date""" + summary: Optional[str] = None + """Document summary""" + class SearchExecuteResponse(BaseModel): results: List[Result] diff --git a/src/supermemory/types/setting_get_response.py b/src/supermemory/types/setting_get_response.py index 0cf25fd9..7f0c9df7 100644 --- a/src/supermemory/types/setting_get_response.py +++ b/src/supermemory/types/setting_get_response.py @@ -1,6 +1,8 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import Dict +from typing import Dict, List, Optional + +from pydantic import Field as FieldInfo from .._models import BaseModel @@ -8,4 +10,12 @@ class SettingGetResponse(BaseModel): - settings: Dict[str, object] + exclude_items: Optional[List[str]] = FieldInfo(alias="excludeItems", default=None) + + filter_prompt: Optional[str] = FieldInfo(alias="filterPrompt", default=None) + + filter_tags: Optional[Dict[str, List[str]]] = FieldInfo(alias="filterTags", default=None) + + include_items: Optional[List[str]] = FieldInfo(alias="includeItems", default=None) + + should_llm_filter: Optional[bool] = FieldInfo(alias="shouldLLMFilter", default=None) diff --git a/src/supermemory/types/setting_update_response.py b/src/supermemory/types/setting_update_response.py index 58a717c0..55725300 100644 --- a/src/supermemory/types/setting_update_response.py +++ b/src/supermemory/types/setting_update_response.py @@ -6,10 +6,10 @@ from .._models import BaseModel -__all__ = ["SettingUpdateResponse", "Settings"] +__all__ = ["SettingUpdateResponse", "Updated"] -class Settings(BaseModel): +class Updated(BaseModel): exclude_items: Optional[List[str]] = FieldInfo(alias="excludeItems", default=None) filter_prompt: Optional[str] = FieldInfo(alias="filterPrompt", default=None) @@ -22,6 +22,8 @@ class Settings(BaseModel): class SettingUpdateResponse(BaseModel): - message: str + org_id: str = FieldInfo(alias="orgId") - settings: Settings + org_slug: str = FieldInfo(alias="orgSlug") + + updated: Updated diff --git a/tests/api_resources/test_memories.py b/tests/api_resources/test_memories.py index 8e7130f2..2c3cca4d 100644 --- a/tests/api_resources/test_memories.py +++ b/tests/api_resources/test_memories.py @@ -13,7 +13,6 @@ MemoryAddResponse, MemoryGetResponse, MemoryListResponse, - MemoryDeleteResponse, MemoryUpdateResponse, MemoryUploadFileResponse, ) @@ -28,7 +27,8 @@ class TestMemories: @parametrize def test_method_update(self, client: Supermemory) -> None: memory = client.memories.update( - id="id", + path_id="id", + body_id="acxV5LHMEsG2hMSNb4umbn", content="This is a detailed article about machine learning concepts...", ) assert_matches_type(MemoryUpdateResponse, memory, path=["response"]) @@ -37,9 +37,11 @@ def test_method_update(self, client: Supermemory) -> None: @parametrize def test_method_update_with_all_params(self, client: Supermemory) -> None: memory = client.memories.update( - id="id", + path_id="id", + body_id="acxV5LHMEsG2hMSNb4umbn", content="This is a detailed article about machine learning concepts...", - container_tags=["string"], + container_tags=["user_123", "project_123"], + custom_id="mem_abc123", metadata={ "source": "web", "category": "technology", @@ -55,7 +57,8 @@ def test_method_update_with_all_params(self, client: Supermemory) -> None: @parametrize def test_raw_response_update(self, client: Supermemory) -> None: response = client.memories.with_raw_response.update( - id="id", + path_id="id", + body_id="acxV5LHMEsG2hMSNb4umbn", content="This is a detailed article about machine learning concepts...", ) @@ -68,7 +71,8 @@ def test_raw_response_update(self, client: Supermemory) -> None: @parametrize def test_streaming_response_update(self, client: Supermemory) -> None: with client.memories.with_streaming_response.update( - id="id", + path_id="id", + body_id="acxV5LHMEsG2hMSNb4umbn", content="This is a detailed article about machine learning concepts...", ) as response: assert not response.is_closed @@ -82,9 +86,10 @@ def test_streaming_response_update(self, client: Supermemory) -> None: @pytest.mark.skip() @parametrize def test_path_params_update(self, client: Supermemory) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `path_id` but received ''"): client.memories.with_raw_response.update( - id="", + path_id="", + body_id="acxV5LHMEsG2hMSNb4umbn", content="This is a detailed article about machine learning concepts...", ) @@ -134,7 +139,7 @@ def test_method_delete(self, client: Supermemory) -> None: memory = client.memories.delete( "id", ) - assert_matches_type(MemoryDeleteResponse, memory, path=["response"]) + assert memory is None @pytest.mark.skip() @parametrize @@ -146,7 +151,7 @@ def test_raw_response_delete(self, client: Supermemory) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" memory = response.parse() - assert_matches_type(MemoryDeleteResponse, memory, path=["response"]) + assert memory is None @pytest.mark.skip() @parametrize @@ -158,7 +163,7 @@ def test_streaming_response_delete(self, client: Supermemory) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" memory = response.parse() - assert_matches_type(MemoryDeleteResponse, memory, path=["response"]) + assert memory is None assert cast(Any, response.is_closed) is True @@ -183,7 +188,8 @@ def test_method_add(self, client: Supermemory) -> None: def test_method_add_with_all_params(self, client: Supermemory) -> None: memory = client.memories.add( content="This is a detailed article about machine learning concepts...", - container_tags=["string"], + container_tags=["user_123", "project_123"], + custom_id="mem_abc123", metadata={ "source": "web", "category": "technology", @@ -305,7 +311,8 @@ class TestAsyncMemories: @parametrize async def test_method_update(self, async_client: AsyncSupermemory) -> None: memory = await async_client.memories.update( - id="id", + path_id="id", + body_id="acxV5LHMEsG2hMSNb4umbn", content="This is a detailed article about machine learning concepts...", ) assert_matches_type(MemoryUpdateResponse, memory, path=["response"]) @@ -314,9 +321,11 @@ async def test_method_update(self, async_client: AsyncSupermemory) -> None: @parametrize async def test_method_update_with_all_params(self, async_client: AsyncSupermemory) -> None: memory = await async_client.memories.update( - id="id", + path_id="id", + body_id="acxV5LHMEsG2hMSNb4umbn", content="This is a detailed article about machine learning concepts...", - container_tags=["string"], + container_tags=["user_123", "project_123"], + custom_id="mem_abc123", metadata={ "source": "web", "category": "technology", @@ -332,7 +341,8 @@ async def test_method_update_with_all_params(self, async_client: AsyncSupermemor @parametrize async def test_raw_response_update(self, async_client: AsyncSupermemory) -> None: response = await async_client.memories.with_raw_response.update( - id="id", + path_id="id", + body_id="acxV5LHMEsG2hMSNb4umbn", content="This is a detailed article about machine learning concepts...", ) @@ -345,7 +355,8 @@ async def test_raw_response_update(self, async_client: AsyncSupermemory) -> None @parametrize async def test_streaming_response_update(self, async_client: AsyncSupermemory) -> None: async with async_client.memories.with_streaming_response.update( - id="id", + path_id="id", + body_id="acxV5LHMEsG2hMSNb4umbn", content="This is a detailed article about machine learning concepts...", ) as response: assert not response.is_closed @@ -359,9 +370,10 @@ async def test_streaming_response_update(self, async_client: AsyncSupermemory) - @pytest.mark.skip() @parametrize async def test_path_params_update(self, async_client: AsyncSupermemory) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `path_id` but received ''"): await async_client.memories.with_raw_response.update( - id="", + path_id="", + body_id="acxV5LHMEsG2hMSNb4umbn", content="This is a detailed article about machine learning concepts...", ) @@ -411,7 +423,7 @@ async def test_method_delete(self, async_client: AsyncSupermemory) -> None: memory = await async_client.memories.delete( "id", ) - assert_matches_type(MemoryDeleteResponse, memory, path=["response"]) + assert memory is None @pytest.mark.skip() @parametrize @@ -423,7 +435,7 @@ async def test_raw_response_delete(self, async_client: AsyncSupermemory) -> None assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" memory = await response.parse() - assert_matches_type(MemoryDeleteResponse, memory, path=["response"]) + assert memory is None @pytest.mark.skip() @parametrize @@ -435,7 +447,7 @@ async def test_streaming_response_delete(self, async_client: AsyncSupermemory) - assert response.http_request.headers.get("X-Stainless-Lang") == "python" memory = await response.parse() - assert_matches_type(MemoryDeleteResponse, memory, path=["response"]) + assert memory is None assert cast(Any, response.is_closed) is True @@ -460,7 +472,8 @@ async def test_method_add(self, async_client: AsyncSupermemory) -> None: async def test_method_add_with_all_params(self, async_client: AsyncSupermemory) -> None: memory = await async_client.memories.add( content="This is a detailed article about machine learning concepts...", - container_tags=["string"], + container_tags=["user_123", "project_123"], + custom_id="mem_abc123", metadata={ "source": "web", "category": "technology", From 1f9f01876a2754521ab8cc75c92634b05221f6c5 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 22 May 2025 02:30:01 +0000 Subject: [PATCH 02/48] chore(docs): grammar improvements --- SECURITY.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/SECURITY.md b/SECURITY.md index 80798832..858d41ff 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -16,11 +16,11 @@ before making any information public. ## Reporting Non-SDK Related Security Issues If you encounter security issues that are not directly related to SDKs but pertain to the services -or products provided by Supermemory please follow the respective company's security reporting guidelines. +or products provided by Supermemory, please follow the respective company's security reporting guidelines. ### Supermemory Terms and Policies -Please contact dhravya@supermemory.com for any questions or concerns regarding security of our services. +Please contact dhravya@supermemory.com for any questions or concerns regarding the security of our services. --- From 5bc5a8611c00f3c77c1e3787f578c91577ddfbb0 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 24 May 2025 20:17:05 +0000 Subject: [PATCH 03/48] feat(api): api update --- .stats.yml | 4 +- src/supermemory/resources/connections.py | 52 ++++++++----------- .../types/connection_create_params.py | 8 +-- tests/api_resources/test_connections.py | 8 +-- 4 files changed, 33 insertions(+), 39 deletions(-) diff --git a/.stats.yml b/.stats.yml index 322bc106..9cd1cd6f 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 12 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/supermemory--inc%2Fsupermemory-101f86a36ccd7f0652cfb6cac5d8f49e45ce3bee2b96692e8decd1eda3419604.yml -openapi_spec_hash: b6c08c22ab103285c7ada4eff95cfd0a +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/supermemory--inc%2Fsupermemory-302f75c936837c3e1163b7455cbec192cbf57f5769541258debfc1573c8597e2.yml +openapi_spec_hash: a36f25703f8a1ead0c95b7a7219096d5 config_hash: 8477e3ee6fd596ab6ac911d052e4de79 diff --git a/src/supermemory/resources/connections.py b/src/supermemory/resources/connections.py index dfc95e77..99f2aba9 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, List, Union, Optional from typing_extensions import Literal import httpx @@ -50,9 +50,9 @@ def create( self, provider: Literal["notion", "google-drive", "onedrive"], *, - end_user_id: str | NotGiven = NOT_GIVEN, - redirect_url: str | NotGiven = NOT_GIVEN, + container_tags: List[str] | NotGiven = NOT_GIVEN, metadata: Optional[Dict[str, Union[str, float, bool]]] | NotGiven = NOT_GIVEN, + redirect_url: str | NotGiven = NOT_GIVEN, # 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, @@ -76,19 +76,16 @@ def create( raise ValueError(f"Expected a non-empty value for `provider` but received {provider!r}") return self._post( f"/v3/connections/{provider}", - body=maybe_transform({"metadata": metadata}, connection_create_params.ConnectionCreateParams), + body=maybe_transform( + { + "container_tags": container_tags, + "metadata": metadata, + "redirect_url": redirect_url, + }, + connection_create_params.ConnectionCreateParams, + ), options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - { - "end_user_id": end_user_id, - "redirect_url": redirect_url, - }, - connection_create_params.ConnectionCreateParams, - ), + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=ConnectionCreateResponse, ) @@ -186,9 +183,9 @@ async def create( self, provider: Literal["notion", "google-drive", "onedrive"], *, - end_user_id: str | NotGiven = NOT_GIVEN, - redirect_url: str | NotGiven = NOT_GIVEN, + container_tags: List[str] | NotGiven = NOT_GIVEN, metadata: Optional[Dict[str, Union[str, float, bool]]] | NotGiven = NOT_GIVEN, + redirect_url: str | NotGiven = NOT_GIVEN, # 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, @@ -212,19 +209,16 @@ async def create( raise ValueError(f"Expected a non-empty value for `provider` but received {provider!r}") return await self._post( f"/v3/connections/{provider}", - body=await async_maybe_transform({"metadata": metadata}, connection_create_params.ConnectionCreateParams), + body=await async_maybe_transform( + { + "container_tags": container_tags, + "metadata": metadata, + "redirect_url": redirect_url, + }, + connection_create_params.ConnectionCreateParams, + ), options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=await async_maybe_transform( - { - "end_user_id": end_user_id, - "redirect_url": redirect_url, - }, - connection_create_params.ConnectionCreateParams, - ), + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=ConnectionCreateResponse, ) diff --git a/src/supermemory/types/connection_create_params.py b/src/supermemory/types/connection_create_params.py index 152cbce3..c2e21fab 100644 --- a/src/supermemory/types/connection_create_params.py +++ b/src/supermemory/types/connection_create_params.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import Dict, Union, Optional +from typing import Dict, List, Union, Optional from typing_extensions import Annotated, TypedDict from .._utils import PropertyInfo @@ -11,8 +11,8 @@ class ConnectionCreateParams(TypedDict, total=False): - end_user_id: Annotated[str, PropertyInfo(alias="endUserId")] - - redirect_url: Annotated[str, PropertyInfo(alias="redirectUrl")] + container_tags: Annotated[List[str], PropertyInfo(alias="containerTags")] metadata: Optional[Dict[str, Union[str, float, bool]]] + + redirect_url: Annotated[str, PropertyInfo(alias="redirectUrl")] diff --git a/tests/api_resources/test_connections.py b/tests/api_resources/test_connections.py index aee15f42..945a3e9f 100644 --- a/tests/api_resources/test_connections.py +++ b/tests/api_resources/test_connections.py @@ -34,9 +34,9 @@ def test_method_create(self, client: Supermemory) -> None: def test_method_create_with_all_params(self, client: Supermemory) -> None: connection = client.connections.create( provider="notion", - end_user_id="endUserId", - redirect_url="redirectUrl", + container_tags=["string"], metadata={"foo": "string"}, + redirect_url="redirectUrl", ) assert_matches_type(ConnectionCreateResponse, connection, path=["response"]) @@ -161,9 +161,9 @@ async def test_method_create(self, async_client: AsyncSupermemory) -> None: async def test_method_create_with_all_params(self, async_client: AsyncSupermemory) -> None: connection = await async_client.connections.create( provider="notion", - end_user_id="endUserId", - redirect_url="redirectUrl", + container_tags=["string"], metadata={"foo": "string"}, + redirect_url="redirectUrl", ) assert_matches_type(ConnectionCreateResponse, connection, path=["response"]) From 1788d476d76fd786ed495ee5f8048c301fae0799 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sun, 25 May 2025 04:16:57 +0000 Subject: [PATCH 04/48] feat(api): api update --- .stats.yml | 4 +- src/supermemory/resources/search.py | 23 +------- src/supermemory/types/memory_get_response.py | 55 ++++++++++++++++++- src/supermemory/types/memory_list_response.py | 23 -------- .../types/search_execute_params.py | 22 +------- tests/api_resources/test_search.py | 36 ------------ 6 files changed, 59 insertions(+), 104 deletions(-) diff --git a/.stats.yml b/.stats.yml index 9cd1cd6f..7003c41f 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 12 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/supermemory--inc%2Fsupermemory-302f75c936837c3e1163b7455cbec192cbf57f5769541258debfc1573c8597e2.yml -openapi_spec_hash: a36f25703f8a1ead0c95b7a7219096d5 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/supermemory--inc%2Fsupermemory-51f7b8cdabb689a8e8fa328e5c3b82906a2db418d486ef019bd0b2656e3a38aa.yml +openapi_spec_hash: cddc9d0c5e419657929408a3de9228db config_hash: 8477e3ee6fd596ab6ac911d052e4de79 diff --git a/src/supermemory/resources/search.py b/src/supermemory/resources/search.py index 4e293996..9aba76ef 100644 --- a/src/supermemory/resources/search.py +++ b/src/supermemory/resources/search.py @@ -2,9 +2,6 @@ from __future__ import annotations -from typing import List -from typing_extensions import Literal - import httpx from ..types import search_execute_params @@ -48,11 +45,9 @@ def execute( self, *, q: str, - categories_filter: List[Literal["technology", "science", "business", "health"]] | NotGiven = NOT_GIVEN, chunk_threshold: float | NotGiven = NOT_GIVEN, doc_id: str | NotGiven = NOT_GIVEN, document_threshold: float | NotGiven = NOT_GIVEN, - filters: search_execute_params.Filters | NotGiven = NOT_GIVEN, include_summary: bool | NotGiven = NOT_GIVEN, limit: int | NotGiven = NOT_GIVEN, only_matching_chunks: bool | NotGiven = NOT_GIVEN, @@ -67,13 +62,11 @@ def execute( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> SearchExecuteResponse: """ - Search memories with filtering + Search memories with basic filtering (simple query parameters only) Args: q: Search query string - categories_filter: Optional category filters - chunk_threshold: Threshold / sensitivity for chunk selection. 0 is least sensitive (returns most chunks, more results), 1 is most sensitive (returns lesser chunks, accurate results) @@ -85,8 +78,6 @@ def execute( most documents, more results), 1 is most sensitive (returns lesser documents, accurate results) - filters: Optional filters to apply to the search - include_summary: If true, include document summary in the response. This is helpful if you want a chatbot to know the full context of the document. @@ -123,11 +114,9 @@ def execute( query=maybe_transform( { "q": q, - "categories_filter": categories_filter, "chunk_threshold": chunk_threshold, "doc_id": doc_id, "document_threshold": document_threshold, - "filters": filters, "include_summary": include_summary, "limit": limit, "only_matching_chunks": only_matching_chunks, @@ -166,11 +155,9 @@ async def execute( self, *, q: str, - categories_filter: List[Literal["technology", "science", "business", "health"]] | NotGiven = NOT_GIVEN, chunk_threshold: float | NotGiven = NOT_GIVEN, doc_id: str | NotGiven = NOT_GIVEN, document_threshold: float | NotGiven = NOT_GIVEN, - filters: search_execute_params.Filters | NotGiven = NOT_GIVEN, include_summary: bool | NotGiven = NOT_GIVEN, limit: int | NotGiven = NOT_GIVEN, only_matching_chunks: bool | NotGiven = NOT_GIVEN, @@ -185,13 +172,11 @@ async def execute( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> SearchExecuteResponse: """ - Search memories with filtering + Search memories with basic filtering (simple query parameters only) Args: q: Search query string - categories_filter: Optional category filters - chunk_threshold: Threshold / sensitivity for chunk selection. 0 is least sensitive (returns most chunks, more results), 1 is most sensitive (returns lesser chunks, accurate results) @@ -203,8 +188,6 @@ async def execute( most documents, more results), 1 is most sensitive (returns lesser documents, accurate results) - filters: Optional filters to apply to the search - include_summary: If true, include document summary in the response. This is helpful if you want a chatbot to know the full context of the document. @@ -241,11 +224,9 @@ async def execute( query=await async_maybe_transform( { "q": q, - "categories_filter": categories_filter, "chunk_threshold": chunk_threshold, "doc_id": doc_id, "document_threshold": document_threshold, - "filters": filters, "include_summary": include_summary, "limit": limit, "only_matching_chunks": only_matching_chunks, diff --git a/src/supermemory/types/memory_get_response.py b/src/supermemory/types/memory_get_response.py index 4d115240..611c802f 100644 --- a/src/supermemory/types/memory_get_response.py +++ b/src/supermemory/types/memory_get_response.py @@ -1,11 +1,62 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +from typing import Dict, List, Union, Optional +from datetime import datetime +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + from .._models import BaseModel -__all__ = ["MemoryGetResponse"] +__all__ = ["MemoryGetResponse", "Doc"] -class MemoryGetResponse(BaseModel): +class Doc(BaseModel): id: str + """Unique identifier of the memory.""" + + content: Optional[str] = None + """The content to extract and process into a memory. + + This can be a URL to a website, a PDF, an image, or a video. + + Plaintext: Any plaintext format + + URL: A URL to a website, PDF, image, or video + + We automatically detect the content type from the url's response format. + """ + + created_at: datetime = FieldInfo(alias="createdAt") + """Creation timestamp""" + + metadata: Union[str, float, bool, Dict[str, object], List[object], None] = None + """Optional metadata for the memory. + + This is used to store additional information about the memory. You can use this + to store any additional information you need about the memory. Metadata can be + filtered through. Keys must be strings and are case sensitive. Values can be + strings, numbers, or booleans. You cannot nest objects. + """ + + status: Literal["unknown", "queued", "extracting", "chunking", "embedding", "indexing", "done", "failed"] + """Status of the memory""" + + summary: Optional[str] = None + """Summary of the memory content""" + + title: Optional[str] = None + """Title of the memory""" + + updated_at: datetime = FieldInfo(alias="updatedAt") + """Last update timestamp""" + + url: Optional[str] = None + """URL of the memory""" + + +class MemoryGetResponse(BaseModel): + doc: Doc + """Memory object""" status: str diff --git a/src/supermemory/types/memory_list_response.py b/src/supermemory/types/memory_list_response.py index 5b325c78..4eb10e9b 100644 --- a/src/supermemory/types/memory_list_response.py +++ b/src/supermemory/types/memory_list_response.py @@ -30,12 +30,6 @@ class Memory(BaseModel): created_at: datetime = FieldInfo(alias="createdAt") """Creation timestamp""" - custom_id: Optional[str] = FieldInfo(alias="customId", default=None) - """Optional custom ID of the memory. - - This could be an ID from your database that will uniquely identify this memory. - """ - metadata: Union[str, float, bool, Dict[str, object], List[object], None] = None """Optional metadata for the memory. @@ -45,10 +39,6 @@ class Memory(BaseModel): strings, numbers, or booleans. You cannot nest objects. """ - og_image: Optional[str] = FieldInfo(alias="ogImage", default=None) - - source: Optional[str] = None - status: Literal["unknown", "queued", "extracting", "chunking", "embedding", "indexing", "done", "failed"] """Status of the memory""" @@ -58,25 +48,12 @@ class Memory(BaseModel): title: Optional[str] = None """Title of the memory""" - type: Literal["text", "pdf", "tweet", "google_doc", "image", "video", "notion_doc", "webpage"] - """Type of the memory""" - updated_at: datetime = FieldInfo(alias="updatedAt") """Last update timestamp""" url: Optional[str] = None """URL of the memory""" - container_tags: Optional[List[str]] = FieldInfo(alias="containerTags", default=None) - """Optional tags this memory should be containerized by. - - This can be an ID for your user, a project ID, or any other identifier you wish - to use to group memories. - """ - - raw: None = None - """Raw content of the memory""" - class Pagination(BaseModel): current_page: float = FieldInfo(alias="currentPage") diff --git a/src/supermemory/types/search_execute_params.py b/src/supermemory/types/search_execute_params.py index bc3b4756..2919132c 100644 --- a/src/supermemory/types/search_execute_params.py +++ b/src/supermemory/types/search_execute_params.py @@ -2,23 +2,17 @@ from __future__ import annotations -from typing import Dict, List, Union, Iterable -from typing_extensions import Literal, Required, Annotated, TypeAlias, TypedDict +from typing_extensions import Required, Annotated, TypedDict from .._utils import PropertyInfo -__all__ = ["SearchExecuteParams", "Filters", "FiltersUnionMember0"] +__all__ = ["SearchExecuteParams"] class SearchExecuteParams(TypedDict, total=False): q: Required[str] """Search query string""" - categories_filter: Annotated[ - List[Literal["technology", "science", "business", "health"]], PropertyInfo(alias="categoriesFilter") - ] - """Optional category filters""" - chunk_threshold: Annotated[float, PropertyInfo(alias="chunkThreshold")] """Threshold / sensitivity for chunk selection. @@ -39,9 +33,6 @@ class SearchExecuteParams(TypedDict, total=False): (returns lesser documents, accurate results) """ - filters: Filters - """Optional filters to apply to the search""" - include_summary: Annotated[bool, PropertyInfo(alias="includeSummary")] """If true, include document summary in the response. @@ -75,12 +66,3 @@ class SearchExecuteParams(TypedDict, total=False): NOTE: This also acts as a filter for the search. """ - - -class FiltersUnionMember0(TypedDict, total=False): - and_: Annotated[Iterable[object], PropertyInfo(alias="AND")] - - or_: Annotated[Iterable[object], PropertyInfo(alias="OR")] - - -Filters: TypeAlias = Union[FiltersUnionMember0, Dict[str, object]] diff --git a/tests/api_resources/test_search.py b/tests/api_resources/test_search.py index c0557ff5..dd57a711 100644 --- a/tests/api_resources/test_search.py +++ b/tests/api_resources/test_search.py @@ -30,27 +30,9 @@ def test_method_execute(self, client: Supermemory) -> None: def test_method_execute_with_all_params(self, client: Supermemory) -> None: search = client.search.execute( q="machine learning concepts", - categories_filter=["technology", "science"], chunk_threshold=0.5, doc_id="doc_xyz789", document_threshold=0.5, - filters={ - "and_": [ - { - "key": "group", - "value": "jira_users", - "negate": False, - }, - { - "filterType": "numeric", - "key": "timestamp", - "value": "1742745777", - "negate": False, - "numericOperator": ">", - }, - ], - "or_": [{}], - }, include_summary=False, limit=10, only_matching_chunks=False, @@ -103,27 +85,9 @@ async def test_method_execute(self, async_client: AsyncSupermemory) -> None: async def test_method_execute_with_all_params(self, async_client: AsyncSupermemory) -> None: search = await async_client.search.execute( q="machine learning concepts", - categories_filter=["technology", "science"], chunk_threshold=0.5, doc_id="doc_xyz789", document_threshold=0.5, - filters={ - "and_": [ - { - "key": "group", - "value": "jira_users", - "negate": False, - }, - { - "filterType": "numeric", - "key": "timestamp", - "value": "1742745777", - "negate": False, - "numericOperator": ">", - }, - ], - "or_": [{}], - }, include_summary=False, limit=10, only_matching_chunks=False, From 02e8b0072cb84a52124f61f5d8f64d69809c7833 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sun, 25 May 2025 05:16:56 +0000 Subject: [PATCH 05/48] feat(api): api update --- .stats.yml | 4 +- src/supermemory/resources/search.py | 78 +------------------ .../types/search_execute_params.py | 42 ---------- tests/api_resources/test_search.py | 14 ---- 4 files changed, 4 insertions(+), 134 deletions(-) diff --git a/.stats.yml b/.stats.yml index 7003c41f..4010350f 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 12 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/supermemory--inc%2Fsupermemory-51f7b8cdabb689a8e8fa328e5c3b82906a2db418d486ef019bd0b2656e3a38aa.yml -openapi_spec_hash: cddc9d0c5e419657929408a3de9228db +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/supermemory--inc%2Fsupermemory-51498a77be155823007306ade088caca08102547dec1860625b8c111bf5a19b3.yml +openapi_spec_hash: 2a18759ac9605976ae0555be3735d711 config_hash: 8477e3ee6fd596ab6ac911d052e4de79 diff --git a/src/supermemory/resources/search.py b/src/supermemory/resources/search.py index 9aba76ef..15b9450d 100644 --- a/src/supermemory/resources/search.py +++ b/src/supermemory/resources/search.py @@ -45,14 +45,7 @@ def execute( self, *, q: str, - chunk_threshold: float | NotGiven = NOT_GIVEN, doc_id: str | NotGiven = NOT_GIVEN, - document_threshold: float | NotGiven = NOT_GIVEN, - include_summary: bool | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - only_matching_chunks: bool | NotGiven = NOT_GIVEN, - rerank: bool | NotGiven = NOT_GIVEN, - rewrite_query: bool | NotGiven = NOT_GIVEN, user_id: str | NotGiven = NOT_GIVEN, # 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. @@ -62,37 +55,14 @@ def execute( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> SearchExecuteResponse: """ - Search memories with basic filtering (simple query parameters only) + Search memories with basic filtering (Deprecated) Args: q: Search query string - chunk_threshold: Threshold / sensitivity for chunk selection. 0 is least sensitive (returns most - chunks, more results), 1 is most sensitive (returns lesser chunks, accurate - results) - doc_id: Optional document ID to search within. You can use this to find chunks in a very large document. - document_threshold: Threshold / sensitivity for document selection. 0 is least sensitive (returns - most documents, more results), 1 is most sensitive (returns lesser documents, - accurate results) - - include_summary: If true, include document summary in the response. This is helpful if you want a - chatbot to know the full context of the document. - - limit: Maximum number of results to return - - only_matching_chunks: If true, only return matching chunks without context. Normally, we send the - previous and next chunk to provide more context for LLMs. If you only want the - matching chunk, set this to true. - - 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 - user_id: End user ID this search is associated with. NOTE: This also acts as a filter for the search. @@ -114,14 +84,7 @@ def execute( query=maybe_transform( { "q": q, - "chunk_threshold": chunk_threshold, "doc_id": doc_id, - "document_threshold": document_threshold, - "include_summary": include_summary, - "limit": limit, - "only_matching_chunks": only_matching_chunks, - "rerank": rerank, - "rewrite_query": rewrite_query, "user_id": user_id, }, search_execute_params.SearchExecuteParams, @@ -155,14 +118,7 @@ async def execute( self, *, q: str, - chunk_threshold: float | NotGiven = NOT_GIVEN, doc_id: str | NotGiven = NOT_GIVEN, - document_threshold: float | NotGiven = NOT_GIVEN, - include_summary: bool | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - only_matching_chunks: bool | NotGiven = NOT_GIVEN, - rerank: bool | NotGiven = NOT_GIVEN, - rewrite_query: bool | NotGiven = NOT_GIVEN, user_id: str | NotGiven = NOT_GIVEN, # 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. @@ -172,37 +128,14 @@ async def execute( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> SearchExecuteResponse: """ - Search memories with basic filtering (simple query parameters only) + Search memories with basic filtering (Deprecated) Args: q: Search query string - chunk_threshold: Threshold / sensitivity for chunk selection. 0 is least sensitive (returns most - chunks, more results), 1 is most sensitive (returns lesser chunks, accurate - results) - doc_id: Optional document ID to search within. You can use this to find chunks in a very large document. - document_threshold: Threshold / sensitivity for document selection. 0 is least sensitive (returns - most documents, more results), 1 is most sensitive (returns lesser documents, - accurate results) - - include_summary: If true, include document summary in the response. This is helpful if you want a - chatbot to know the full context of the document. - - limit: Maximum number of results to return - - only_matching_chunks: If true, only return matching chunks without context. Normally, we send the - previous and next chunk to provide more context for LLMs. If you only want the - matching chunk, set this to true. - - 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 - user_id: End user ID this search is associated with. NOTE: This also acts as a filter for the search. @@ -224,14 +157,7 @@ async def execute( query=await async_maybe_transform( { "q": q, - "chunk_threshold": chunk_threshold, "doc_id": doc_id, - "document_threshold": document_threshold, - "include_summary": include_summary, - "limit": limit, - "only_matching_chunks": only_matching_chunks, - "rerank": rerank, - "rewrite_query": rewrite_query, "user_id": user_id, }, search_execute_params.SearchExecuteParams, diff --git a/src/supermemory/types/search_execute_params.py b/src/supermemory/types/search_execute_params.py index 2919132c..392ed495 100644 --- a/src/supermemory/types/search_execute_params.py +++ b/src/supermemory/types/search_execute_params.py @@ -13,54 +13,12 @@ class SearchExecuteParams(TypedDict, total=False): q: Required[str] """Search query string""" - chunk_threshold: Annotated[float, PropertyInfo(alias="chunkThreshold")] - """Threshold / sensitivity for chunk selection. - - 0 is least sensitive (returns most chunks, more results), 1 is most sensitive - (returns lesser chunks, accurate results) - """ - doc_id: Annotated[str, PropertyInfo(alias="docId")] """Optional document ID to search within. You can use this to find chunks in a very large document. """ - document_threshold: Annotated[float, PropertyInfo(alias="documentThreshold")] - """Threshold / sensitivity for document selection. - - 0 is least sensitive (returns most documents, more results), 1 is most sensitive - (returns lesser documents, accurate results) - """ - - include_summary: Annotated[bool, PropertyInfo(alias="includeSummary")] - """If true, include document summary in the response. - - This is helpful if you want a chatbot to know the full context of the document. - """ - - limit: int - """Maximum number of results to return""" - - only_matching_chunks: Annotated[bool, PropertyInfo(alias="onlyMatchingChunks")] - """If true, only return matching chunks without context. - - Normally, we send the previous and next chunk to provide more context for LLMs. - If you only want the matching chunk, set this to true. - """ - - 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 - """ - user_id: Annotated[str, PropertyInfo(alias="userId")] """End user ID this search is associated with. diff --git a/tests/api_resources/test_search.py b/tests/api_resources/test_search.py index dd57a711..842d7cf3 100644 --- a/tests/api_resources/test_search.py +++ b/tests/api_resources/test_search.py @@ -30,14 +30,7 @@ def test_method_execute(self, client: Supermemory) -> None: def test_method_execute_with_all_params(self, client: Supermemory) -> None: search = client.search.execute( q="machine learning concepts", - chunk_threshold=0.5, doc_id="doc_xyz789", - document_threshold=0.5, - include_summary=False, - limit=10, - only_matching_chunks=False, - rerank=False, - rewrite_query=False, user_id="user_123", ) assert_matches_type(SearchExecuteResponse, search, path=["response"]) @@ -85,14 +78,7 @@ async def test_method_execute(self, async_client: AsyncSupermemory) -> None: async def test_method_execute_with_all_params(self, async_client: AsyncSupermemory) -> None: search = await async_client.search.execute( q="machine learning concepts", - chunk_threshold=0.5, doc_id="doc_xyz789", - document_threshold=0.5, - include_summary=False, - limit=10, - only_matching_chunks=False, - rerank=False, - rewrite_query=False, user_id="user_123", ) assert_matches_type(SearchExecuteResponse, search, path=["response"]) From ca1c59663837d2294b5a79b1901ed45bcb6e8f93 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sun, 25 May 2025 22:16:58 +0000 Subject: [PATCH 06/48] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 4010350f..4eb236dc 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 12 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/supermemory--inc%2Fsupermemory-51498a77be155823007306ade088caca08102547dec1860625b8c111bf5a19b3.yml -openapi_spec_hash: 2a18759ac9605976ae0555be3735d711 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/supermemory--inc%2Fsupermemory-81e61ea85fe981dbb9354b20dd7cd26c3f04fcef985689d4aa9e24b7052a4636.yml +openapi_spec_hash: e8fb802c947666e8931e557ffbeb01f2 config_hash: 8477e3ee6fd596ab6ac911d052e4de79 From 7d2fded9eadfef95118fc101a185522e40eb9239 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sun, 25 May 2025 23:16:57 +0000 Subject: [PATCH 07/48] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 4eb236dc..4010350f 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 12 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/supermemory--inc%2Fsupermemory-81e61ea85fe981dbb9354b20dd7cd26c3f04fcef985689d4aa9e24b7052a4636.yml -openapi_spec_hash: e8fb802c947666e8931e557ffbeb01f2 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/supermemory--inc%2Fsupermemory-51498a77be155823007306ade088caca08102547dec1860625b8c111bf5a19b3.yml +openapi_spec_hash: 2a18759ac9605976ae0555be3735d711 config_hash: 8477e3ee6fd596ab6ac911d052e4de79 From 173a337626cbd75d951d10a04e91261ed3a2a384 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 26 May 2025 00:16:58 +0000 Subject: [PATCH 08/48] feat(api): api update --- .stats.yml | 4 ++-- src/supermemory/resources/search.py | 10 ---------- src/supermemory/types/search_execute_params.py | 6 ------ tests/api_resources/test_search.py | 2 -- 4 files changed, 2 insertions(+), 20 deletions(-) diff --git a/.stats.yml b/.stats.yml index 4010350f..5b791890 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 12 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/supermemory--inc%2Fsupermemory-51498a77be155823007306ade088caca08102547dec1860625b8c111bf5a19b3.yml -openapi_spec_hash: 2a18759ac9605976ae0555be3735d711 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/supermemory--inc%2Fsupermemory-be07470b9ac183653e025d87493d1fa66e1099c281d78c11b4822c56b81f3ce3.yml +openapi_spec_hash: ab48eeb62ea3b68bec83da9b3e246680 config_hash: 8477e3ee6fd596ab6ac911d052e4de79 diff --git a/src/supermemory/resources/search.py b/src/supermemory/resources/search.py index 15b9450d..851755e4 100644 --- a/src/supermemory/resources/search.py +++ b/src/supermemory/resources/search.py @@ -46,7 +46,6 @@ def execute( *, q: str, doc_id: str | NotGiven = NOT_GIVEN, - user_id: str | NotGiven = NOT_GIVEN, # 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, @@ -63,9 +62,6 @@ def execute( doc_id: Optional document ID to search within. You can use this to find chunks in a very large document. - user_id: End user ID this search is associated with. NOTE: This also acts as a filter for - the search. - extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -85,7 +81,6 @@ def execute( { "q": q, "doc_id": doc_id, - "user_id": user_id, }, search_execute_params.SearchExecuteParams, ), @@ -119,7 +114,6 @@ async def execute( *, q: str, doc_id: str | NotGiven = NOT_GIVEN, - user_id: str | NotGiven = NOT_GIVEN, # 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, @@ -136,9 +130,6 @@ async def execute( doc_id: Optional document ID to search within. You can use this to find chunks in a very large document. - user_id: End user ID this search is associated with. NOTE: This also acts as a filter for - the search. - extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -158,7 +149,6 @@ async def execute( { "q": q, "doc_id": doc_id, - "user_id": user_id, }, search_execute_params.SearchExecuteParams, ), diff --git a/src/supermemory/types/search_execute_params.py b/src/supermemory/types/search_execute_params.py index 392ed495..e7b8f595 100644 --- a/src/supermemory/types/search_execute_params.py +++ b/src/supermemory/types/search_execute_params.py @@ -18,9 +18,3 @@ class SearchExecuteParams(TypedDict, total=False): You can use this to find chunks in a very large document. """ - - user_id: Annotated[str, PropertyInfo(alias="userId")] - """End user ID this search is associated with. - - NOTE: This also acts as a filter for the search. - """ diff --git a/tests/api_resources/test_search.py b/tests/api_resources/test_search.py index 842d7cf3..57fdb8d0 100644 --- a/tests/api_resources/test_search.py +++ b/tests/api_resources/test_search.py @@ -31,7 +31,6 @@ def test_method_execute_with_all_params(self, client: Supermemory) -> None: search = client.search.execute( q="machine learning concepts", doc_id="doc_xyz789", - user_id="user_123", ) assert_matches_type(SearchExecuteResponse, search, path=["response"]) @@ -79,7 +78,6 @@ async def test_method_execute_with_all_params(self, async_client: AsyncSupermemo search = await async_client.search.execute( q="machine learning concepts", doc_id="doc_xyz789", - user_id="user_123", ) assert_matches_type(SearchExecuteResponse, search, path=["response"]) From beffc5e09b5aee9c1517453b2f5574881ecf5dd7 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 29 May 2025 07:17:06 +0000 Subject: [PATCH 09/48] feat(api): api update --- .stats.yml | 4 +-- api.md | 2 +- src/supermemory/resources/memories.py | 24 +++++-------- src/supermemory/types/memory_update_params.py | 3 -- tests/api_resources/test_memories.py | 34 +++++++------------ 5 files changed, 23 insertions(+), 44 deletions(-) diff --git a/.stats.yml b/.stats.yml index 5b791890..9acd0976 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 12 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/supermemory--inc%2Fsupermemory-be07470b9ac183653e025d87493d1fa66e1099c281d78c11b4822c56b81f3ce3.yml -openapi_spec_hash: ab48eeb62ea3b68bec83da9b3e246680 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/supermemory--inc%2Fsupermemory-c907416d5056243275fc6d1c3842e38096406a4a895f6523c2cbfddfca072ad8.yml +openapi_spec_hash: 0eef2d6ae43857f544ba7bcace19c755 config_hash: 8477e3ee6fd596ab6ac911d052e4de79 diff --git a/api.md b/api.md index 6035906a..9c6a5a39 100644 --- a/api.md +++ b/api.md @@ -14,7 +14,7 @@ from supermemory.types import ( Methods: -- client.memories.update(path_id, \*\*params) -> MemoryUpdateResponse +- client.memories.update(id, \*\*params) -> MemoryUpdateResponse - client.memories.list(\*\*params) -> MemoryListResponse - client.memories.delete(id) -> None - client.memories.add(\*\*params) -> MemoryAddResponse diff --git a/src/supermemory/resources/memories.py b/src/supermemory/resources/memories.py index 85be93a4..689c5aa0 100644 --- a/src/supermemory/resources/memories.py +++ b/src/supermemory/resources/memories.py @@ -50,9 +50,8 @@ def with_streaming_response(self) -> MemoriesResourceWithStreamingResponse: def update( self, - path_id: str, + id: str, *, - body_id: str, content: str, container_tags: List[str] | NotGiven = NOT_GIVEN, custom_id: str | NotGiven = NOT_GIVEN, @@ -68,8 +67,6 @@ def update( Update a memory with any content type (text, url, file, etc.) and metadata Args: - body_id: Unique identifier of the memory. - content: The content to extract and process into a memory. This can be a URL to a website, a PDF, an image, or a video. @@ -99,13 +96,12 @@ def update( timeout: Override the client-level default timeout for this request, in seconds """ - if not path_id: - raise ValueError(f"Expected a non-empty value for `path_id` but received {path_id!r}") + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._patch( - f"/v3/memories/{path_id}", + f"/v3/memories/{id}", body=maybe_transform( { - "body_id": body_id, "content": content, "container_tags": container_tags, "custom_id": custom_id, @@ -370,9 +366,8 @@ def with_streaming_response(self) -> AsyncMemoriesResourceWithStreamingResponse: async def update( self, - path_id: str, + id: str, *, - body_id: str, content: str, container_tags: List[str] | NotGiven = NOT_GIVEN, custom_id: str | NotGiven = NOT_GIVEN, @@ -388,8 +383,6 @@ async def update( Update a memory with any content type (text, url, file, etc.) and metadata Args: - body_id: Unique identifier of the memory. - content: The content to extract and process into a memory. This can be a URL to a website, a PDF, an image, or a video. @@ -419,13 +412,12 @@ async def update( timeout: Override the client-level default timeout for this request, in seconds """ - if not path_id: - raise ValueError(f"Expected a non-empty value for `path_id` but received {path_id!r}") + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._patch( - f"/v3/memories/{path_id}", + f"/v3/memories/{id}", body=await async_maybe_transform( { - "body_id": body_id, "content": content, "container_tags": container_tags, "custom_id": custom_id, diff --git a/src/supermemory/types/memory_update_params.py b/src/supermemory/types/memory_update_params.py index 10309d2d..2dc1d45d 100644 --- a/src/supermemory/types/memory_update_params.py +++ b/src/supermemory/types/memory_update_params.py @@ -11,9 +11,6 @@ class MemoryUpdateParams(TypedDict, total=False): - body_id: Required[Annotated[str, PropertyInfo(alias="id")]] - """Unique identifier of the memory.""" - content: Required[str] """The content to extract and process into a memory. diff --git a/tests/api_resources/test_memories.py b/tests/api_resources/test_memories.py index 2c3cca4d..146797e0 100644 --- a/tests/api_resources/test_memories.py +++ b/tests/api_resources/test_memories.py @@ -27,8 +27,7 @@ class TestMemories: @parametrize def test_method_update(self, client: Supermemory) -> None: memory = client.memories.update( - path_id="id", - body_id="acxV5LHMEsG2hMSNb4umbn", + id="id", content="This is a detailed article about machine learning concepts...", ) assert_matches_type(MemoryUpdateResponse, memory, path=["response"]) @@ -37,8 +36,7 @@ def test_method_update(self, client: Supermemory) -> None: @parametrize def test_method_update_with_all_params(self, client: Supermemory) -> None: memory = client.memories.update( - path_id="id", - body_id="acxV5LHMEsG2hMSNb4umbn", + id="id", content="This is a detailed article about machine learning concepts...", container_tags=["user_123", "project_123"], custom_id="mem_abc123", @@ -57,8 +55,7 @@ def test_method_update_with_all_params(self, client: Supermemory) -> None: @parametrize def test_raw_response_update(self, client: Supermemory) -> None: response = client.memories.with_raw_response.update( - path_id="id", - body_id="acxV5LHMEsG2hMSNb4umbn", + id="id", content="This is a detailed article about machine learning concepts...", ) @@ -71,8 +68,7 @@ def test_raw_response_update(self, client: Supermemory) -> None: @parametrize def test_streaming_response_update(self, client: Supermemory) -> None: with client.memories.with_streaming_response.update( - path_id="id", - body_id="acxV5LHMEsG2hMSNb4umbn", + id="id", content="This is a detailed article about machine learning concepts...", ) as response: assert not response.is_closed @@ -86,10 +82,9 @@ def test_streaming_response_update(self, client: Supermemory) -> None: @pytest.mark.skip() @parametrize def test_path_params_update(self, client: Supermemory) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `path_id` but received ''"): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): client.memories.with_raw_response.update( - path_id="", - body_id="acxV5LHMEsG2hMSNb4umbn", + id="", content="This is a detailed article about machine learning concepts...", ) @@ -311,8 +306,7 @@ class TestAsyncMemories: @parametrize async def test_method_update(self, async_client: AsyncSupermemory) -> None: memory = await async_client.memories.update( - path_id="id", - body_id="acxV5LHMEsG2hMSNb4umbn", + id="id", content="This is a detailed article about machine learning concepts...", ) assert_matches_type(MemoryUpdateResponse, memory, path=["response"]) @@ -321,8 +315,7 @@ async def test_method_update(self, async_client: AsyncSupermemory) -> None: @parametrize async def test_method_update_with_all_params(self, async_client: AsyncSupermemory) -> None: memory = await async_client.memories.update( - path_id="id", - body_id="acxV5LHMEsG2hMSNb4umbn", + id="id", content="This is a detailed article about machine learning concepts...", container_tags=["user_123", "project_123"], custom_id="mem_abc123", @@ -341,8 +334,7 @@ async def test_method_update_with_all_params(self, async_client: AsyncSupermemor @parametrize async def test_raw_response_update(self, async_client: AsyncSupermemory) -> None: response = await async_client.memories.with_raw_response.update( - path_id="id", - body_id="acxV5LHMEsG2hMSNb4umbn", + id="id", content="This is a detailed article about machine learning concepts...", ) @@ -355,8 +347,7 @@ async def test_raw_response_update(self, async_client: AsyncSupermemory) -> None @parametrize async def test_streaming_response_update(self, async_client: AsyncSupermemory) -> None: async with async_client.memories.with_streaming_response.update( - path_id="id", - body_id="acxV5LHMEsG2hMSNb4umbn", + id="id", content="This is a detailed article about machine learning concepts...", ) as response: assert not response.is_closed @@ -370,10 +361,9 @@ async def test_streaming_response_update(self, async_client: AsyncSupermemory) - @pytest.mark.skip() @parametrize async def test_path_params_update(self, async_client: AsyncSupermemory) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `path_id` but received ''"): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): await async_client.memories.with_raw_response.update( - path_id="", - body_id="acxV5LHMEsG2hMSNb4umbn", + id="", content="This is a detailed article about machine learning concepts...", ) From e95b77ee719a76f0eb22c56f07e90f05712de7cd Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sun, 1 Jun 2025 08:17:20 +0000 Subject: [PATCH 10/48] feat(api): api update --- .stats.yml | 4 +-- src/supermemory/types/memory_get_response.py | 29 +++++++++++++++---- src/supermemory/types/memory_list_response.py | 24 +++++++-------- 3 files changed, 36 insertions(+), 21 deletions(-) diff --git a/.stats.yml b/.stats.yml index 9acd0976..f0bf54eb 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 12 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/supermemory--inc%2Fsupermemory-c907416d5056243275fc6d1c3842e38096406a4a895f6523c2cbfddfca072ad8.yml -openapi_spec_hash: 0eef2d6ae43857f544ba7bcace19c755 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/supermemory--inc%2Fsupermemory-6d14bb475452b9220ee0f563688b6d5e7070e32def30bfc2716cdd90b50de943.yml +openapi_spec_hash: 6cf7bf30ba6361c7fbc7639fee70437f config_hash: 8477e3ee6fd596ab6ac911d052e4de79 diff --git a/src/supermemory/types/memory_get_response.py b/src/supermemory/types/memory_get_response.py index 611c802f..78390511 100644 --- a/src/supermemory/types/memory_get_response.py +++ b/src/supermemory/types/memory_get_response.py @@ -8,10 +8,10 @@ from .._models import BaseModel -__all__ = ["MemoryGetResponse", "Doc"] +__all__ = ["MemoryGetResponse"] -class Doc(BaseModel): +class MemoryGetResponse(BaseModel): id: str """Unique identifier of the memory.""" @@ -30,6 +30,12 @@ class Doc(BaseModel): created_at: datetime = FieldInfo(alias="createdAt") """Creation timestamp""" + custom_id: Optional[str] = FieldInfo(alias="customId", default=None) + """Optional custom ID of the memory. + + This could be an ID from your database that will uniquely identify this memory. + """ + metadata: Union[str, float, bool, Dict[str, object], List[object], None] = None """Optional metadata for the memory. @@ -39,6 +45,11 @@ class Doc(BaseModel): strings, numbers, or booleans. You cannot nest objects. """ + og_image: Optional[str] = FieldInfo(alias="ogImage", default=None) + + source: Optional[str] = None + """Source of the memory""" + status: Literal["unknown", "queued", "extracting", "chunking", "embedding", "indexing", "done", "failed"] """Status of the memory""" @@ -48,15 +59,21 @@ class Doc(BaseModel): title: Optional[str] = None """Title of the memory""" + type: Literal["text", "pdf", "tweet", "google_doc", "image", "video", "notion_doc", "webpage"] + """Type of the memory""" + updated_at: datetime = FieldInfo(alias="updatedAt") """Last update timestamp""" url: Optional[str] = None """URL of the memory""" + container_tags: Optional[List[str]] = FieldInfo(alias="containerTags", default=None) + """Optional tags this memory should be containerized by. -class MemoryGetResponse(BaseModel): - doc: Doc - """Memory object""" + This can be an ID for your user, a project ID, or any other identifier you wish + to use to group memories. + """ - status: str + raw: None = None + """Raw content of the memory""" diff --git a/src/supermemory/types/memory_list_response.py b/src/supermemory/types/memory_list_response.py index 4eb10e9b..90b3d012 100644 --- a/src/supermemory/types/memory_list_response.py +++ b/src/supermemory/types/memory_list_response.py @@ -15,21 +15,15 @@ class Memory(BaseModel): id: str """Unique identifier of the memory.""" - content: Optional[str] = None - """The content to extract and process into a memory. - - This can be a URL to a website, a PDF, an image, or a video. - - Plaintext: Any plaintext format + created_at: datetime = FieldInfo(alias="createdAt") + """Creation timestamp""" - URL: A URL to a website, PDF, image, or video + custom_id: Optional[str] = FieldInfo(alias="customId", default=None) + """Optional custom ID of the memory. - We automatically detect the content type from the url's response format. + This could be an ID from your database that will uniquely identify this memory. """ - created_at: datetime = FieldInfo(alias="createdAt") - """Creation timestamp""" - metadata: Union[str, float, bool, Dict[str, object], List[object], None] = None """Optional metadata for the memory. @@ -51,8 +45,12 @@ class Memory(BaseModel): updated_at: datetime = FieldInfo(alias="updatedAt") """Last update timestamp""" - url: Optional[str] = None - """URL of the memory""" + container_tags: Optional[List[str]] = FieldInfo(alias="containerTags", default=None) + """Optional tags this memory should be containerized by. + + This can be an ID for your user, a project ID, or any other identifier you wish + to use to group memories. + """ class Pagination(BaseModel): From f58d93dc249798bdf62c00335c6b9bfdf8430795 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 2 Jun 2025 19:17:21 +0000 Subject: [PATCH 11/48] feat(api): api update --- .stats.yml | 4 +- api.md | 2 +- src/supermemory/resources/connections.py | 44 +++---------------- src/supermemory/types/__init__.py | 1 - .../types/connection_list_params.py | 13 ------ tests/api_resources/test_connections.py | 16 ------- 6 files changed, 8 insertions(+), 72 deletions(-) delete mode 100644 src/supermemory/types/connection_list_params.py diff --git a/.stats.yml b/.stats.yml index f0bf54eb..1dc922b9 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 12 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/supermemory--inc%2Fsupermemory-6d14bb475452b9220ee0f563688b6d5e7070e32def30bfc2716cdd90b50de943.yml -openapi_spec_hash: 6cf7bf30ba6361c7fbc7639fee70437f +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/supermemory--inc%2Fsupermemory-b60a3e75b9b6d64bb82b80cbf544f414f618a4c7f881220d8d8accd77b117e44.yml +openapi_spec_hash: 84d972259e24260e09ec3c07612c55ed config_hash: 8477e3ee6fd596ab6ac911d052e4de79 diff --git a/api.md b/api.md index 9c6a5a39..b4e1d214 100644 --- a/api.md +++ b/api.md @@ -61,5 +61,5 @@ from supermemory.types import ( Methods: - client.connections.create(provider, \*\*params) -> ConnectionCreateResponse -- client.connections.list(\*\*params) -> ConnectionListResponse +- client.connections.list() -> ConnectionListResponse - client.connections.get(connection_id) -> ConnectionGetResponse diff --git a/src/supermemory/resources/connections.py b/src/supermemory/resources/connections.py index 99f2aba9..c969bcf1 100644 --- a/src/supermemory/resources/connections.py +++ b/src/supermemory/resources/connections.py @@ -7,7 +7,7 @@ import httpx -from ..types import connection_list_params, connection_create_params +from ..types import connection_create_params from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven from .._utils import maybe_transform, async_maybe_transform from .._compat import cached_property @@ -93,7 +93,6 @@ def create( def list( self, *, - end_user_id: str | NotGiven = NOT_GIVEN, # 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, @@ -101,26 +100,11 @@ def list( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> ConnectionListResponse: - """ - List all connections - - 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 - """ + """List all connections""" return self._get( "/v3/connections", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform({"end_user_id": end_user_id}, connection_list_params.ConnectionListParams), + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=ConnectionListResponse, ) @@ -226,7 +210,6 @@ async def create( async def list( self, *, - end_user_id: str | NotGiven = NOT_GIVEN, # 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, @@ -234,28 +217,11 @@ async def list( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> ConnectionListResponse: - """ - List all connections - - 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 - """ + """List all connections""" return await self._get( "/v3/connections", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=await async_maybe_transform( - {"end_user_id": end_user_id}, connection_list_params.ConnectionListParams - ), + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=ConnectionListResponse, ) diff --git a/src/supermemory/types/__init__.py b/src/supermemory/types/__init__.py index 6d9b94d2..b70f43d5 100644 --- a/src/supermemory/types/__init__.py +++ b/src/supermemory/types/__init__.py @@ -11,7 +11,6 @@ from .setting_get_response import SettingGetResponse as SettingGetResponse from .search_execute_params import SearchExecuteParams as SearchExecuteParams from .setting_update_params import SettingUpdateParams as SettingUpdateParams -from .connection_list_params import ConnectionListParams as ConnectionListParams from .memory_update_response import MemoryUpdateResponse as MemoryUpdateResponse from .connection_get_response import ConnectionGetResponse as ConnectionGetResponse from .search_execute_response import SearchExecuteResponse as SearchExecuteResponse diff --git a/src/supermemory/types/connection_list_params.py b/src/supermemory/types/connection_list_params.py deleted file mode 100644 index e027d5f1..00000000 --- a/src/supermemory/types/connection_list_params.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Annotated, TypedDict - -from .._utils import PropertyInfo - -__all__ = ["ConnectionListParams"] - - -class ConnectionListParams(TypedDict, total=False): - end_user_id: Annotated[str, PropertyInfo(alias="endUserId")] diff --git a/tests/api_resources/test_connections.py b/tests/api_resources/test_connections.py index 945a3e9f..c8838b83 100644 --- a/tests/api_resources/test_connections.py +++ b/tests/api_resources/test_connections.py @@ -72,14 +72,6 @@ def test_method_list(self, client: Supermemory) -> None: connection = client.connections.list() assert_matches_type(ConnectionListResponse, connection, path=["response"]) - @pytest.mark.skip() - @parametrize - def test_method_list_with_all_params(self, client: Supermemory) -> None: - connection = client.connections.list( - end_user_id="endUserId", - ) - assert_matches_type(ConnectionListResponse, connection, path=["response"]) - @pytest.mark.skip() @parametrize def test_raw_response_list(self, client: Supermemory) -> None: @@ -199,14 +191,6 @@ async def test_method_list(self, async_client: AsyncSupermemory) -> None: connection = await async_client.connections.list() assert_matches_type(ConnectionListResponse, connection, path=["response"]) - @pytest.mark.skip() - @parametrize - async def test_method_list_with_all_params(self, async_client: AsyncSupermemory) -> None: - connection = await async_client.connections.list( - end_user_id="endUserId", - ) - assert_matches_type(ConnectionListResponse, connection, path=["response"]) - @pytest.mark.skip() @parametrize async def test_raw_response_list(self, async_client: AsyncSupermemory) -> None: From cd4782de2b4f05196e6b3f37768de900a6a9e81d Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 2 Jun 2025 22:17:20 +0000 Subject: [PATCH 12/48] feat(api): api update --- .stats.yml | 6 +- api.md | 7 +-- src/supermemory/resources/connections.py | 51 --------------- src/supermemory/types/__init__.py | 1 - .../types/connection_list_response.py | 25 -------- tests/api_resources/test_connections.py | 62 +------------------ 6 files changed, 5 insertions(+), 147 deletions(-) delete mode 100644 src/supermemory/types/connection_list_response.py diff --git a/.stats.yml b/.stats.yml index 1dc922b9..b5a21500 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 12 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/supermemory--inc%2Fsupermemory-b60a3e75b9b6d64bb82b80cbf544f414f618a4c7f881220d8d8accd77b117e44.yml -openapi_spec_hash: 84d972259e24260e09ec3c07612c55ed +configured_endpoints: 11 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/supermemory--inc%2Fsupermemory-c57750052aa770c1f554e81b2fcdaf9e4c4d2524a0cec568edbee560fde44dde.yml +openapi_spec_hash: 3adaefca8d4d5c84ee4106e8ba10cabb config_hash: 8477e3ee6fd596ab6ac911d052e4de79 diff --git a/api.md b/api.md index b4e1d214..4ef1f5f6 100644 --- a/api.md +++ b/api.md @@ -51,15 +51,10 @@ Methods: Types: ```python -from supermemory.types import ( - ConnectionCreateResponse, - ConnectionListResponse, - ConnectionGetResponse, -) +from supermemory.types import ConnectionCreateResponse, ConnectionGetResponse ``` Methods: - client.connections.create(provider, \*\*params) -> ConnectionCreateResponse -- client.connections.list() -> ConnectionListResponse - client.connections.get(connection_id) -> ConnectionGetResponse diff --git a/src/supermemory/resources/connections.py b/src/supermemory/resources/connections.py index c969bcf1..33c9c1ec 100644 --- a/src/supermemory/resources/connections.py +++ b/src/supermemory/resources/connections.py @@ -20,7 +20,6 @@ ) from .._base_client import make_request_options from ..types.connection_get_response import ConnectionGetResponse -from ..types.connection_list_response import ConnectionListResponse from ..types.connection_create_response import ConnectionCreateResponse __all__ = ["ConnectionsResource", "AsyncConnectionsResource"] @@ -90,25 +89,6 @@ def create( cast_to=ConnectionCreateResponse, ) - def list( - self, - *, - # 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, - ) -> ConnectionListResponse: - """List all connections""" - return self._get( - "/v3/connections", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=ConnectionListResponse, - ) - def get( self, connection_id: str, @@ -207,25 +187,6 @@ async def create( cast_to=ConnectionCreateResponse, ) - async def list( - self, - *, - # 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, - ) -> ConnectionListResponse: - """List all connections""" - return await self._get( - "/v3/connections", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=ConnectionListResponse, - ) - async def get( self, connection_id: str, @@ -267,9 +228,6 @@ def __init__(self, connections: ConnectionsResource) -> None: self.create = to_raw_response_wrapper( connections.create, ) - self.list = to_raw_response_wrapper( - connections.list, - ) self.get = to_raw_response_wrapper( connections.get, ) @@ -282,9 +240,6 @@ def __init__(self, connections: AsyncConnectionsResource) -> None: self.create = async_to_raw_response_wrapper( connections.create, ) - self.list = async_to_raw_response_wrapper( - connections.list, - ) self.get = async_to_raw_response_wrapper( connections.get, ) @@ -297,9 +252,6 @@ def __init__(self, connections: ConnectionsResource) -> None: self.create = to_streamed_response_wrapper( connections.create, ) - self.list = to_streamed_response_wrapper( - connections.list, - ) self.get = to_streamed_response_wrapper( connections.get, ) @@ -312,9 +264,6 @@ def __init__(self, connections: AsyncConnectionsResource) -> None: self.create = async_to_streamed_response_wrapper( connections.create, ) - self.list = async_to_streamed_response_wrapper( - connections.list, - ) self.get = async_to_streamed_response_wrapper( connections.get, ) diff --git a/src/supermemory/types/__init__.py b/src/supermemory/types/__init__.py index b70f43d5..652e1a43 100644 --- a/src/supermemory/types/__init__.py +++ b/src/supermemory/types/__init__.py @@ -16,7 +16,6 @@ from .search_execute_response import SearchExecuteResponse as SearchExecuteResponse from .setting_update_response import SettingUpdateResponse as SettingUpdateResponse from .connection_create_params import ConnectionCreateParams as ConnectionCreateParams -from .connection_list_response import ConnectionListResponse as ConnectionListResponse from .memory_upload_file_params import MemoryUploadFileParams as MemoryUploadFileParams from .connection_create_response import ConnectionCreateResponse as ConnectionCreateResponse from .memory_upload_file_response import MemoryUploadFileResponse as MemoryUploadFileResponse diff --git a/src/supermemory/types/connection_list_response.py b/src/supermemory/types/connection_list_response.py deleted file mode 100644 index f68e38b8..00000000 --- a/src/supermemory/types/connection_list_response.py +++ /dev/null @@ -1,25 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Dict, List, Optional -from typing_extensions import TypeAlias - -from pydantic import Field as FieldInfo - -from .._models import BaseModel - -__all__ = ["ConnectionListResponse", "ConnectionListResponseItem"] - - -class ConnectionListResponseItem(BaseModel): - id: str - - created_at: float = FieldInfo(alias="createdAt") - - provider: str - - expires_at: Optional[float] = FieldInfo(alias="expiresAt", default=None) - - metadata: Optional[Dict[str, object]] = None - - -ConnectionListResponse: TypeAlias = List[ConnectionListResponseItem] diff --git a/tests/api_resources/test_connections.py b/tests/api_resources/test_connections.py index c8838b83..29f199c1 100644 --- a/tests/api_resources/test_connections.py +++ b/tests/api_resources/test_connections.py @@ -9,11 +9,7 @@ from supermemory import Supermemory, AsyncSupermemory from tests.utils import assert_matches_type -from supermemory.types import ( - ConnectionGetResponse, - ConnectionListResponse, - ConnectionCreateResponse, -) +from supermemory.types import ConnectionGetResponse, ConnectionCreateResponse base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -66,34 +62,6 @@ def test_streaming_response_create(self, client: Supermemory) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() - @parametrize - def test_method_list(self, client: Supermemory) -> None: - connection = client.connections.list() - assert_matches_type(ConnectionListResponse, connection, path=["response"]) - - @pytest.mark.skip() - @parametrize - def test_raw_response_list(self, client: Supermemory) -> None: - response = client.connections.with_raw_response.list() - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - connection = response.parse() - assert_matches_type(ConnectionListResponse, connection, path=["response"]) - - @pytest.mark.skip() - @parametrize - def test_streaming_response_list(self, client: Supermemory) -> None: - with client.connections.with_streaming_response.list() as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - connection = response.parse() - assert_matches_type(ConnectionListResponse, connection, path=["response"]) - - assert cast(Any, response.is_closed) is True - @pytest.mark.skip() @parametrize def test_method_get(self, client: Supermemory) -> None: @@ -185,34 +153,6 @@ async def test_streaming_response_create(self, async_client: AsyncSupermemory) - assert cast(Any, response.is_closed) is True - @pytest.mark.skip() - @parametrize - async def test_method_list(self, async_client: AsyncSupermemory) -> None: - connection = await async_client.connections.list() - assert_matches_type(ConnectionListResponse, connection, path=["response"]) - - @pytest.mark.skip() - @parametrize - async def test_raw_response_list(self, async_client: AsyncSupermemory) -> None: - response = await async_client.connections.with_raw_response.list() - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - connection = await response.parse() - assert_matches_type(ConnectionListResponse, connection, path=["response"]) - - @pytest.mark.skip() - @parametrize - async def test_streaming_response_list(self, async_client: AsyncSupermemory) -> None: - async with async_client.connections.with_streaming_response.list() as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - connection = await response.parse() - assert_matches_type(ConnectionListResponse, connection, path=["response"]) - - assert cast(Any, response.is_closed) is True - @pytest.mark.skip() @parametrize async def test_method_get(self, async_client: AsyncSupermemory) -> None: From a0d7515e7655cc420af0b62d85cc7885002bb4bd Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 3 Jun 2025 00:17:21 +0000 Subject: [PATCH 13/48] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index b5a21500..f99358b5 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 11 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/supermemory--inc%2Fsupermemory-c57750052aa770c1f554e81b2fcdaf9e4c4d2524a0cec568edbee560fde44dde.yml -openapi_spec_hash: 3adaefca8d4d5c84ee4106e8ba10cabb +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/supermemory--inc%2Fsupermemory-22abdebdde60ed0948dd9349f80924d026119a7570df7d636d6cdc50bcaf1230.yml +openapi_spec_hash: 01dc74531f781b03d7f023fa52f292c6 config_hash: 8477e3ee6fd596ab6ac911d052e4de79 From 51586e68f9ec7b04b7aba149142f6e550113c0b9 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 3 Jun 2025 01:17:22 +0000 Subject: [PATCH 14/48] feat(api): api update --- .stats.yml | 4 ++-- src/supermemory/types/memory_get_response.py | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index f99358b5..0f70f8c9 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 11 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/supermemory--inc%2Fsupermemory-22abdebdde60ed0948dd9349f80924d026119a7570df7d636d6cdc50bcaf1230.yml -openapi_spec_hash: 01dc74531f781b03d7f023fa52f292c6 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/supermemory--inc%2Fsupermemory-b4d7b52f83f90e73f5a8dc93586b0e44987e34240a2893491fded1d8b1620b3f.yml +openapi_spec_hash: 2b02dc5e58ba20bbee0770c81bf92917 config_hash: 8477e3ee6fd596ab6ac911d052e4de79 diff --git a/src/supermemory/types/memory_get_response.py b/src/supermemory/types/memory_get_response.py index 78390511..fbb33e02 100644 --- a/src/supermemory/types/memory_get_response.py +++ b/src/supermemory/types/memory_get_response.py @@ -15,6 +15,8 @@ class MemoryGetResponse(BaseModel): id: str """Unique identifier of the memory.""" + connection_id: Optional[str] = FieldInfo(alias="connectionId", default=None) + content: Optional[str] = None """The content to extract and process into a memory. From 23840c8b8a67cb3b60e94c1eb74994d28b3508f1 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 3 Jun 2025 02:21:22 +0000 Subject: [PATCH 15/48] chore(docs): remove reference to rye shell --- CONTRIBUTING.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9426a1fa..0f988c37 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -17,8 +17,7 @@ $ rye sync --all-features You can then run scripts using `rye run python script.py` or by activating the virtual environment: ```sh -$ rye shell -# or manually activate - https://docs.python.org/3/library/venv.html#how-venvs-work +# Activate the virtual environment - https://docs.python.org/3/library/venv.html#how-venvs-work $ source .venv/bin/activate # now you can omit the `rye run` prefix From abd637db1e3642c0dcc609bf4cac3d0a2bdad061 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 3 Jun 2025 03:34:47 +0000 Subject: [PATCH 16/48] feat(client): add follow_redirects request option --- src/supermemory/_base_client.py | 6 ++++ src/supermemory/_models.py | 2 ++ src/supermemory/_types.py | 2 ++ tests/test_client.py | 54 +++++++++++++++++++++++++++++++++ 4 files changed, 64 insertions(+) diff --git a/src/supermemory/_base_client.py b/src/supermemory/_base_client.py index ca79f9a6..8ebfbf2a 100644 --- a/src/supermemory/_base_client.py +++ b/src/supermemory/_base_client.py @@ -960,6 +960,9 @@ def request( if self.custom_auth is not None: kwargs["auth"] = self.custom_auth + if options.follow_redirects is not None: + kwargs["follow_redirects"] = options.follow_redirects + log.debug("Sending HTTP Request: %s %s", request.method, request.url) response = None @@ -1460,6 +1463,9 @@ async def request( if self.custom_auth is not None: kwargs["auth"] = self.custom_auth + if options.follow_redirects is not None: + kwargs["follow_redirects"] = options.follow_redirects + log.debug("Sending HTTP Request: %s %s", request.method, request.url) response = None diff --git a/src/supermemory/_models.py b/src/supermemory/_models.py index 798956f1..4f214980 100644 --- a/src/supermemory/_models.py +++ b/src/supermemory/_models.py @@ -737,6 +737,7 @@ class FinalRequestOptionsInput(TypedDict, total=False): idempotency_key: str json_data: Body extra_json: AnyMapping + follow_redirects: bool @final @@ -750,6 +751,7 @@ class FinalRequestOptions(pydantic.BaseModel): files: Union[HttpxRequestFiles, None] = None idempotency_key: Union[str, None] = None post_parser: Union[Callable[[Any], Any], NotGiven] = NotGiven() + follow_redirects: Union[bool, None] = None # It should be noted that we cannot use `json` here as that would override # a BaseModel method in an incompatible fashion. diff --git a/src/supermemory/_types.py b/src/supermemory/_types.py index 22b4e97c..8491d513 100644 --- a/src/supermemory/_types.py +++ b/src/supermemory/_types.py @@ -100,6 +100,7 @@ class RequestOptions(TypedDict, total=False): params: Query extra_json: AnyMapping idempotency_key: str + follow_redirects: bool # Sentinel class used until PEP 0661 is accepted @@ -215,3 +216,4 @@ class _GenericAlias(Protocol): class HttpxSendArgs(TypedDict, total=False): auth: httpx.Auth + follow_redirects: bool diff --git a/tests/test_client.py b/tests/test_client.py index 3636a001..dc292b03 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -846,6 +846,33 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: assert response.http_request.headers.get("x-stainless-retry-count") == "42" + @pytest.mark.respx(base_url=base_url) + def test_follow_redirects(self, respx_mock: MockRouter) -> None: + # Test that the default follow_redirects=True allows following redirects + respx_mock.post("/redirect").mock( + return_value=httpx.Response(302, headers={"Location": f"{base_url}/redirected"}) + ) + respx_mock.get("/redirected").mock(return_value=httpx.Response(200, json={"status": "ok"})) + + response = self.client.post("/redirect", body={"key": "value"}, cast_to=httpx.Response) + assert response.status_code == 200 + assert response.json() == {"status": "ok"} + + @pytest.mark.respx(base_url=base_url) + def test_follow_redirects_disabled(self, respx_mock: MockRouter) -> None: + # Test that follow_redirects=False prevents following redirects + respx_mock.post("/redirect").mock( + return_value=httpx.Response(302, headers={"Location": f"{base_url}/redirected"}) + ) + + with pytest.raises(APIStatusError) as exc_info: + self.client.post( + "/redirect", body={"key": "value"}, options={"follow_redirects": False}, cast_to=httpx.Response + ) + + assert exc_info.value.response.status_code == 302 + assert exc_info.value.response.headers["Location"] == f"{base_url}/redirected" + class TestAsyncSupermemory: client = AsyncSupermemory(base_url=base_url, api_key=api_key, _strict_response_validation=True) @@ -1687,3 +1714,30 @@ async def test_main() -> None: raise AssertionError("calling get_platform using asyncify resulted in a hung process") time.sleep(0.1) + + @pytest.mark.respx(base_url=base_url) + async def test_follow_redirects(self, respx_mock: MockRouter) -> None: + # Test that the default follow_redirects=True allows following redirects + respx_mock.post("/redirect").mock( + return_value=httpx.Response(302, headers={"Location": f"{base_url}/redirected"}) + ) + respx_mock.get("/redirected").mock(return_value=httpx.Response(200, json={"status": "ok"})) + + response = await self.client.post("/redirect", body={"key": "value"}, cast_to=httpx.Response) + assert response.status_code == 200 + assert response.json() == {"status": "ok"} + + @pytest.mark.respx(base_url=base_url) + async def test_follow_redirects_disabled(self, respx_mock: MockRouter) -> None: + # Test that follow_redirects=False prevents following redirects + respx_mock.post("/redirect").mock( + return_value=httpx.Response(302, headers={"Location": f"{base_url}/redirected"}) + ) + + with pytest.raises(APIStatusError) as exc_info: + await self.client.post( + "/redirect", body={"key": "value"}, options={"follow_redirects": False}, cast_to=httpx.Response + ) + + assert exc_info.value.response.status_code == 302 + assert exc_info.value.response.headers["Location"] == f"{base_url}/redirected" From 07a519615d7a49858b823d86ae571cb2a841f121 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 3 Jun 2025 20:17:38 +0000 Subject: [PATCH 17/48] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 0f70f8c9..cfa14c39 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 11 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/supermemory--inc%2Fsupermemory-b4d7b52f83f90e73f5a8dc93586b0e44987e34240a2893491fded1d8b1620b3f.yml -openapi_spec_hash: 2b02dc5e58ba20bbee0770c81bf92917 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/supermemory--inc%2Fsupermemory-bb9938937010738b975d3dd7014e1d1d5a31b921e333a778f3348528a7b5d5e9.yml +openapi_spec_hash: 4fe7bc7771fd7804dbf78354ce7f745d config_hash: 8477e3ee6fd596ab6ac911d052e4de79 From 755cef6c460a591e90d69d874debd24b0486c75e Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 3 Jun 2025 22:17:36 +0000 Subject: [PATCH 18/48] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index cfa14c39..da51650e 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 11 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/supermemory--inc%2Fsupermemory-bb9938937010738b975d3dd7014e1d1d5a31b921e333a778f3348528a7b5d5e9.yml -openapi_spec_hash: 4fe7bc7771fd7804dbf78354ce7f745d +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/supermemory--inc%2Fsupermemory-2dc17adc2b9fedfbcbee5a88d469b66980169c89af5e1475d2a2ee5aeebc503c.yml +openapi_spec_hash: fc7e963d497cf01d350d46cb1ff98732 config_hash: 8477e3ee6fd596ab6ac911d052e4de79 From 02180e296f97a30626b183ba533f45879981cc62 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 4 Jun 2025 19:17:40 +0000 Subject: [PATCH 19/48] feat(api): api update --- .stats.yml | 4 ++-- src/supermemory/resources/memories.py | 10 ++++++++++ src/supermemory/types/memory_get_response.py | 2 -- src/supermemory/types/memory_list_params.py | 12 +++++++++++- tests/api_resources/test_memories.py | 2 ++ 5 files changed, 25 insertions(+), 5 deletions(-) diff --git a/.stats.yml b/.stats.yml index da51650e..9aa3723d 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 11 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/supermemory--inc%2Fsupermemory-2dc17adc2b9fedfbcbee5a88d469b66980169c89af5e1475d2a2ee5aeebc503c.yml -openapi_spec_hash: fc7e963d497cf01d350d46cb1ff98732 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/supermemory--inc%2Fsupermemory-34f3b1235237761dc6eb4292823363cd8f53be760c3883c127da4a23dd1475fa.yml +openapi_spec_hash: 932c86561ab153d35f3313317ccff0cb config_hash: 8477e3ee6fd596ab6ac911d052e4de79 diff --git a/src/supermemory/resources/memories.py b/src/supermemory/resources/memories.py index 689c5aa0..9705200d 100644 --- a/src/supermemory/resources/memories.py +++ b/src/supermemory/resources/memories.py @@ -118,6 +118,7 @@ def update( def list( self, *, + container_tags: List[str] | NotGiven = NOT_GIVEN, filters: str | NotGiven = NOT_GIVEN, limit: str | NotGiven = NOT_GIVEN, order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, @@ -134,6 +135,9 @@ def list( Retrieves a paginated list of memories with their metadata and workflow status Args: + container_tags: Optional tags this memory should be containerized by. This can be an ID for your + user, a project ID, or any other identifier you wish to use to group memories. + filters: Optional filters to apply to the search limit: Number of items per page @@ -161,6 +165,7 @@ def list( timeout=timeout, query=maybe_transform( { + "container_tags": container_tags, "filters": filters, "limit": limit, "order": order, @@ -434,6 +439,7 @@ async def update( async def list( self, *, + container_tags: List[str] | NotGiven = NOT_GIVEN, filters: str | NotGiven = NOT_GIVEN, limit: str | NotGiven = NOT_GIVEN, order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, @@ -450,6 +456,9 @@ async def list( Retrieves a paginated list of memories with their metadata and workflow status Args: + container_tags: Optional tags this memory should be containerized by. This can be an ID for your + user, a project ID, or any other identifier you wish to use to group memories. + filters: Optional filters to apply to the search limit: Number of items per page @@ -477,6 +486,7 @@ async def list( timeout=timeout, query=await async_maybe_transform( { + "container_tags": container_tags, "filters": filters, "limit": limit, "order": order, diff --git a/src/supermemory/types/memory_get_response.py b/src/supermemory/types/memory_get_response.py index fbb33e02..78390511 100644 --- a/src/supermemory/types/memory_get_response.py +++ b/src/supermemory/types/memory_get_response.py @@ -15,8 +15,6 @@ class MemoryGetResponse(BaseModel): id: str """Unique identifier of the memory.""" - connection_id: Optional[str] = FieldInfo(alias="connectionId", default=None) - content: Optional[str] = None """The content to extract and process into a memory. diff --git a/src/supermemory/types/memory_list_params.py b/src/supermemory/types/memory_list_params.py index d40690da..7895ad1d 100644 --- a/src/supermemory/types/memory_list_params.py +++ b/src/supermemory/types/memory_list_params.py @@ -2,12 +2,22 @@ from __future__ import annotations -from typing_extensions import Literal, TypedDict +from typing import List +from typing_extensions import Literal, Annotated, TypedDict + +from .._utils import PropertyInfo __all__ = ["MemoryListParams"] class MemoryListParams(TypedDict, total=False): + container_tags: Annotated[List[str], PropertyInfo(alias="containerTags")] + """Optional tags this memory should be containerized by. + + This can be an ID for your user, a project ID, or any other identifier you wish + to use to group memories. + """ + filters: str """Optional filters to apply to the search""" diff --git a/tests/api_resources/test_memories.py b/tests/api_resources/test_memories.py index 146797e0..ac555b9f 100644 --- a/tests/api_resources/test_memories.py +++ b/tests/api_resources/test_memories.py @@ -98,6 +98,7 @@ def test_method_list(self, client: Supermemory) -> None: @parametrize def test_method_list_with_all_params(self, client: Supermemory) -> None: memory = client.memories.list( + container_tags=["user_123", "project_123"], filters='{"AND":[{"key":"group","value":"jira_users","negate":false},{"filterType":"numeric","key":"timestamp","value":"1742745777","negate":false,"numericOperator":">"}]}', limit="10", order="desc", @@ -377,6 +378,7 @@ async def test_method_list(self, async_client: AsyncSupermemory) -> None: @parametrize async def test_method_list_with_all_params(self, async_client: AsyncSupermemory) -> None: memory = await async_client.memories.list( + container_tags=["user_123", "project_123"], filters='{"AND":[{"key":"group","value":"jira_users","negate":false},{"filterType":"numeric","key":"timestamp","value":"1742745777","negate":false,"numericOperator":">"}]}', limit="10", order="desc", From 625befe4a1732972dafd8826c5b959692861336f Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 5 Jun 2025 03:27:08 +0000 Subject: [PATCH 20/48] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 9aa3723d..49fbecd6 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 11 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/supermemory--inc%2Fsupermemory-34f3b1235237761dc6eb4292823363cd8f53be760c3883c127da4a23dd1475fa.yml -openapi_spec_hash: 932c86561ab153d35f3313317ccff0cb +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/supermemory--inc%2Fsupermemory-545cb166662675b4428244c154764fd6a8c5de0072df1e276c9cab7dad33ae32.yml +openapi_spec_hash: ed54243a79c6a94893158276b16f7150 config_hash: 8477e3ee6fd596ab6ac911d052e4de79 From d1b2a9719deb080c4b64ae97893ba139ecaebf68 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 5 Jun 2025 20:17:40 +0000 Subject: [PATCH 21/48] feat(api): api update --- .stats.yml | 4 ++-- src/supermemory/resources/connections.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.stats.yml b/.stats.yml index 49fbecd6..95f441e3 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 11 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/supermemory--inc%2Fsupermemory-545cb166662675b4428244c154764fd6a8c5de0072df1e276c9cab7dad33ae32.yml -openapi_spec_hash: ed54243a79c6a94893158276b16f7150 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/supermemory--inc%2Fsupermemory-7b4755d42a27a1e18d43be34546005155b34a06583a96ec20698a68a1ce9b466.yml +openapi_spec_hash: 836ddb14f2b942acd38b1b3420ca2a07 config_hash: 8477e3ee6fd596ab6ac911d052e4de79 diff --git a/src/supermemory/resources/connections.py b/src/supermemory/resources/connections.py index 33c9c1ec..7793692e 100644 --- a/src/supermemory/resources/connections.py +++ b/src/supermemory/resources/connections.py @@ -101,7 +101,7 @@ def get( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> ConnectionGetResponse: """ - Get connection details + Get connection details with id Args: extra_headers: Send extra headers @@ -199,7 +199,7 @@ async def get( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> ConnectionGetResponse: """ - Get connection details + Get connection details with id Args: extra_headers: Send extra headers From fa9f582226ed9dee9ea8196b78b9312938093465 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 7 Jun 2025 03:25:25 +0000 Subject: [PATCH 22/48] feat(api): api update --- .stats.yml | 4 ++-- src/supermemory/resources/connections.py | 10 ++++++++++ src/supermemory/types/connection_create_params.py | 6 ++++++ src/supermemory/types/connection_get_response.py | 2 ++ tests/api_resources/test_connections.py | 2 ++ 5 files changed, 22 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 95f441e3..7c84dea3 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 11 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/supermemory--inc%2Fsupermemory-7b4755d42a27a1e18d43be34546005155b34a06583a96ec20698a68a1ce9b466.yml -openapi_spec_hash: 836ddb14f2b942acd38b1b3420ca2a07 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/supermemory--inc%2Fsupermemory-a21611536939732050acbb8df94fea712aaca3d24a0428d97f97d7fd47d585ef.yml +openapi_spec_hash: db614ee0bb0fa13db67364ddf563f2c7 config_hash: 8477e3ee6fd596ab6ac911d052e4de79 diff --git a/src/supermemory/resources/connections.py b/src/supermemory/resources/connections.py index 7793692e..dada6709 100644 --- a/src/supermemory/resources/connections.py +++ b/src/supermemory/resources/connections.py @@ -50,6 +50,7 @@ def create( provider: Literal["notion", "google-drive", "onedrive"], *, container_tags: List[str] | NotGiven = NOT_GIVEN, + document_limit: int | NotGiven = NOT_GIVEN, metadata: Optional[Dict[str, Union[str, float, bool]]] | NotGiven = NOT_GIVEN, redirect_url: str | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -63,6 +64,9 @@ def create( Initialize connection and get authorization URL Args: + document_limit: Maximum number of documents to sync from this connection (default: 100, + max: 10000) + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -78,6 +82,7 @@ def create( body=maybe_transform( { "container_tags": container_tags, + "document_limit": document_limit, "metadata": metadata, "redirect_url": redirect_url, }, @@ -148,6 +153,7 @@ async def create( provider: Literal["notion", "google-drive", "onedrive"], *, container_tags: List[str] | NotGiven = NOT_GIVEN, + document_limit: int | NotGiven = NOT_GIVEN, metadata: Optional[Dict[str, Union[str, float, bool]]] | NotGiven = NOT_GIVEN, redirect_url: str | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -161,6 +167,9 @@ async def create( Initialize connection and get authorization URL Args: + document_limit: Maximum number of documents to sync from this connection (default: 100, + max: 10000) + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -176,6 +185,7 @@ async def create( body=await async_maybe_transform( { "container_tags": container_tags, + "document_limit": document_limit, "metadata": metadata, "redirect_url": redirect_url, }, diff --git a/src/supermemory/types/connection_create_params.py b/src/supermemory/types/connection_create_params.py index c2e21fab..8e84991d 100644 --- a/src/supermemory/types/connection_create_params.py +++ b/src/supermemory/types/connection_create_params.py @@ -13,6 +13,12 @@ class ConnectionCreateParams(TypedDict, total=False): container_tags: Annotated[List[str], PropertyInfo(alias="containerTags")] + document_limit: Annotated[int, PropertyInfo(alias="documentLimit")] + """ + Maximum number of documents to sync from this connection (default: 100, + max: 10000) + """ + metadata: Optional[Dict[str, Union[str, float, bool]]] redirect_url: Annotated[str, PropertyInfo(alias="redirectUrl")] diff --git a/src/supermemory/types/connection_get_response.py b/src/supermemory/types/connection_get_response.py index 8f3be8de..7b37b085 100644 --- a/src/supermemory/types/connection_get_response.py +++ b/src/supermemory/types/connection_get_response.py @@ -16,6 +16,8 @@ class ConnectionGetResponse(BaseModel): provider: str + document_limit: Optional[float] = FieldInfo(alias="documentLimit", default=None) + expires_at: Optional[float] = FieldInfo(alias="expiresAt", default=None) metadata: Optional[Dict[str, object]] = None diff --git a/tests/api_resources/test_connections.py b/tests/api_resources/test_connections.py index 29f199c1..c1d5317e 100644 --- a/tests/api_resources/test_connections.py +++ b/tests/api_resources/test_connections.py @@ -31,6 +31,7 @@ def test_method_create_with_all_params(self, client: Supermemory) -> None: connection = client.connections.create( provider="notion", container_tags=["string"], + document_limit=100, metadata={"foo": "string"}, redirect_url="redirectUrl", ) @@ -122,6 +123,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncSupermemor connection = await async_client.connections.create( provider="notion", container_tags=["string"], + document_limit=100, metadata={"foo": "string"}, redirect_url="redirectUrl", ) From d517cbce5d74b7cd92d8921d16ba1bb025848549 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sun, 8 Jun 2025 17:17:24 +0000 Subject: [PATCH 23/48] feat(api): api update --- .stats.yml | 4 ++-- src/supermemory/resources/connections.py | 6 ------ src/supermemory/resources/memories.py | 8 ++++---- src/supermemory/types/connection_create_params.py | 4 ---- src/supermemory/types/memory_list_params.py | 6 +++--- tests/api_resources/test_connections.py | 4 ++-- 6 files changed, 11 insertions(+), 21 deletions(-) diff --git a/.stats.yml b/.stats.yml index 7c84dea3..312012be 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 11 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/supermemory--inc%2Fsupermemory-a21611536939732050acbb8df94fea712aaca3d24a0428d97f97d7fd47d585ef.yml -openapi_spec_hash: db614ee0bb0fa13db67364ddf563f2c7 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/supermemory--inc%2Fsupermemory-603f50d7f791edc333ab3daa15187aa28663836e5352171134e747fe25a263e8.yml +openapi_spec_hash: 28dca6595e16eb68600abb339c43783c config_hash: 8477e3ee6fd596ab6ac911d052e4de79 diff --git a/src/supermemory/resources/connections.py b/src/supermemory/resources/connections.py index dada6709..f431a728 100644 --- a/src/supermemory/resources/connections.py +++ b/src/supermemory/resources/connections.py @@ -64,9 +64,6 @@ def create( Initialize connection and get authorization URL Args: - document_limit: Maximum number of documents to sync from this connection (default: 100, - max: 10000) - extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -167,9 +164,6 @@ async def create( Initialize connection and get authorization URL Args: - document_limit: Maximum number of documents to sync from this connection (default: 100, - max: 10000) - extra_headers: Send extra headers extra_query: Add additional query parameters to the request diff --git a/src/supermemory/resources/memories.py b/src/supermemory/resources/memories.py index 9705200d..4f2b5716 100644 --- a/src/supermemory/resources/memories.py +++ b/src/supermemory/resources/memories.py @@ -120,9 +120,9 @@ def list( *, container_tags: List[str] | NotGiven = NOT_GIVEN, filters: str | NotGiven = NOT_GIVEN, - limit: str | NotGiven = NOT_GIVEN, + limit: Union[str, float] | NotGiven = NOT_GIVEN, order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, - page: str | NotGiven = NOT_GIVEN, + page: Union[str, float] | NotGiven = NOT_GIVEN, sort: Literal["createdAt", "updatedAt"] | NotGiven = NOT_GIVEN, # 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. @@ -441,9 +441,9 @@ async def list( *, container_tags: List[str] | NotGiven = NOT_GIVEN, filters: str | NotGiven = NOT_GIVEN, - limit: str | NotGiven = NOT_GIVEN, + limit: Union[str, float] | NotGiven = NOT_GIVEN, order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, - page: str | NotGiven = NOT_GIVEN, + page: Union[str, float] | NotGiven = NOT_GIVEN, sort: Literal["createdAt", "updatedAt"] | NotGiven = NOT_GIVEN, # 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. diff --git a/src/supermemory/types/connection_create_params.py b/src/supermemory/types/connection_create_params.py index 8e84991d..8bb9e071 100644 --- a/src/supermemory/types/connection_create_params.py +++ b/src/supermemory/types/connection_create_params.py @@ -14,10 +14,6 @@ class ConnectionCreateParams(TypedDict, total=False): container_tags: Annotated[List[str], PropertyInfo(alias="containerTags")] document_limit: Annotated[int, PropertyInfo(alias="documentLimit")] - """ - Maximum number of documents to sync from this connection (default: 100, - max: 10000) - """ metadata: Optional[Dict[str, Union[str, float, bool]]] diff --git a/src/supermemory/types/memory_list_params.py b/src/supermemory/types/memory_list_params.py index 7895ad1d..7e683d71 100644 --- a/src/supermemory/types/memory_list_params.py +++ b/src/supermemory/types/memory_list_params.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import List +from typing import List, Union from typing_extensions import Literal, Annotated, TypedDict from .._utils import PropertyInfo @@ -21,13 +21,13 @@ class MemoryListParams(TypedDict, total=False): filters: str """Optional filters to apply to the search""" - limit: str + limit: Union[str, float] """Number of items per page""" order: Literal["asc", "desc"] """Sort order""" - page: str + page: Union[str, float] """Page number to fetch""" sort: Literal["createdAt", "updatedAt"] diff --git a/tests/api_resources/test_connections.py b/tests/api_resources/test_connections.py index c1d5317e..18492519 100644 --- a/tests/api_resources/test_connections.py +++ b/tests/api_resources/test_connections.py @@ -31,7 +31,7 @@ def test_method_create_with_all_params(self, client: Supermemory) -> None: connection = client.connections.create( provider="notion", container_tags=["string"], - document_limit=100, + document_limit=1, metadata={"foo": "string"}, redirect_url="redirectUrl", ) @@ -123,7 +123,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncSupermemor connection = await async_client.connections.create( provider="notion", container_tags=["string"], - document_limit=100, + document_limit=1, metadata={"foo": "string"}, redirect_url="redirectUrl", ) From c194b2ecbe42ebe5997aab4bc96fb7120a5f6b9c Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 9 Jun 2025 23:17:24 +0000 Subject: [PATCH 24/48] feat(api): api update --- .stats.yml | 6 +- README.md | 17 ---- api.md | 2 - src/supermemory/_files.py | 2 +- src/supermemory/resources/memories.py | 99 +------------------ src/supermemory/types/__init__.py | 2 - .../types/memory_upload_file_params.py | 13 --- .../types/memory_upload_file_response.py | 11 --- tests/api_resources/test_memories.py | 69 ------------- 9 files changed, 8 insertions(+), 213 deletions(-) delete mode 100644 src/supermemory/types/memory_upload_file_params.py delete mode 100644 src/supermemory/types/memory_upload_file_response.py diff --git a/.stats.yml b/.stats.yml index 312012be..bcb588cf 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 11 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/supermemory--inc%2Fsupermemory-603f50d7f791edc333ab3daa15187aa28663836e5352171134e747fe25a263e8.yml -openapi_spec_hash: 28dca6595e16eb68600abb339c43783c +configured_endpoints: 10 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/supermemory--inc%2Fsupermemory-ce16942ff073fdc761f55efe3ba7888070133e624fe3de39cc8af1b54137b421.yml +openapi_spec_hash: 03e1dc871236ce5f3ca653caedc84a42 config_hash: 8477e3ee6fd596ab6ac911d052e4de79 diff --git a/README.md b/README.md index 55246cf4..43bb97a0 100644 --- a/README.md +++ b/README.md @@ -77,23 +77,6 @@ Nested request parameters are [TypedDicts](https://docs.python.org/3/library/typ Typed requests and responses provide autocomplete and documentation within your editor. If you would like to see type errors in VS Code to help catch bugs earlier, set `python.analysis.typeCheckingMode` to `basic`. -## File uploads - -Request parameters that correspond to file uploads can be passed as `bytes`, or a [`PathLike`](https://docs.python.org/3/library/os.html#os.PathLike) instance or a tuple of `(filename, contents, media type)`. - -```python -from pathlib import Path -from supermemory import Supermemory - -client = Supermemory() - -client.memories.upload_file( - file=Path("/path/to/file"), -) -``` - -The async client uses the exact same interface. If you pass a [`PathLike`](https://docs.python.org/3/library/os.html#os.PathLike) instance, the file contents will be read asynchronously automatically. - ## Handling errors When the library is unable to connect to the API (for example, due to network connection problems or a timeout), a subclass of `supermemory.APIConnectionError` is raised. diff --git a/api.md b/api.md index 4ef1f5f6..5616cf40 100644 --- a/api.md +++ b/api.md @@ -8,7 +8,6 @@ from supermemory.types import ( MemoryListResponse, MemoryAddResponse, MemoryGetResponse, - MemoryUploadFileResponse, ) ``` @@ -19,7 +18,6 @@ Methods: - client.memories.delete(id) -> None - client.memories.add(\*\*params) -> MemoryAddResponse - client.memories.get(id) -> MemoryGetResponse -- client.memories.upload_file(\*\*params) -> MemoryUploadFileResponse # Search diff --git a/src/supermemory/_files.py b/src/supermemory/_files.py index 0dcf63d3..715cc207 100644 --- a/src/supermemory/_files.py +++ b/src/supermemory/_files.py @@ -34,7 +34,7 @@ def assert_is_file_content(obj: object, *, key: str | None = None) -> None: if not is_file_content(obj): prefix = f"Expected entry at `{key}`" if key is not None else f"Expected file input `{obj!r}`" raise RuntimeError( - f"{prefix} to be bytes, an io.IOBase instance, PathLike or a tuple but received {type(obj)} instead. See https://github.com/supermemoryai/python-sdk/tree/main#file-uploads" + f"{prefix} to be bytes, an io.IOBase instance, PathLike or a tuple but received {type(obj)} instead." ) from None diff --git a/src/supermemory/resources/memories.py b/src/supermemory/resources/memories.py index 4f2b5716..7eda616f 100644 --- a/src/supermemory/resources/memories.py +++ b/src/supermemory/resources/memories.py @@ -2,14 +2,14 @@ from __future__ import annotations -from typing import Dict, List, Union, Mapping, cast +from typing import Dict, List, Union from typing_extensions import Literal import httpx -from ..types import memory_add_params, memory_list_params, memory_update_params, memory_upload_file_params -from .._types import NOT_GIVEN, Body, Query, Headers, NoneType, NotGiven, FileTypes -from .._utils import extract_files, maybe_transform, deepcopy_minimal, async_maybe_transform +from ..types import memory_add_params, memory_list_params, memory_update_params +from .._types import NOT_GIVEN, Body, Query, Headers, NoneType, NotGiven +from .._utils import maybe_transform, async_maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import ( @@ -23,7 +23,6 @@ from ..types.memory_get_response import MemoryGetResponse from ..types.memory_list_response import MemoryListResponse from ..types.memory_update_response import MemoryUpdateResponse -from ..types.memory_upload_file_response import MemoryUploadFileResponse __all__ = ["MemoriesResource", "AsyncMemoriesResource"] @@ -309,45 +308,6 @@ def get( cast_to=MemoryGetResponse, ) - def upload_file( - self, - *, - file: FileTypes, - # 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, - ) -> MemoryUploadFileResponse: - """ - Upload a file to be processed - - 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 - """ - body = deepcopy_minimal({"file": file}) - files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) - # It should be noted that the actual Content-Type header that will be - # sent to the server will contain a `boundary` parameter, e.g. - # multipart/form-data; boundary=---abc-- - extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} - return self._post( - "/v3/memories/file", - body=maybe_transform(body, memory_upload_file_params.MemoryUploadFileParams), - files=files, - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=MemoryUploadFileResponse, - ) - class AsyncMemoriesResource(AsyncAPIResource): @cached_property @@ -630,45 +590,6 @@ async def get( cast_to=MemoryGetResponse, ) - async def upload_file( - self, - *, - file: FileTypes, - # 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, - ) -> MemoryUploadFileResponse: - """ - Upload a file to be processed - - 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 - """ - body = deepcopy_minimal({"file": file}) - files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) - # It should be noted that the actual Content-Type header that will be - # sent to the server will contain a `boundary` parameter, e.g. - # multipart/form-data; boundary=---abc-- - extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} - return await self._post( - "/v3/memories/file", - body=await async_maybe_transform(body, memory_upload_file_params.MemoryUploadFileParams), - files=files, - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=MemoryUploadFileResponse, - ) - class MemoriesResourceWithRawResponse: def __init__(self, memories: MemoriesResource) -> None: @@ -689,9 +610,6 @@ def __init__(self, memories: MemoriesResource) -> None: self.get = to_raw_response_wrapper( memories.get, ) - self.upload_file = to_raw_response_wrapper( - memories.upload_file, - ) class AsyncMemoriesResourceWithRawResponse: @@ -713,9 +631,6 @@ def __init__(self, memories: AsyncMemoriesResource) -> None: self.get = async_to_raw_response_wrapper( memories.get, ) - self.upload_file = async_to_raw_response_wrapper( - memories.upload_file, - ) class MemoriesResourceWithStreamingResponse: @@ -737,9 +652,6 @@ def __init__(self, memories: MemoriesResource) -> None: self.get = to_streamed_response_wrapper( memories.get, ) - self.upload_file = to_streamed_response_wrapper( - memories.upload_file, - ) class AsyncMemoriesResourceWithStreamingResponse: @@ -761,6 +673,3 @@ def __init__(self, memories: AsyncMemoriesResource) -> None: self.get = async_to_streamed_response_wrapper( memories.get, ) - 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 652e1a43..7dfa5f0a 100644 --- a/src/supermemory/types/__init__.py +++ b/src/supermemory/types/__init__.py @@ -16,6 +16,4 @@ from .search_execute_response import SearchExecuteResponse as SearchExecuteResponse from .setting_update_response import SettingUpdateResponse as SettingUpdateResponse from .connection_create_params import ConnectionCreateParams as ConnectionCreateParams -from .memory_upload_file_params import MemoryUploadFileParams as MemoryUploadFileParams from .connection_create_response import ConnectionCreateResponse as ConnectionCreateResponse -from .memory_upload_file_response import MemoryUploadFileResponse as MemoryUploadFileResponse diff --git a/src/supermemory/types/memory_upload_file_params.py b/src/supermemory/types/memory_upload_file_params.py deleted file mode 100644 index aa6c082a..00000000 --- a/src/supermemory/types/memory_upload_file_params.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Required, TypedDict - -from .._types import FileTypes - -__all__ = ["MemoryUploadFileParams"] - - -class MemoryUploadFileParams(TypedDict, total=False): - file: Required[FileTypes] diff --git a/src/supermemory/types/memory_upload_file_response.py b/src/supermemory/types/memory_upload_file_response.py deleted file mode 100644 index f67b958f..00000000 --- a/src/supermemory/types/memory_upload_file_response.py +++ /dev/null @@ -1,11 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from .._models import BaseModel - -__all__ = ["MemoryUploadFileResponse"] - - -class MemoryUploadFileResponse(BaseModel): - id: str - - status: str diff --git a/tests/api_resources/test_memories.py b/tests/api_resources/test_memories.py index ac555b9f..edfa3443 100644 --- a/tests/api_resources/test_memories.py +++ b/tests/api_resources/test_memories.py @@ -14,7 +14,6 @@ MemoryGetResponse, MemoryListResponse, MemoryUpdateResponse, - MemoryUploadFileResponse, ) base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -265,40 +264,6 @@ def test_path_params_get(self, client: Supermemory) -> None: "", ) - @pytest.mark.skip() - @parametrize - def test_method_upload_file(self, client: Supermemory) -> None: - memory = client.memories.upload_file( - file=b"raw file contents", - ) - assert_matches_type(MemoryUploadFileResponse, memory, path=["response"]) - - @pytest.mark.skip() - @parametrize - def test_raw_response_upload_file(self, client: Supermemory) -> None: - response = client.memories.with_raw_response.upload_file( - file=b"raw file contents", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - memory = response.parse() - assert_matches_type(MemoryUploadFileResponse, memory, path=["response"]) - - @pytest.mark.skip() - @parametrize - def test_streaming_response_upload_file(self, client: Supermemory) -> None: - with client.memories.with_streaming_response.upload_file( - file=b"raw file contents", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - memory = response.parse() - assert_matches_type(MemoryUploadFileResponse, memory, path=["response"]) - - assert cast(Any, response.is_closed) is True - class TestAsyncMemories: parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) @@ -544,37 +509,3 @@ async def test_path_params_get(self, async_client: AsyncSupermemory) -> None: await async_client.memories.with_raw_response.get( "", ) - - @pytest.mark.skip() - @parametrize - async def test_method_upload_file(self, async_client: AsyncSupermemory) -> None: - memory = await async_client.memories.upload_file( - file=b"raw file contents", - ) - assert_matches_type(MemoryUploadFileResponse, memory, path=["response"]) - - @pytest.mark.skip() - @parametrize - async def test_raw_response_upload_file(self, async_client: AsyncSupermemory) -> None: - response = await async_client.memories.with_raw_response.upload_file( - file=b"raw file contents", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - memory = await response.parse() - assert_matches_type(MemoryUploadFileResponse, memory, path=["response"]) - - @pytest.mark.skip() - @parametrize - async def test_streaming_response_upload_file(self, async_client: AsyncSupermemory) -> None: - async with async_client.memories.with_streaming_response.upload_file( - file=b"raw file contents", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - memory = await response.parse() - assert_matches_type(MemoryUploadFileResponse, memory, path=["response"]) - - assert cast(Any, response.is_closed) is True From 7b7711f282eebbaf4edb462b1dc2ed5dfb1bc0fc Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 10 Jun 2025 15:17:24 +0000 Subject: [PATCH 25/48] feat(api): api update --- .stats.yml | 4 ++-- src/supermemory/types/memory_get_response.py | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.stats.yml b/.stats.yml index bcb588cf..09f58922 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 10 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/supermemory--inc%2Fsupermemory-ce16942ff073fdc761f55efe3ba7888070133e624fe3de39cc8af1b54137b421.yml -openapi_spec_hash: 03e1dc871236ce5f3ca653caedc84a42 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/supermemory--inc%2Fsupermemory-ed398f5eafd7dffae0725ec2f42f2a2b0db528292cee661e9f1cdb1063e6eb7b.yml +openapi_spec_hash: c09b2e00932fc1762acabbe9e5c41390 config_hash: 8477e3ee6fd596ab6ac911d052e4de79 diff --git a/src/supermemory/types/memory_get_response.py b/src/supermemory/types/memory_get_response.py index 78390511..0d648d7c 100644 --- a/src/supermemory/types/memory_get_response.py +++ b/src/supermemory/types/memory_get_response.py @@ -59,7 +59,9 @@ class MemoryGetResponse(BaseModel): title: Optional[str] = None """Title of the memory""" - type: Literal["text", "pdf", "tweet", "google_doc", "image", "video", "notion_doc", "webpage"] + type: Literal[ + "text", "pdf", "tweet", "google_doc", "google_slide", "google_sheet", "image", "video", "notion_doc", "webpage" + ] """Type of the memory""" updated_at: datetime = FieldInfo(alias="updatedAt") From 378085f0090ebf25bbc46d0e3947737457ae3a30 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 12 Jun 2025 07:17:32 +0000 Subject: [PATCH 26/48] feat(api): api update --- .stats.yml | 4 ++-- src/supermemory/types/memory_get_response.py | 6 ++++++ src/supermemory/types/memory_list_response.py | 11 +++++++++++ 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 09f58922..42b12314 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 10 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/supermemory--inc%2Fsupermemory-ed398f5eafd7dffae0725ec2f42f2a2b0db528292cee661e9f1cdb1063e6eb7b.yml -openapi_spec_hash: c09b2e00932fc1762acabbe9e5c41390 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/supermemory--inc%2Fsupermemory-df7eac3ee0eb7ee11c9586521c56b1ed4069a7a67b5744cfc7ea87785713badf.yml +openapi_spec_hash: 196cfa21c4c95a04d96ca6ed449b5ca9 config_hash: 8477e3ee6fd596ab6ac911d052e4de79 diff --git a/src/supermemory/types/memory_get_response.py b/src/supermemory/types/memory_get_response.py index 0d648d7c..b52e788c 100644 --- a/src/supermemory/types/memory_get_response.py +++ b/src/supermemory/types/memory_get_response.py @@ -15,6 +15,12 @@ class MemoryGetResponse(BaseModel): id: str """Unique identifier of the memory.""" + connection_id: Optional[str] = FieldInfo(alias="connectionId", default=None) + """Optional ID of connection the memory was created from. + + This is useful for identifying the source of the memory. + """ + content: Optional[str] = None """The content to extract and process into a memory. diff --git a/src/supermemory/types/memory_list_response.py b/src/supermemory/types/memory_list_response.py index 90b3d012..e2d321ab 100644 --- a/src/supermemory/types/memory_list_response.py +++ b/src/supermemory/types/memory_list_response.py @@ -15,6 +15,12 @@ class Memory(BaseModel): id: str """Unique identifier of the memory.""" + connection_id: Optional[str] = FieldInfo(alias="connectionId", default=None) + """Optional ID of connection the memory was created from. + + This is useful for identifying the source of the memory. + """ + created_at: datetime = FieldInfo(alias="createdAt") """Creation timestamp""" @@ -42,6 +48,11 @@ class Memory(BaseModel): title: Optional[str] = None """Title of the memory""" + type: Literal[ + "text", "pdf", "tweet", "google_doc", "google_slide", "google_sheet", "image", "video", "notion_doc", "webpage" + ] + """Type of the memory""" + updated_at: datetime = FieldInfo(alias="updatedAt") """Last update timestamp""" From 486888da89d570779e132f3b810c7d1a4f59082a Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 12 Jun 2025 08:17:31 +0000 Subject: [PATCH 27/48] feat(api): api update --- .stats.yml | 4 ++-- src/supermemory/types/memory_get_response.py | 6 ------ src/supermemory/types/memory_list_response.py | 11 ----------- 3 files changed, 2 insertions(+), 19 deletions(-) diff --git a/.stats.yml b/.stats.yml index 42b12314..09f58922 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 10 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/supermemory--inc%2Fsupermemory-df7eac3ee0eb7ee11c9586521c56b1ed4069a7a67b5744cfc7ea87785713badf.yml -openapi_spec_hash: 196cfa21c4c95a04d96ca6ed449b5ca9 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/supermemory--inc%2Fsupermemory-ed398f5eafd7dffae0725ec2f42f2a2b0db528292cee661e9f1cdb1063e6eb7b.yml +openapi_spec_hash: c09b2e00932fc1762acabbe9e5c41390 config_hash: 8477e3ee6fd596ab6ac911d052e4de79 diff --git a/src/supermemory/types/memory_get_response.py b/src/supermemory/types/memory_get_response.py index b52e788c..0d648d7c 100644 --- a/src/supermemory/types/memory_get_response.py +++ b/src/supermemory/types/memory_get_response.py @@ -15,12 +15,6 @@ class MemoryGetResponse(BaseModel): id: str """Unique identifier of the memory.""" - connection_id: Optional[str] = FieldInfo(alias="connectionId", default=None) - """Optional ID of connection the memory was created from. - - This is useful for identifying the source of the memory. - """ - content: Optional[str] = None """The content to extract and process into a memory. diff --git a/src/supermemory/types/memory_list_response.py b/src/supermemory/types/memory_list_response.py index e2d321ab..90b3d012 100644 --- a/src/supermemory/types/memory_list_response.py +++ b/src/supermemory/types/memory_list_response.py @@ -15,12 +15,6 @@ class Memory(BaseModel): id: str """Unique identifier of the memory.""" - connection_id: Optional[str] = FieldInfo(alias="connectionId", default=None) - """Optional ID of connection the memory was created from. - - This is useful for identifying the source of the memory. - """ - created_at: datetime = FieldInfo(alias="createdAt") """Creation timestamp""" @@ -48,11 +42,6 @@ class Memory(BaseModel): title: Optional[str] = None """Title of the memory""" - type: Literal[ - "text", "pdf", "tweet", "google_doc", "google_slide", "google_sheet", "image", "video", "notion_doc", "webpage" - ] - """Type of the memory""" - updated_at: datetime = FieldInfo(alias="updatedAt") """Last update timestamp""" From 79f6359beb267f85e273a5a3017283d9e231e78a Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 13 Jun 2025 02:06:51 +0000 Subject: [PATCH 28/48] chore(tests): run tests in parallel --- pyproject.toml | 3 ++- requirements-dev.lock | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 9ca24d46..2608b936 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -54,6 +54,7 @@ dev-dependencies = [ "importlib-metadata>=6.7.0", "rich>=13.7.1", "nest_asyncio==1.6.0", + "pytest-xdist>=3.6.1", ] [tool.rye.scripts] @@ -125,7 +126,7 @@ replacement = '[\1](https://github.com/supermemoryai/python-sdk/tree/main/\g<2>) [tool.pytest.ini_options] testpaths = ["tests"] -addopts = "--tb=short" +addopts = "--tb=short -n auto" xfail_strict = true asyncio_mode = "auto" asyncio_default_fixture_loop_scope = "session" diff --git a/requirements-dev.lock b/requirements-dev.lock index efa05b0d..14d829b6 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -30,6 +30,8 @@ distro==1.8.0 exceptiongroup==1.2.2 # via anyio # via pytest +execnet==2.1.1 + # via pytest-xdist filelock==3.12.4 # via virtualenv h11==0.14.0 @@ -72,7 +74,9 @@ pygments==2.18.0 pyright==1.1.399 pytest==8.3.3 # via pytest-asyncio + # via pytest-xdist pytest-asyncio==0.24.0 +pytest-xdist==3.7.0 python-dateutil==2.8.2 # via time-machine pytz==2023.3.post1 From b236e8552e9393048b3541c2c41d969fd5a88ac0 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 13 Jun 2025 02:32:55 +0000 Subject: [PATCH 29/48] fix(client): correctly parse binary response | stream --- src/supermemory/_base_client.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/supermemory/_base_client.py b/src/supermemory/_base_client.py index 8ebfbf2a..01d05a51 100644 --- a/src/supermemory/_base_client.py +++ b/src/supermemory/_base_client.py @@ -1071,7 +1071,14 @@ def _process_response( ) -> ResponseT: origin = get_origin(cast_to) or cast_to - if inspect.isclass(origin) and issubclass(origin, BaseAPIResponse): + if ( + inspect.isclass(origin) + and issubclass(origin, BaseAPIResponse) + # we only want to actually return the custom BaseAPIResponse class if we're + # returning the raw response, or if we're not streaming SSE, as if we're streaming + # SSE then `cast_to` doesn't actively reflect the type we need to parse into + and (not stream or bool(response.request.headers.get(RAW_RESPONSE_HEADER))) + ): if not issubclass(origin, APIResponse): raise TypeError(f"API Response types must subclass {APIResponse}; Received {origin}") @@ -1574,7 +1581,14 @@ async def _process_response( ) -> ResponseT: origin = get_origin(cast_to) or cast_to - if inspect.isclass(origin) and issubclass(origin, BaseAPIResponse): + if ( + inspect.isclass(origin) + and issubclass(origin, BaseAPIResponse) + # we only want to actually return the custom BaseAPIResponse class if we're + # returning the raw response, or if we're not streaming SSE, as if we're streaming + # SSE then `cast_to` doesn't actively reflect the type we need to parse into + and (not stream or bool(response.request.headers.get(RAW_RESPONSE_HEADER))) + ): if not issubclass(origin, AsyncAPIResponse): raise TypeError(f"API Response types must subclass {AsyncAPIResponse}; Received {origin}") From c7e0c2ccae941136705f2328fe536870789cf52a Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 13 Jun 2025 08:17:31 +0000 Subject: [PATCH 30/48] feat(api): api update --- .stats.yml | 4 ++-- src/supermemory/types/connection_get_response.py | 2 ++ src/supermemory/types/memory_get_response.py | 6 ++++++ src/supermemory/types/memory_list_response.py | 11 +++++++++++ 4 files changed, 21 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 09f58922..4877856a 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 10 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/supermemory--inc%2Fsupermemory-ed398f5eafd7dffae0725ec2f42f2a2b0db528292cee661e9f1cdb1063e6eb7b.yml -openapi_spec_hash: c09b2e00932fc1762acabbe9e5c41390 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/supermemory--inc%2Fsupermemory-c56ce3c876eec0737db8394bd6235bff8dc42314b6460f3365c06945749d8714.yml +openapi_spec_hash: 9e633a8fe5c71fa8bd5cbc34073b7c52 config_hash: 8477e3ee6fd596ab6ac911d052e4de79 diff --git a/src/supermemory/types/connection_get_response.py b/src/supermemory/types/connection_get_response.py index 7b37b085..69d9e7b0 100644 --- a/src/supermemory/types/connection_get_response.py +++ b/src/supermemory/types/connection_get_response.py @@ -18,6 +18,8 @@ class ConnectionGetResponse(BaseModel): document_limit: Optional[float] = FieldInfo(alias="documentLimit", default=None) + email: Optional[str] = None + expires_at: Optional[float] = FieldInfo(alias="expiresAt", default=None) metadata: Optional[Dict[str, object]] = None diff --git a/src/supermemory/types/memory_get_response.py b/src/supermemory/types/memory_get_response.py index 0d648d7c..b52e788c 100644 --- a/src/supermemory/types/memory_get_response.py +++ b/src/supermemory/types/memory_get_response.py @@ -15,6 +15,12 @@ class MemoryGetResponse(BaseModel): id: str """Unique identifier of the memory.""" + connection_id: Optional[str] = FieldInfo(alias="connectionId", default=None) + """Optional ID of connection the memory was created from. + + This is useful for identifying the source of the memory. + """ + content: Optional[str] = None """The content to extract and process into a memory. diff --git a/src/supermemory/types/memory_list_response.py b/src/supermemory/types/memory_list_response.py index 90b3d012..e2d321ab 100644 --- a/src/supermemory/types/memory_list_response.py +++ b/src/supermemory/types/memory_list_response.py @@ -15,6 +15,12 @@ class Memory(BaseModel): id: str """Unique identifier of the memory.""" + connection_id: Optional[str] = FieldInfo(alias="connectionId", default=None) + """Optional ID of connection the memory was created from. + + This is useful for identifying the source of the memory. + """ + created_at: datetime = FieldInfo(alias="createdAt") """Creation timestamp""" @@ -42,6 +48,11 @@ class Memory(BaseModel): title: Optional[str] = None """Title of the memory""" + type: Literal[ + "text", "pdf", "tweet", "google_doc", "google_slide", "google_sheet", "image", "video", "notion_doc", "webpage" + ] + """Type of the memory""" + updated_at: datetime = FieldInfo(alias="updatedAt") """Last update timestamp""" From 2d9b26ce58cf533ffbff6d7a002c8504752c1e6a Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 16 Jun 2025 01:17:22 +0000 Subject: [PATCH 31/48] feat(api): api update --- .stats.yml | 4 +- src/supermemory/resources/memories.py | 27 ++++++--- src/supermemory/resources/search.py | 28 ++++++--- tests/api_resources/test_memories.py | 74 +++++++++++++---------- tests/api_resources/test_search.py | 84 ++++++++++++++++----------- 5 files changed, 134 insertions(+), 83 deletions(-) diff --git a/.stats.yml b/.stats.yml index 4877856a..45df4c51 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 10 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/supermemory--inc%2Fsupermemory-c56ce3c876eec0737db8394bd6235bff8dc42314b6460f3365c06945749d8714.yml -openapi_spec_hash: 9e633a8fe5c71fa8bd5cbc34073b7c52 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/supermemory--inc%2Fsupermemory-1e11561ed34d9eb33445d7ef7b0f808d197d18862913ef09cfe6b26b8ccfd722.yml +openapi_spec_hash: d6049696f057b0d0d81e25aca16b88a7 config_hash: 8477e3ee6fd596ab6ac911d052e4de79 diff --git a/src/supermemory/resources/memories.py b/src/supermemory/resources/memories.py index 7eda616f..7de63813 100644 --- a/src/supermemory/resources/memories.py +++ b/src/supermemory/resources/memories.py @@ -2,6 +2,7 @@ from __future__ import annotations +import typing_extensions from typing import Dict, List, Union from typing_extensions import Literal @@ -114,6 +115,7 @@ def update( cast_to=MemoryUpdateResponse, ) + @typing_extensions.deprecated("deprecated") def list( self, *, @@ -396,6 +398,7 @@ async def update( cast_to=MemoryUpdateResponse, ) + @typing_extensions.deprecated("deprecated") async def list( self, *, @@ -598,8 +601,10 @@ def __init__(self, memories: MemoriesResource) -> None: self.update = to_raw_response_wrapper( memories.update, ) - self.list = to_raw_response_wrapper( - memories.list, + self.list = ( # pyright: ignore[reportDeprecated] + to_raw_response_wrapper( + memories.list # pyright: ignore[reportDeprecated], + ) ) self.delete = to_raw_response_wrapper( memories.delete, @@ -619,8 +624,10 @@ def __init__(self, memories: AsyncMemoriesResource) -> None: self.update = async_to_raw_response_wrapper( memories.update, ) - self.list = async_to_raw_response_wrapper( - memories.list, + self.list = ( # pyright: ignore[reportDeprecated] + async_to_raw_response_wrapper( + memories.list # pyright: ignore[reportDeprecated], + ) ) self.delete = async_to_raw_response_wrapper( memories.delete, @@ -640,8 +647,10 @@ def __init__(self, memories: MemoriesResource) -> None: self.update = to_streamed_response_wrapper( memories.update, ) - self.list = to_streamed_response_wrapper( - memories.list, + self.list = ( # pyright: ignore[reportDeprecated] + to_streamed_response_wrapper( + memories.list # pyright: ignore[reportDeprecated], + ) ) self.delete = to_streamed_response_wrapper( memories.delete, @@ -661,8 +670,10 @@ def __init__(self, memories: AsyncMemoriesResource) -> None: self.update = async_to_streamed_response_wrapper( memories.update, ) - self.list = async_to_streamed_response_wrapper( - memories.list, + self.list = ( # pyright: ignore[reportDeprecated] + async_to_streamed_response_wrapper( + memories.list # pyright: ignore[reportDeprecated], + ) ) self.delete = async_to_streamed_response_wrapper( memories.delete, diff --git a/src/supermemory/resources/search.py b/src/supermemory/resources/search.py index 851755e4..3f5bcea8 100644 --- a/src/supermemory/resources/search.py +++ b/src/supermemory/resources/search.py @@ -2,6 +2,8 @@ from __future__ import annotations +import typing_extensions + import httpx from ..types import search_execute_params @@ -41,6 +43,7 @@ def with_streaming_response(self) -> SearchResourceWithStreamingResponse: """ return SearchResourceWithStreamingResponse(self) + @typing_extensions.deprecated("deprecated") def execute( self, *, @@ -109,6 +112,7 @@ def with_streaming_response(self) -> AsyncSearchResourceWithStreamingResponse: """ return AsyncSearchResourceWithStreamingResponse(self) + @typing_extensions.deprecated("deprecated") async def execute( self, *, @@ -161,8 +165,10 @@ class SearchResourceWithRawResponse: def __init__(self, search: SearchResource) -> None: self._search = search - self.execute = to_raw_response_wrapper( - search.execute, + self.execute = ( # pyright: ignore[reportDeprecated] + to_raw_response_wrapper( + search.execute # pyright: ignore[reportDeprecated], + ) ) @@ -170,8 +176,10 @@ class AsyncSearchResourceWithRawResponse: def __init__(self, search: AsyncSearchResource) -> None: self._search = search - self.execute = async_to_raw_response_wrapper( - search.execute, + self.execute = ( # pyright: ignore[reportDeprecated] + async_to_raw_response_wrapper( + search.execute # pyright: ignore[reportDeprecated], + ) ) @@ -179,8 +187,10 @@ class SearchResourceWithStreamingResponse: def __init__(self, search: SearchResource) -> None: self._search = search - self.execute = to_streamed_response_wrapper( - search.execute, + self.execute = ( # pyright: ignore[reportDeprecated] + to_streamed_response_wrapper( + search.execute # pyright: ignore[reportDeprecated], + ) ) @@ -188,6 +198,8 @@ class AsyncSearchResourceWithStreamingResponse: def __init__(self, search: AsyncSearchResource) -> None: self._search = search - self.execute = async_to_streamed_response_wrapper( - search.execute, + self.execute = ( # pyright: ignore[reportDeprecated] + async_to_streamed_response_wrapper( + search.execute # pyright: ignore[reportDeprecated], + ) ) diff --git a/tests/api_resources/test_memories.py b/tests/api_resources/test_memories.py index edfa3443..2aae3b46 100644 --- a/tests/api_resources/test_memories.py +++ b/tests/api_resources/test_memories.py @@ -16,6 +16,8 @@ MemoryUpdateResponse, ) +# pyright: reportDeprecated=false + base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -90,26 +92,31 @@ def test_path_params_update(self, client: Supermemory) -> None: @pytest.mark.skip() @parametrize def test_method_list(self, client: Supermemory) -> None: - memory = client.memories.list() + with pytest.warns(DeprecationWarning): + memory = client.memories.list() + assert_matches_type(MemoryListResponse, memory, path=["response"]) @pytest.mark.skip() @parametrize def test_method_list_with_all_params(self, client: Supermemory) -> None: - memory = client.memories.list( - container_tags=["user_123", "project_123"], - filters='{"AND":[{"key":"group","value":"jira_users","negate":false},{"filterType":"numeric","key":"timestamp","value":"1742745777","negate":false,"numericOperator":">"}]}', - limit="10", - order="desc", - page="1", - sort="createdAt", - ) + with pytest.warns(DeprecationWarning): + memory = client.memories.list( + container_tags=["user_123", "project_123"], + filters='{"AND":[{"key":"group","value":"jira_users","negate":false},{"filterType":"numeric","key":"timestamp","value":"1742745777","negate":false,"numericOperator":">"}]}', + limit="10", + order="desc", + page="1", + sort="createdAt", + ) + assert_matches_type(MemoryListResponse, memory, path=["response"]) @pytest.mark.skip() @parametrize def test_raw_response_list(self, client: Supermemory) -> None: - response = client.memories.with_raw_response.list() + with pytest.warns(DeprecationWarning): + response = client.memories.with_raw_response.list() assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -119,12 +126,13 @@ def test_raw_response_list(self, client: Supermemory) -> None: @pytest.mark.skip() @parametrize def test_streaming_response_list(self, client: Supermemory) -> None: - with client.memories.with_streaming_response.list() as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" + with pytest.warns(DeprecationWarning): + with client.memories.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" - memory = response.parse() - assert_matches_type(MemoryListResponse, memory, path=["response"]) + memory = response.parse() + assert_matches_type(MemoryListResponse, memory, path=["response"]) assert cast(Any, response.is_closed) is True @@ -336,26 +344,31 @@ async def test_path_params_update(self, async_client: AsyncSupermemory) -> None: @pytest.mark.skip() @parametrize async def test_method_list(self, async_client: AsyncSupermemory) -> None: - memory = await async_client.memories.list() + with pytest.warns(DeprecationWarning): + memory = await async_client.memories.list() + assert_matches_type(MemoryListResponse, memory, path=["response"]) @pytest.mark.skip() @parametrize async def test_method_list_with_all_params(self, async_client: AsyncSupermemory) -> None: - memory = await async_client.memories.list( - container_tags=["user_123", "project_123"], - filters='{"AND":[{"key":"group","value":"jira_users","negate":false},{"filterType":"numeric","key":"timestamp","value":"1742745777","negate":false,"numericOperator":">"}]}', - limit="10", - order="desc", - page="1", - sort="createdAt", - ) + with pytest.warns(DeprecationWarning): + memory = await async_client.memories.list( + container_tags=["user_123", "project_123"], + filters='{"AND":[{"key":"group","value":"jira_users","negate":false},{"filterType":"numeric","key":"timestamp","value":"1742745777","negate":false,"numericOperator":">"}]}', + limit="10", + order="desc", + page="1", + sort="createdAt", + ) + assert_matches_type(MemoryListResponse, memory, path=["response"]) @pytest.mark.skip() @parametrize async def test_raw_response_list(self, async_client: AsyncSupermemory) -> None: - response = await async_client.memories.with_raw_response.list() + with pytest.warns(DeprecationWarning): + response = await async_client.memories.with_raw_response.list() assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -365,12 +378,13 @@ async def test_raw_response_list(self, async_client: AsyncSupermemory) -> None: @pytest.mark.skip() @parametrize async def test_streaming_response_list(self, async_client: AsyncSupermemory) -> None: - async with async_client.memories.with_streaming_response.list() as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" + with pytest.warns(DeprecationWarning): + async with async_client.memories.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" - memory = await response.parse() - assert_matches_type(MemoryListResponse, memory, path=["response"]) + memory = await response.parse() + assert_matches_type(MemoryListResponse, memory, path=["response"]) assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/test_search.py b/tests/api_resources/test_search.py index 57fdb8d0..f60a8b74 100644 --- a/tests/api_resources/test_search.py +++ b/tests/api_resources/test_search.py @@ -11,6 +11,8 @@ from tests.utils import assert_matches_type from supermemory.types import SearchExecuteResponse +# pyright: reportDeprecated=false + base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -20,26 +22,31 @@ class TestSearch: @pytest.mark.skip() @parametrize def test_method_execute(self, client: Supermemory) -> None: - search = client.search.execute( - q="machine learning concepts", - ) + with pytest.warns(DeprecationWarning): + search = client.search.execute( + q="machine learning concepts", + ) + assert_matches_type(SearchExecuteResponse, search, path=["response"]) @pytest.mark.skip() @parametrize def test_method_execute_with_all_params(self, client: Supermemory) -> None: - search = client.search.execute( - q="machine learning concepts", - doc_id="doc_xyz789", - ) + with pytest.warns(DeprecationWarning): + search = client.search.execute( + q="machine learning concepts", + doc_id="doc_xyz789", + ) + assert_matches_type(SearchExecuteResponse, search, path=["response"]) @pytest.mark.skip() @parametrize def test_raw_response_execute(self, client: Supermemory) -> None: - response = client.search.with_raw_response.execute( - q="machine learning concepts", - ) + with pytest.warns(DeprecationWarning): + response = client.search.with_raw_response.execute( + q="machine learning concepts", + ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -49,14 +56,15 @@ def test_raw_response_execute(self, client: Supermemory) -> None: @pytest.mark.skip() @parametrize def test_streaming_response_execute(self, client: Supermemory) -> None: - with client.search.with_streaming_response.execute( - q="machine learning concepts", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" + with pytest.warns(DeprecationWarning): + with client.search.with_streaming_response.execute( + q="machine learning concepts", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" - search = response.parse() - assert_matches_type(SearchExecuteResponse, search, path=["response"]) + search = response.parse() + assert_matches_type(SearchExecuteResponse, search, path=["response"]) assert cast(Any, response.is_closed) is True @@ -67,26 +75,31 @@ class TestAsyncSearch: @pytest.mark.skip() @parametrize async def test_method_execute(self, async_client: AsyncSupermemory) -> None: - search = await async_client.search.execute( - q="machine learning concepts", - ) + with pytest.warns(DeprecationWarning): + search = await async_client.search.execute( + q="machine learning concepts", + ) + assert_matches_type(SearchExecuteResponse, search, path=["response"]) @pytest.mark.skip() @parametrize async def test_method_execute_with_all_params(self, async_client: AsyncSupermemory) -> None: - search = await async_client.search.execute( - q="machine learning concepts", - doc_id="doc_xyz789", - ) + with pytest.warns(DeprecationWarning): + search = await async_client.search.execute( + q="machine learning concepts", + doc_id="doc_xyz789", + ) + assert_matches_type(SearchExecuteResponse, search, path=["response"]) @pytest.mark.skip() @parametrize async def test_raw_response_execute(self, async_client: AsyncSupermemory) -> None: - response = await async_client.search.with_raw_response.execute( - q="machine learning concepts", - ) + with pytest.warns(DeprecationWarning): + response = await async_client.search.with_raw_response.execute( + q="machine learning concepts", + ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -96,13 +109,14 @@ async def test_raw_response_execute(self, async_client: AsyncSupermemory) -> Non @pytest.mark.skip() @parametrize async def test_streaming_response_execute(self, async_client: AsyncSupermemory) -> None: - async with async_client.search.with_streaming_response.execute( - q="machine learning concepts", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - search = await response.parse() - assert_matches_type(SearchExecuteResponse, search, path=["response"]) + with pytest.warns(DeprecationWarning): + async with async_client.search.with_streaming_response.execute( + q="machine learning concepts", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + search = await response.parse() + assert_matches_type(SearchExecuteResponse, search, path=["response"]) assert cast(Any, response.is_closed) is True From a462f2240dac23bf780f540ba39da3febbc561e7 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 17 Jun 2025 02:36:22 +0000 Subject: [PATCH 32/48] chore(tests): add tests for httpx client instantiation & proxies --- tests/test_client.py | 46 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/tests/test_client.py b/tests/test_client.py index dc292b03..96b28cc6 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -31,6 +31,8 @@ DEFAULT_TIMEOUT, HTTPX_DEFAULT_TIMEOUT, BaseClient, + DefaultHttpxClient, + DefaultAsyncHttpxClient, make_request_options, ) from supermemory.types.memory_add_params import MemoryAddParams @@ -846,6 +848,28 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: assert response.http_request.headers.get("x-stainless-retry-count") == "42" + def test_proxy_environment_variables(self, monkeypatch: pytest.MonkeyPatch) -> None: + # Test that the proxy environment variables are set correctly + monkeypatch.setenv("HTTPS_PROXY", "https://example.org") + + client = DefaultHttpxClient() + + mounts = tuple(client._mounts.items()) + assert len(mounts) == 1 + assert mounts[0][0].pattern == "https://" + + @pytest.mark.filterwarnings("ignore:.*deprecated.*:DeprecationWarning") + def test_default_client_creation(self) -> None: + # Ensure that the client can be initialized without any exceptions + DefaultHttpxClient( + verify=True, + cert=None, + trust_env=True, + http1=True, + http2=False, + limits=httpx.Limits(max_connections=100, max_keepalive_connections=20), + ) + @pytest.mark.respx(base_url=base_url) def test_follow_redirects(self, respx_mock: MockRouter) -> None: # Test that the default follow_redirects=True allows following redirects @@ -1715,6 +1739,28 @@ async def test_main() -> None: time.sleep(0.1) + async def test_proxy_environment_variables(self, monkeypatch: pytest.MonkeyPatch) -> None: + # Test that the proxy environment variables are set correctly + monkeypatch.setenv("HTTPS_PROXY", "https://example.org") + + client = DefaultAsyncHttpxClient() + + mounts = tuple(client._mounts.items()) + assert len(mounts) == 1 + assert mounts[0][0].pattern == "https://" + + @pytest.mark.filterwarnings("ignore:.*deprecated.*:DeprecationWarning") + async def test_default_client_creation(self) -> None: + # Ensure that the client can be initialized without any exceptions + DefaultAsyncHttpxClient( + verify=True, + cert=None, + trust_env=True, + http1=True, + http2=False, + limits=httpx.Limits(max_connections=100, max_keepalive_connections=20), + ) + @pytest.mark.respx(base_url=base_url) async def test_follow_redirects(self, respx_mock: MockRouter) -> None: # Test that the default follow_redirects=True allows following redirects From 355810b458222ddf405ec130cf43887cf7b2138d Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 17 Jun 2025 04:06:26 +0000 Subject: [PATCH 33/48] chore(internal): update conftest.py --- tests/conftest.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/conftest.py b/tests/conftest.py index 44fc9e09..d6ce7847 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,3 +1,5 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + from __future__ import annotations import os From ec1b12b9447ff582d80a0dedf75dc5c924aee6e4 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 17 Jun 2025 06:36:39 +0000 Subject: [PATCH 34/48] chore(ci): enable for pull requests --- .github/workflows/ci.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 71338782..43988b20 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,6 +7,10 @@ on: - 'integrated/**' - 'stl-preview-head/**' - 'stl-preview-base/**' + pull_request: + branches-ignore: + - 'stl-preview-head/**' + - 'stl-preview-base/**' jobs: lint: From 9415b50222cea2e827a8e9c13525ad8e19df9ff2 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 18 Jun 2025 02:08:48 +0000 Subject: [PATCH 35/48] chore(readme): update badges --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 43bb97a0..06df291c 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Supermemory Python API library -[![PyPI version](https://img.shields.io/pypi/v/supermemory.svg)](https://pypi.org/project/supermemory/) +[![PyPI version]()](https://pypi.org/project/supermemory/) The Supermemory Python library provides convenient access to the Supermemory REST API from any Python 3.8+ application. The library includes type definitions for all request params and response fields, From f9bf3c135c6a6236c8ef0ee5d538843021448b9d Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 18 Jun 2025 05:46:00 +0000 Subject: [PATCH 36/48] fix(tests): fix: tests which call HTTP endpoints directly with the example parameters --- tests/test_client.py | 73 ++++++++++++-------------------------------- 1 file changed, 20 insertions(+), 53 deletions(-) diff --git a/tests/test_client.py b/tests/test_client.py index 96b28cc6..5d2288bf 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -23,9 +23,7 @@ from supermemory import Supermemory, AsyncSupermemory, APIResponseValidationError from supermemory._types import Omit -from supermemory._utils import maybe_transform from supermemory._models import BaseModel, FinalRequestOptions -from supermemory._constants import RAW_RESPONSE_HEADER from supermemory._exceptions import APIStatusError, APITimeoutError, SupermemoryError, APIResponseValidationError from supermemory._base_client import ( DEFAULT_TIMEOUT, @@ -35,7 +33,6 @@ DefaultAsyncHttpxClient, make_request_options, ) -from supermemory.types.memory_add_params import MemoryAddParams from .utils import update_env @@ -725,42 +722,25 @@ def test_parse_retry_after_header(self, remaining_retries: int, retry_after: str @mock.patch("supermemory._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) - def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter) -> None: + def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter, client: Supermemory) -> None: respx_mock.post("/v3/memories").mock(side_effect=httpx.TimeoutException("Test timeout error")) with pytest.raises(APITimeoutError): - self.client.post( - "/v3/memories", - body=cast( - object, - maybe_transform( - dict(content="This is a detailed article about machine learning concepts..."), MemoryAddParams - ), - ), - cast_to=httpx.Response, - options={"headers": {RAW_RESPONSE_HEADER: "stream"}}, - ) + client.memories.with_streaming_response.add( + content="This is a detailed article about machine learning concepts..." + ).__enter__() assert _get_open_connections(self.client) == 0 @mock.patch("supermemory._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) - def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter) -> None: + def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter, client: Supermemory) -> None: respx_mock.post("/v3/memories").mock(return_value=httpx.Response(500)) with pytest.raises(APIStatusError): - self.client.post( - "/v3/memories", - body=cast( - object, - maybe_transform( - dict(content="This is a detailed article about machine learning concepts..."), MemoryAddParams - ), - ), - cast_to=httpx.Response, - options={"headers": {RAW_RESPONSE_HEADER: "stream"}}, - ) - + client.memories.with_streaming_response.add( + content="This is a detailed article about machine learning concepts..." + ).__enter__() assert _get_open_connections(self.client) == 0 @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) @@ -1568,42 +1548,29 @@ async def test_parse_retry_after_header(self, remaining_retries: int, retry_afte @mock.patch("supermemory._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) - async def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter) -> None: + async def test_retrying_timeout_errors_doesnt_leak( + self, respx_mock: MockRouter, async_client: AsyncSupermemory + ) -> None: respx_mock.post("/v3/memories").mock(side_effect=httpx.TimeoutException("Test timeout error")) with pytest.raises(APITimeoutError): - await self.client.post( - "/v3/memories", - body=cast( - object, - maybe_transform( - dict(content="This is a detailed article about machine learning concepts..."), MemoryAddParams - ), - ), - cast_to=httpx.Response, - options={"headers": {RAW_RESPONSE_HEADER: "stream"}}, - ) + await async_client.memories.with_streaming_response.add( + content="This is a detailed article about machine learning concepts..." + ).__aenter__() assert _get_open_connections(self.client) == 0 @mock.patch("supermemory._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) - async def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter) -> None: + async def test_retrying_status_errors_doesnt_leak( + self, respx_mock: MockRouter, async_client: AsyncSupermemory + ) -> None: respx_mock.post("/v3/memories").mock(return_value=httpx.Response(500)) with pytest.raises(APIStatusError): - await self.client.post( - "/v3/memories", - body=cast( - object, - maybe_transform( - dict(content="This is a detailed article about machine learning concepts..."), MemoryAddParams - ), - ), - cast_to=httpx.Response, - options={"headers": {RAW_RESPONSE_HEADER: "stream"}}, - ) - + await async_client.memories.with_streaming_response.add( + content="This is a detailed article about machine learning concepts..." + ).__aenter__() assert _get_open_connections(self.client) == 0 @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) From be3ccbdebe4974592c670cb43ed572ced78e60a3 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 19 Jun 2025 02:48:04 +0000 Subject: [PATCH 37/48] docs(client): fix httpx.Timeout documentation reference --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 06df291c..c9ee3b09 100644 --- a/README.md +++ b/README.md @@ -146,7 +146,7 @@ client.with_options(max_retries=5).memories.add( ### Timeouts By default requests time out after 1 minute. You can configure this with a `timeout` option, -which accepts a float or an [`httpx.Timeout`](https://www.python-httpx.org/advanced/#fine-tuning-the-configuration) object: +which accepts a float or an [`httpx.Timeout`](https://www.python-httpx.org/advanced/timeouts/#fine-tuning-the-configuration) object: ```python from supermemory import Supermemory From 1e9b706a04bea69c10ff310be9a10c51052db32d Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 19 Jun 2025 23:17:26 +0000 Subject: [PATCH 38/48] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 45df4c51..6025fc56 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 10 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/supermemory--inc%2Fsupermemory-1e11561ed34d9eb33445d7ef7b0f808d197d18862913ef09cfe6b26b8ccfd722.yml -openapi_spec_hash: d6049696f057b0d0d81e25aca16b88a7 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/supermemory--inc%2Fsupermemory-a3f5eda63cbdc4bfdd49d8df67977c090c9ec56dac7160aa54a229593ef2d33f.yml +openapi_spec_hash: 5abb9b20ab9c8c379956ed52a608467b config_hash: 8477e3ee6fd596ab6ac911d052e4de79 From 200e97acf9f2be46a3ffef069912da9e4d05a951 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 20 Jun 2025 23:17:42 +0000 Subject: [PATCH 39/48] feat(api): api update --- .stats.yml | 6 +- README.md | 12 +- api.md | 20 +- src/supermemory/_client.py | 10 +- src/supermemory/resources/__init__.py | 14 -- src/supermemory/resources/memories.py | 153 +------------ src/supermemory/resources/search.py | 205 ------------------ src/supermemory/resources/settings.py | 58 ++++- src/supermemory/types/__init__.py | 4 - src/supermemory/types/memory_list_params.py | 34 --- src/supermemory/types/memory_list_response.py | 81 ------- .../types/search_execute_params.py | 20 -- .../types/search_execute_response.py | 55 ----- src/supermemory/types/setting_get_response.py | 32 ++- .../types/setting_update_params.py | 36 ++- .../types/setting_update_response.py | 32 ++- tests/api_resources/test_memories.py | 97 --------- tests/api_resources/test_search.py | 122 ----------- tests/api_resources/test_settings.py | 34 ++- 19 files changed, 171 insertions(+), 854 deletions(-) delete mode 100644 src/supermemory/resources/search.py delete mode 100644 src/supermemory/types/memory_list_params.py delete mode 100644 src/supermemory/types/memory_list_response.py delete mode 100644 src/supermemory/types/search_execute_params.py delete mode 100644 src/supermemory/types/search_execute_response.py delete mode 100644 tests/api_resources/test_search.py diff --git a/.stats.yml b/.stats.yml index 6025fc56..539bcbfc 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 10 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/supermemory--inc%2Fsupermemory-a3f5eda63cbdc4bfdd49d8df67977c090c9ec56dac7160aa54a229593ef2d33f.yml -openapi_spec_hash: 5abb9b20ab9c8c379956ed52a608467b +configured_endpoints: 8 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/supermemory--inc%2Fsupermemory-ce8810a8f8d5fa2d49c37e64b5bc3847eb102d7cc9154455ee598be5d118614d.yml +openapi_spec_hash: 36e6e52e5cf59f5df66f96afae0afef7 config_hash: 8477e3ee6fd596ab6ac911d052e4de79 diff --git a/README.md b/README.md index c9ee3b09..2957f876 100644 --- a/README.md +++ b/README.md @@ -31,10 +31,10 @@ client = Supermemory( api_key=os.environ.get("SUPERMEMORY_API_KEY"), # This is the default and can be omitted ) -response = client.search.execute( - q="documents related to python", +response = client.memories.add( + content="This is a detailed article about machine learning concepts...", ) -print(response.results) +print(response.id) ``` While you can provide an `api_key` keyword argument, @@ -57,10 +57,10 @@ client = AsyncSupermemory( async def main() -> None: - response = await client.search.execute( - q="documents related to python", + response = await client.memories.add( + content="This is a detailed article about machine learning concepts...", ) - print(response.results) + print(response.id) asyncio.run(main()) diff --git a/api.md b/api.md index 5616cf40..77c6c399 100644 --- a/api.md +++ b/api.md @@ -3,34 +3,16 @@ Types: ```python -from supermemory.types import ( - MemoryUpdateResponse, - MemoryListResponse, - MemoryAddResponse, - MemoryGetResponse, -) +from supermemory.types import MemoryUpdateResponse, MemoryAddResponse, MemoryGetResponse ``` Methods: - client.memories.update(id, \*\*params) -> MemoryUpdateResponse -- client.memories.list(\*\*params) -> MemoryListResponse - client.memories.delete(id) -> None - client.memories.add(\*\*params) -> MemoryAddResponse - client.memories.get(id) -> MemoryGetResponse -# Search - -Types: - -```python -from supermemory.types import SearchExecuteResponse -``` - -Methods: - -- client.search.execute(\*\*params) -> SearchExecuteResponse - # Settings Types: diff --git a/src/supermemory/_client.py b/src/supermemory/_client.py index e2e99f0b..0a336bf6 100644 --- a/src/supermemory/_client.py +++ b/src/supermemory/_client.py @@ -21,7 +21,7 @@ ) from ._utils import is_given, get_async_library from ._version import __version__ -from .resources import search, memories, settings, connections +from .resources import memories, settings, connections from ._streaming import Stream as Stream, AsyncStream as AsyncStream from ._exceptions import APIStatusError, SupermemoryError from ._base_client import ( @@ -44,7 +44,6 @@ class Supermemory(SyncAPIClient): memories: memories.MemoriesResource - search: search.SearchResource settings: settings.SettingsResource connections: connections.ConnectionsResource with_raw_response: SupermemoryWithRawResponse @@ -105,7 +104,6 @@ def __init__( ) self.memories = memories.MemoriesResource(self) - self.search = search.SearchResource(self) self.settings = settings.SettingsResource(self) self.connections = connections.ConnectionsResource(self) self.with_raw_response = SupermemoryWithRawResponse(self) @@ -218,7 +216,6 @@ def _make_status_error( class AsyncSupermemory(AsyncAPIClient): memories: memories.AsyncMemoriesResource - search: search.AsyncSearchResource settings: settings.AsyncSettingsResource connections: connections.AsyncConnectionsResource with_raw_response: AsyncSupermemoryWithRawResponse @@ -279,7 +276,6 @@ def __init__( ) self.memories = memories.AsyncMemoriesResource(self) - self.search = search.AsyncSearchResource(self) self.settings = settings.AsyncSettingsResource(self) self.connections = connections.AsyncConnectionsResource(self) self.with_raw_response = AsyncSupermemoryWithRawResponse(self) @@ -393,7 +389,6 @@ def _make_status_error( class SupermemoryWithRawResponse: def __init__(self, client: Supermemory) -> None: self.memories = memories.MemoriesResourceWithRawResponse(client.memories) - self.search = search.SearchResourceWithRawResponse(client.search) self.settings = settings.SettingsResourceWithRawResponse(client.settings) self.connections = connections.ConnectionsResourceWithRawResponse(client.connections) @@ -401,7 +396,6 @@ def __init__(self, client: Supermemory) -> None: class AsyncSupermemoryWithRawResponse: def __init__(self, client: AsyncSupermemory) -> None: self.memories = memories.AsyncMemoriesResourceWithRawResponse(client.memories) - self.search = search.AsyncSearchResourceWithRawResponse(client.search) self.settings = settings.AsyncSettingsResourceWithRawResponse(client.settings) self.connections = connections.AsyncConnectionsResourceWithRawResponse(client.connections) @@ -409,7 +403,6 @@ def __init__(self, client: AsyncSupermemory) -> None: class SupermemoryWithStreamedResponse: def __init__(self, client: Supermemory) -> None: self.memories = memories.MemoriesResourceWithStreamingResponse(client.memories) - self.search = search.SearchResourceWithStreamingResponse(client.search) self.settings = settings.SettingsResourceWithStreamingResponse(client.settings) self.connections = connections.ConnectionsResourceWithStreamingResponse(client.connections) @@ -417,7 +410,6 @@ def __init__(self, client: Supermemory) -> None: class AsyncSupermemoryWithStreamedResponse: def __init__(self, client: AsyncSupermemory) -> None: self.memories = memories.AsyncMemoriesResourceWithStreamingResponse(client.memories) - self.search = search.AsyncSearchResourceWithStreamingResponse(client.search) self.settings = settings.AsyncSettingsResourceWithStreamingResponse(client.settings) self.connections = connections.AsyncConnectionsResourceWithStreamingResponse(client.connections) diff --git a/src/supermemory/resources/__init__.py b/src/supermemory/resources/__init__.py index 275ecfbe..5a1fb723 100644 --- a/src/supermemory/resources/__init__.py +++ b/src/supermemory/resources/__init__.py @@ -1,13 +1,5 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from .search import ( - SearchResource, - AsyncSearchResource, - SearchResourceWithRawResponse, - AsyncSearchResourceWithRawResponse, - SearchResourceWithStreamingResponse, - AsyncSearchResourceWithStreamingResponse, -) from .memories import ( MemoriesResource, AsyncMemoriesResource, @@ -40,12 +32,6 @@ "AsyncMemoriesResourceWithRawResponse", "MemoriesResourceWithStreamingResponse", "AsyncMemoriesResourceWithStreamingResponse", - "SearchResource", - "AsyncSearchResource", - "SearchResourceWithRawResponse", - "AsyncSearchResourceWithRawResponse", - "SearchResourceWithStreamingResponse", - "AsyncSearchResourceWithStreamingResponse", "SettingsResource", "AsyncSettingsResource", "SettingsResourceWithRawResponse", diff --git a/src/supermemory/resources/memories.py b/src/supermemory/resources/memories.py index 7de63813..02cf1801 100644 --- a/src/supermemory/resources/memories.py +++ b/src/supermemory/resources/memories.py @@ -2,13 +2,11 @@ from __future__ import annotations -import typing_extensions from typing import Dict, List, Union -from typing_extensions import Literal import httpx -from ..types import memory_add_params, memory_list_params, memory_update_params +from ..types import memory_add_params, memory_update_params from .._types import NOT_GIVEN, Body, Query, Headers, NoneType, NotGiven from .._utils import maybe_transform, async_maybe_transform from .._compat import cached_property @@ -22,7 +20,6 @@ from .._base_client import make_request_options from ..types.memory_add_response import MemoryAddResponse from ..types.memory_get_response import MemoryGetResponse -from ..types.memory_list_response import MemoryListResponse from ..types.memory_update_response import MemoryUpdateResponse __all__ = ["MemoriesResource", "AsyncMemoriesResource"] @@ -115,70 +112,6 @@ def update( cast_to=MemoryUpdateResponse, ) - @typing_extensions.deprecated("deprecated") - def list( - self, - *, - container_tags: List[str] | NotGiven = NOT_GIVEN, - filters: str | NotGiven = NOT_GIVEN, - limit: Union[str, float] | NotGiven = NOT_GIVEN, - order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, - page: Union[str, float] | NotGiven = NOT_GIVEN, - sort: Literal["createdAt", "updatedAt"] | NotGiven = NOT_GIVEN, - # 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, - ) -> MemoryListResponse: - """ - Retrieves a paginated list of memories with their metadata and workflow status - - Args: - container_tags: Optional tags this memory should be containerized by. This can be an ID for your - user, a project ID, or any other identifier you wish to use to group memories. - - filters: Optional filters to apply to the search - - limit: Number of items per page - - order: Sort order - - page: Page number to fetch - - sort: Field to sort by - - 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._get( - "/v3/memories", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - { - "container_tags": container_tags, - "filters": filters, - "limit": limit, - "order": order, - "page": page, - "sort": sort, - }, - memory_list_params.MemoryListParams, - ), - ), - cast_to=MemoryListResponse, - ) - def delete( self, id: str, @@ -398,70 +331,6 @@ async def update( cast_to=MemoryUpdateResponse, ) - @typing_extensions.deprecated("deprecated") - async def list( - self, - *, - container_tags: List[str] | NotGiven = NOT_GIVEN, - filters: str | NotGiven = NOT_GIVEN, - limit: Union[str, float] | NotGiven = NOT_GIVEN, - order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, - page: Union[str, float] | NotGiven = NOT_GIVEN, - sort: Literal["createdAt", "updatedAt"] | NotGiven = NOT_GIVEN, - # 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, - ) -> MemoryListResponse: - """ - Retrieves a paginated list of memories with their metadata and workflow status - - Args: - container_tags: Optional tags this memory should be containerized by. This can be an ID for your - user, a project ID, or any other identifier you wish to use to group memories. - - filters: Optional filters to apply to the search - - limit: Number of items per page - - order: Sort order - - page: Page number to fetch - - sort: Field to sort by - - 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._get( - "/v3/memories", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=await async_maybe_transform( - { - "container_tags": container_tags, - "filters": filters, - "limit": limit, - "order": order, - "page": page, - "sort": sort, - }, - memory_list_params.MemoryListParams, - ), - ), - cast_to=MemoryListResponse, - ) - async def delete( self, id: str, @@ -601,11 +470,6 @@ def __init__(self, memories: MemoriesResource) -> None: self.update = to_raw_response_wrapper( memories.update, ) - self.list = ( # pyright: ignore[reportDeprecated] - to_raw_response_wrapper( - memories.list # pyright: ignore[reportDeprecated], - ) - ) self.delete = to_raw_response_wrapper( memories.delete, ) @@ -624,11 +488,6 @@ def __init__(self, memories: AsyncMemoriesResource) -> None: self.update = async_to_raw_response_wrapper( memories.update, ) - self.list = ( # pyright: ignore[reportDeprecated] - async_to_raw_response_wrapper( - memories.list # pyright: ignore[reportDeprecated], - ) - ) self.delete = async_to_raw_response_wrapper( memories.delete, ) @@ -647,11 +506,6 @@ def __init__(self, memories: MemoriesResource) -> None: self.update = to_streamed_response_wrapper( memories.update, ) - self.list = ( # pyright: ignore[reportDeprecated] - to_streamed_response_wrapper( - memories.list # pyright: ignore[reportDeprecated], - ) - ) self.delete = to_streamed_response_wrapper( memories.delete, ) @@ -670,11 +524,6 @@ def __init__(self, memories: AsyncMemoriesResource) -> None: self.update = async_to_streamed_response_wrapper( memories.update, ) - self.list = ( # pyright: ignore[reportDeprecated] - async_to_streamed_response_wrapper( - memories.list # pyright: ignore[reportDeprecated], - ) - ) self.delete = async_to_streamed_response_wrapper( memories.delete, ) diff --git a/src/supermemory/resources/search.py b/src/supermemory/resources/search.py deleted file mode 100644 index 3f5bcea8..00000000 --- a/src/supermemory/resources/search.py +++ /dev/null @@ -1,205 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -import typing_extensions - -import httpx - -from ..types import search_execute_params -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._utils import maybe_transform, async_maybe_transform -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from .._base_client import make_request_options -from ..types.search_execute_response import SearchExecuteResponse - -__all__ = ["SearchResource", "AsyncSearchResource"] - - -class SearchResource(SyncAPIResource): - @cached_property - def with_raw_response(self) -> SearchResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/supermemoryai/python-sdk#accessing-raw-response-data-eg-headers - """ - return SearchResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> SearchResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/supermemoryai/python-sdk#with_streaming_response - """ - return SearchResourceWithStreamingResponse(self) - - @typing_extensions.deprecated("deprecated") - def execute( - self, - *, - q: str, - doc_id: str | NotGiven = NOT_GIVEN, - # 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, - ) -> SearchExecuteResponse: - """ - Search memories with basic filtering (Deprecated) - - Args: - q: Search query string - - doc_id: Optional document ID to search within. You can use this to find chunks in a very - large document. - - 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._get( - "/v3/search", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - { - "q": q, - "doc_id": doc_id, - }, - search_execute_params.SearchExecuteParams, - ), - ), - cast_to=SearchExecuteResponse, - ) - - -class AsyncSearchResource(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncSearchResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/supermemoryai/python-sdk#accessing-raw-response-data-eg-headers - """ - return AsyncSearchResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncSearchResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/supermemoryai/python-sdk#with_streaming_response - """ - return AsyncSearchResourceWithStreamingResponse(self) - - @typing_extensions.deprecated("deprecated") - async def execute( - self, - *, - q: str, - doc_id: str | NotGiven = NOT_GIVEN, - # 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, - ) -> SearchExecuteResponse: - """ - Search memories with basic filtering (Deprecated) - - Args: - q: Search query string - - doc_id: Optional document ID to search within. You can use this to find chunks in a very - large document. - - 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._get( - "/v3/search", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=await async_maybe_transform( - { - "q": q, - "doc_id": doc_id, - }, - search_execute_params.SearchExecuteParams, - ), - ), - cast_to=SearchExecuteResponse, - ) - - -class SearchResourceWithRawResponse: - def __init__(self, search: SearchResource) -> None: - self._search = search - - self.execute = ( # pyright: ignore[reportDeprecated] - to_raw_response_wrapper( - search.execute # pyright: ignore[reportDeprecated], - ) - ) - - -class AsyncSearchResourceWithRawResponse: - def __init__(self, search: AsyncSearchResource) -> None: - self._search = search - - self.execute = ( # pyright: ignore[reportDeprecated] - async_to_raw_response_wrapper( - search.execute # pyright: ignore[reportDeprecated], - ) - ) - - -class SearchResourceWithStreamingResponse: - def __init__(self, search: SearchResource) -> None: - self._search = search - - self.execute = ( # pyright: ignore[reportDeprecated] - to_streamed_response_wrapper( - search.execute # pyright: ignore[reportDeprecated], - ) - ) - - -class AsyncSearchResourceWithStreamingResponse: - def __init__(self, search: AsyncSearchResource) -> None: - self._search = search - - self.execute = ( # pyright: ignore[reportDeprecated] - async_to_streamed_response_wrapper( - search.execute # pyright: ignore[reportDeprecated], - ) - ) diff --git a/src/supermemory/resources/settings.py b/src/supermemory/resources/settings.py index 33a331fa..1ae36b65 100644 --- a/src/supermemory/resources/settings.py +++ b/src/supermemory/resources/settings.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import Dict, List +from typing import Dict, Union, Iterable, Optional import httpx @@ -47,11 +47,20 @@ def with_streaming_response(self) -> SettingsResourceWithStreamingResponse: def update( self, *, - exclude_items: List[str] | NotGiven = NOT_GIVEN, - filter_prompt: str | NotGiven = NOT_GIVEN, - filter_tags: Dict[str, List[str]] | NotGiven = NOT_GIVEN, - include_items: List[str] | NotGiven = NOT_GIVEN, - should_llm_filter: bool | NotGiven = NOT_GIVEN, + exclude_items: Union[str, float, bool, Dict[str, object], Iterable[object], None] | NotGiven = NOT_GIVEN, + filter_prompt: Optional[str] | NotGiven = NOT_GIVEN, + filter_tags: Union[str, float, bool, Dict[str, object], Iterable[object], None] | NotGiven = NOT_GIVEN, + google_drive_client_id: Optional[str] | NotGiven = NOT_GIVEN, + google_drive_client_secret: Optional[str] | NotGiven = NOT_GIVEN, + google_drive_custom_key_enabled: Optional[bool] | NotGiven = NOT_GIVEN, + include_items: Union[str, float, bool, Dict[str, object], Iterable[object], None] | NotGiven = NOT_GIVEN, + notion_client_id: Optional[str] | NotGiven = NOT_GIVEN, + notion_client_secret: Optional[str] | NotGiven = NOT_GIVEN, + notion_custom_key_enabled: Optional[bool] | NotGiven = NOT_GIVEN, + onedrive_client_id: Optional[str] | NotGiven = NOT_GIVEN, + onedrive_client_secret: Optional[str] | NotGiven = NOT_GIVEN, + onedrive_custom_key_enabled: Optional[bool] | NotGiven = NOT_GIVEN, + should_llm_filter: Optional[bool] | NotGiven = NOT_GIVEN, # 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, @@ -78,7 +87,16 @@ def update( "exclude_items": exclude_items, "filter_prompt": filter_prompt, "filter_tags": filter_tags, + "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, "include_items": include_items, + "notion_client_id": notion_client_id, + "notion_client_secret": notion_client_secret, + "notion_custom_key_enabled": notion_custom_key_enabled, + "onedrive_client_id": onedrive_client_id, + "onedrive_client_secret": onedrive_client_secret, + "onedrive_custom_key_enabled": onedrive_custom_key_enabled, "should_llm_filter": should_llm_filter, }, setting_update_params.SettingUpdateParams, @@ -132,11 +150,20 @@ def with_streaming_response(self) -> AsyncSettingsResourceWithStreamingResponse: async def update( self, *, - exclude_items: List[str] | NotGiven = NOT_GIVEN, - filter_prompt: str | NotGiven = NOT_GIVEN, - filter_tags: Dict[str, List[str]] | NotGiven = NOT_GIVEN, - include_items: List[str] | NotGiven = NOT_GIVEN, - should_llm_filter: bool | NotGiven = NOT_GIVEN, + exclude_items: Union[str, float, bool, Dict[str, object], Iterable[object], None] | NotGiven = NOT_GIVEN, + filter_prompt: Optional[str] | NotGiven = NOT_GIVEN, + filter_tags: Union[str, float, bool, Dict[str, object], Iterable[object], None] | NotGiven = NOT_GIVEN, + google_drive_client_id: Optional[str] | NotGiven = NOT_GIVEN, + google_drive_client_secret: Optional[str] | NotGiven = NOT_GIVEN, + google_drive_custom_key_enabled: Optional[bool] | NotGiven = NOT_GIVEN, + include_items: Union[str, float, bool, Dict[str, object], Iterable[object], None] | NotGiven = NOT_GIVEN, + notion_client_id: Optional[str] | NotGiven = NOT_GIVEN, + notion_client_secret: Optional[str] | NotGiven = NOT_GIVEN, + notion_custom_key_enabled: Optional[bool] | NotGiven = NOT_GIVEN, + onedrive_client_id: Optional[str] | NotGiven = NOT_GIVEN, + onedrive_client_secret: Optional[str] | NotGiven = NOT_GIVEN, + onedrive_custom_key_enabled: Optional[bool] | NotGiven = NOT_GIVEN, + should_llm_filter: Optional[bool] | NotGiven = NOT_GIVEN, # 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, @@ -163,7 +190,16 @@ async def update( "exclude_items": exclude_items, "filter_prompt": filter_prompt, "filter_tags": filter_tags, + "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, "include_items": include_items, + "notion_client_id": notion_client_id, + "notion_client_secret": notion_client_secret, + "notion_custom_key_enabled": notion_custom_key_enabled, + "onedrive_client_id": onedrive_client_id, + "onedrive_client_secret": onedrive_client_secret, + "onedrive_custom_key_enabled": onedrive_custom_key_enabled, "should_llm_filter": should_llm_filter, }, setting_update_params.SettingUpdateParams, diff --git a/src/supermemory/types/__init__.py b/src/supermemory/types/__init__.py index 7dfa5f0a..6ca2ae1a 100644 --- a/src/supermemory/types/__init__.py +++ b/src/supermemory/types/__init__.py @@ -3,17 +3,13 @@ from __future__ import annotations from .memory_add_params import MemoryAddParams as MemoryAddParams -from .memory_list_params import MemoryListParams as MemoryListParams from .memory_add_response import MemoryAddResponse as MemoryAddResponse from .memory_get_response import MemoryGetResponse as MemoryGetResponse -from .memory_list_response import MemoryListResponse as MemoryListResponse from .memory_update_params import MemoryUpdateParams as MemoryUpdateParams from .setting_get_response import SettingGetResponse as SettingGetResponse -from .search_execute_params import SearchExecuteParams as SearchExecuteParams from .setting_update_params import SettingUpdateParams as SettingUpdateParams from .memory_update_response import MemoryUpdateResponse as MemoryUpdateResponse from .connection_get_response import ConnectionGetResponse as ConnectionGetResponse -from .search_execute_response import SearchExecuteResponse as SearchExecuteResponse from .setting_update_response import SettingUpdateResponse as SettingUpdateResponse from .connection_create_params import ConnectionCreateParams as ConnectionCreateParams from .connection_create_response import ConnectionCreateResponse as ConnectionCreateResponse diff --git a/src/supermemory/types/memory_list_params.py b/src/supermemory/types/memory_list_params.py deleted file mode 100644 index 7e683d71..00000000 --- a/src/supermemory/types/memory_list_params.py +++ /dev/null @@ -1,34 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import List, Union -from typing_extensions import Literal, Annotated, TypedDict - -from .._utils import PropertyInfo - -__all__ = ["MemoryListParams"] - - -class MemoryListParams(TypedDict, total=False): - container_tags: Annotated[List[str], PropertyInfo(alias="containerTags")] - """Optional tags this memory should be containerized by. - - This can be an ID for your user, a project ID, or any other identifier you wish - to use to group memories. - """ - - filters: str - """Optional filters to apply to the search""" - - limit: Union[str, float] - """Number of items per page""" - - order: Literal["asc", "desc"] - """Sort order""" - - page: Union[str, float] - """Page number to fetch""" - - sort: Literal["createdAt", "updatedAt"] - """Field to sort by""" diff --git a/src/supermemory/types/memory_list_response.py b/src/supermemory/types/memory_list_response.py deleted file mode 100644 index e2d321ab..00000000 --- a/src/supermemory/types/memory_list_response.py +++ /dev/null @@ -1,81 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Dict, List, Union, Optional -from datetime import datetime -from typing_extensions import Literal - -from pydantic import Field as FieldInfo - -from .._models import BaseModel - -__all__ = ["MemoryListResponse", "Memory", "Pagination"] - - -class Memory(BaseModel): - id: str - """Unique identifier of the memory.""" - - connection_id: Optional[str] = FieldInfo(alias="connectionId", default=None) - """Optional ID of connection the memory was created from. - - This is useful for identifying the source of the memory. - """ - - created_at: datetime = FieldInfo(alias="createdAt") - """Creation timestamp""" - - custom_id: Optional[str] = FieldInfo(alias="customId", default=None) - """Optional custom ID of the memory. - - This could be an ID from your database that will uniquely identify this memory. - """ - - metadata: Union[str, float, bool, Dict[str, object], List[object], None] = None - """Optional metadata for the memory. - - This is used to store additional information about the memory. You can use this - to store any additional information you need about the memory. Metadata can be - filtered through. Keys must be strings and are case sensitive. Values can be - strings, numbers, or booleans. You cannot nest objects. - """ - - status: Literal["unknown", "queued", "extracting", "chunking", "embedding", "indexing", "done", "failed"] - """Status of the memory""" - - summary: Optional[str] = None - """Summary of the memory content""" - - title: Optional[str] = None - """Title of the memory""" - - type: Literal[ - "text", "pdf", "tweet", "google_doc", "google_slide", "google_sheet", "image", "video", "notion_doc", "webpage" - ] - """Type of the memory""" - - updated_at: datetime = FieldInfo(alias="updatedAt") - """Last update timestamp""" - - container_tags: Optional[List[str]] = FieldInfo(alias="containerTags", default=None) - """Optional tags this memory should be containerized by. - - This can be an ID for your user, a project ID, or any other identifier you wish - to use to group memories. - """ - - -class Pagination(BaseModel): - current_page: float = FieldInfo(alias="currentPage") - - limit: float - - total_items: float = FieldInfo(alias="totalItems") - - total_pages: float = FieldInfo(alias="totalPages") - - -class MemoryListResponse(BaseModel): - memories: List[Memory] - - pagination: Pagination - """Pagination metadata""" diff --git a/src/supermemory/types/search_execute_params.py b/src/supermemory/types/search_execute_params.py deleted file mode 100644 index e7b8f595..00000000 --- a/src/supermemory/types/search_execute_params.py +++ /dev/null @@ -1,20 +0,0 @@ -# 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__ = ["SearchExecuteParams"] - - -class SearchExecuteParams(TypedDict, total=False): - q: Required[str] - """Search query string""" - - doc_id: Annotated[str, PropertyInfo(alias="docId")] - """Optional document ID to search within. - - You can use this to find chunks in a very large document. - """ diff --git a/src/supermemory/types/search_execute_response.py b/src/supermemory/types/search_execute_response.py deleted file mode 100644 index b48b351d..00000000 --- a/src/supermemory/types/search_execute_response.py +++ /dev/null @@ -1,55 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Dict, List, Optional -from datetime import datetime - -from pydantic import Field as FieldInfo - -from .._models import BaseModel - -__all__ = ["SearchExecuteResponse", "Result", "ResultChunk"] - - -class ResultChunk(BaseModel): - content: str - """Content of the matching chunk""" - - is_relevant: bool = FieldInfo(alias="isRelevant") - """Whether this chunk is relevant to the query""" - - score: float - """Similarity score for this chunk""" - - -class Result(BaseModel): - chunks: List[ResultChunk] - """Matching content chunks from the document""" - - created_at: datetime = FieldInfo(alias="createdAt") - """Document creation date""" - - document_id: str = FieldInfo(alias="documentId") - """ID of the matching document""" - - metadata: Optional[Dict[str, object]] = None - """Document metadata""" - - score: float - """Relevance score of the match""" - - title: Optional[str] = None - """Document title""" - - updated_at: datetime = FieldInfo(alias="updatedAt") - """Document last update date""" - - summary: Optional[str] = None - """Document summary""" - - -class SearchExecuteResponse(BaseModel): - results: List[Result] - - timing: float - - total: float diff --git a/src/supermemory/types/setting_get_response.py b/src/supermemory/types/setting_get_response.py index 7f0c9df7..32bdbe57 100644 --- a/src/supermemory/types/setting_get_response.py +++ b/src/supermemory/types/setting_get_response.py @@ -1,6 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import Dict, List, Optional +from typing import Dict, List, Union, Optional from pydantic import Field as FieldInfo @@ -10,12 +10,36 @@ class SettingGetResponse(BaseModel): - exclude_items: Optional[List[str]] = FieldInfo(alias="excludeItems", default=None) + exclude_items: Union[str, float, bool, Dict[str, object], List[object], None] = FieldInfo( + alias="excludeItems", default=None + ) filter_prompt: Optional[str] = FieldInfo(alias="filterPrompt", default=None) - filter_tags: Optional[Dict[str, List[str]]] = FieldInfo(alias="filterTags", default=None) + filter_tags: Union[str, float, bool, Dict[str, object], List[object], None] = FieldInfo( + alias="filterTags", default=None + ) - include_items: Optional[List[str]] = FieldInfo(alias="includeItems", default=None) + google_drive_client_id: Optional[str] = FieldInfo(alias="googleDriveClientId", default=None) + + google_drive_client_secret: Optional[str] = FieldInfo(alias="googleDriveClientSecret", default=None) + + google_drive_custom_key_enabled: Optional[bool] = FieldInfo(alias="googleDriveCustomKeyEnabled", default=None) + + include_items: Union[str, float, bool, Dict[str, object], List[object], None] = FieldInfo( + alias="includeItems", default=None + ) + + notion_client_id: Optional[str] = FieldInfo(alias="notionClientId", default=None) + + notion_client_secret: Optional[str] = FieldInfo(alias="notionClientSecret", default=None) + + notion_custom_key_enabled: Optional[bool] = FieldInfo(alias="notionCustomKeyEnabled", default=None) + + onedrive_client_id: Optional[str] = FieldInfo(alias="onedriveClientId", default=None) + + onedrive_client_secret: Optional[str] = FieldInfo(alias="onedriveClientSecret", default=None) + + onedrive_custom_key_enabled: Optional[bool] = FieldInfo(alias="onedriveCustomKeyEnabled", default=None) should_llm_filter: Optional[bool] = FieldInfo(alias="shouldLLMFilter", default=None) diff --git a/src/supermemory/types/setting_update_params.py b/src/supermemory/types/setting_update_params.py index 8f19a083..16263ee7 100644 --- a/src/supermemory/types/setting_update_params.py +++ b/src/supermemory/types/setting_update_params.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import Dict, List +from typing import Dict, Union, Iterable, Optional from typing_extensions import Annotated, TypedDict from .._utils import PropertyInfo @@ -11,12 +11,36 @@ class SettingUpdateParams(TypedDict, total=False): - exclude_items: Annotated[List[str], PropertyInfo(alias="excludeItems")] + exclude_items: Annotated[ + Union[str, float, bool, Dict[str, object], Iterable[object], None], PropertyInfo(alias="excludeItems") + ] - filter_prompt: Annotated[str, PropertyInfo(alias="filterPrompt")] + filter_prompt: Annotated[Optional[str], PropertyInfo(alias="filterPrompt")] - filter_tags: Annotated[Dict[str, List[str]], PropertyInfo(alias="filterTags")] + filter_tags: Annotated[ + Union[str, float, bool, Dict[str, object], Iterable[object], None], PropertyInfo(alias="filterTags") + ] - include_items: Annotated[List[str], PropertyInfo(alias="includeItems")] + google_drive_client_id: Annotated[Optional[str], PropertyInfo(alias="googleDriveClientId")] - should_llm_filter: Annotated[bool, PropertyInfo(alias="shouldLLMFilter")] + google_drive_client_secret: Annotated[Optional[str], PropertyInfo(alias="googleDriveClientSecret")] + + google_drive_custom_key_enabled: Annotated[Optional[bool], PropertyInfo(alias="googleDriveCustomKeyEnabled")] + + include_items: Annotated[ + Union[str, float, bool, Dict[str, object], Iterable[object], None], PropertyInfo(alias="includeItems") + ] + + notion_client_id: Annotated[Optional[str], PropertyInfo(alias="notionClientId")] + + notion_client_secret: Annotated[Optional[str], PropertyInfo(alias="notionClientSecret")] + + notion_custom_key_enabled: Annotated[Optional[bool], PropertyInfo(alias="notionCustomKeyEnabled")] + + onedrive_client_id: Annotated[Optional[str], PropertyInfo(alias="onedriveClientId")] + + onedrive_client_secret: Annotated[Optional[str], PropertyInfo(alias="onedriveClientSecret")] + + onedrive_custom_key_enabled: Annotated[Optional[bool], PropertyInfo(alias="onedriveCustomKeyEnabled")] + + should_llm_filter: Annotated[Optional[bool], PropertyInfo(alias="shouldLLMFilter")] diff --git a/src/supermemory/types/setting_update_response.py b/src/supermemory/types/setting_update_response.py index 55725300..3770b38a 100644 --- a/src/supermemory/types/setting_update_response.py +++ b/src/supermemory/types/setting_update_response.py @@ -1,6 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import Dict, List, Optional +from typing import Dict, List, Union, Optional from pydantic import Field as FieldInfo @@ -10,13 +10,37 @@ class Updated(BaseModel): - exclude_items: Optional[List[str]] = FieldInfo(alias="excludeItems", default=None) + exclude_items: Union[str, float, bool, Dict[str, object], List[object], None] = FieldInfo( + alias="excludeItems", default=None + ) filter_prompt: Optional[str] = FieldInfo(alias="filterPrompt", default=None) - filter_tags: Optional[Dict[str, List[str]]] = FieldInfo(alias="filterTags", default=None) + filter_tags: Union[str, float, bool, Dict[str, object], List[object], None] = FieldInfo( + alias="filterTags", default=None + ) - include_items: Optional[List[str]] = FieldInfo(alias="includeItems", default=None) + google_drive_client_id: Optional[str] = FieldInfo(alias="googleDriveClientId", default=None) + + google_drive_client_secret: Optional[str] = FieldInfo(alias="googleDriveClientSecret", default=None) + + google_drive_custom_key_enabled: Optional[bool] = FieldInfo(alias="googleDriveCustomKeyEnabled", default=None) + + include_items: Union[str, float, bool, Dict[str, object], List[object], None] = FieldInfo( + alias="includeItems", default=None + ) + + notion_client_id: Optional[str] = FieldInfo(alias="notionClientId", default=None) + + notion_client_secret: Optional[str] = FieldInfo(alias="notionClientSecret", default=None) + + notion_custom_key_enabled: Optional[bool] = FieldInfo(alias="notionCustomKeyEnabled", default=None) + + onedrive_client_id: Optional[str] = FieldInfo(alias="onedriveClientId", default=None) + + onedrive_client_secret: Optional[str] = FieldInfo(alias="onedriveClientSecret", default=None) + + onedrive_custom_key_enabled: Optional[bool] = FieldInfo(alias="onedriveCustomKeyEnabled", default=None) should_llm_filter: Optional[bool] = FieldInfo(alias="shouldLLMFilter", default=None) diff --git a/tests/api_resources/test_memories.py b/tests/api_resources/test_memories.py index 2aae3b46..6336b998 100644 --- a/tests/api_resources/test_memories.py +++ b/tests/api_resources/test_memories.py @@ -12,12 +12,9 @@ from supermemory.types import ( MemoryAddResponse, MemoryGetResponse, - MemoryListResponse, MemoryUpdateResponse, ) -# pyright: reportDeprecated=false - base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -89,53 +86,6 @@ def test_path_params_update(self, client: Supermemory) -> None: content="This is a detailed article about machine learning concepts...", ) - @pytest.mark.skip() - @parametrize - def test_method_list(self, client: Supermemory) -> None: - with pytest.warns(DeprecationWarning): - memory = client.memories.list() - - assert_matches_type(MemoryListResponse, memory, path=["response"]) - - @pytest.mark.skip() - @parametrize - def test_method_list_with_all_params(self, client: Supermemory) -> None: - with pytest.warns(DeprecationWarning): - memory = client.memories.list( - container_tags=["user_123", "project_123"], - filters='{"AND":[{"key":"group","value":"jira_users","negate":false},{"filterType":"numeric","key":"timestamp","value":"1742745777","negate":false,"numericOperator":">"}]}', - limit="10", - order="desc", - page="1", - sort="createdAt", - ) - - assert_matches_type(MemoryListResponse, memory, path=["response"]) - - @pytest.mark.skip() - @parametrize - def test_raw_response_list(self, client: Supermemory) -> None: - with pytest.warns(DeprecationWarning): - response = client.memories.with_raw_response.list() - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - memory = response.parse() - assert_matches_type(MemoryListResponse, memory, path=["response"]) - - @pytest.mark.skip() - @parametrize - def test_streaming_response_list(self, client: Supermemory) -> None: - with pytest.warns(DeprecationWarning): - with client.memories.with_streaming_response.list() as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - memory = response.parse() - assert_matches_type(MemoryListResponse, memory, path=["response"]) - - assert cast(Any, response.is_closed) is True - @pytest.mark.skip() @parametrize def test_method_delete(self, client: Supermemory) -> None: @@ -341,53 +291,6 @@ async def test_path_params_update(self, async_client: AsyncSupermemory) -> None: content="This is a detailed article about machine learning concepts...", ) - @pytest.mark.skip() - @parametrize - async def test_method_list(self, async_client: AsyncSupermemory) -> None: - with pytest.warns(DeprecationWarning): - memory = await async_client.memories.list() - - assert_matches_type(MemoryListResponse, memory, path=["response"]) - - @pytest.mark.skip() - @parametrize - async def test_method_list_with_all_params(self, async_client: AsyncSupermemory) -> None: - with pytest.warns(DeprecationWarning): - memory = await async_client.memories.list( - container_tags=["user_123", "project_123"], - filters='{"AND":[{"key":"group","value":"jira_users","negate":false},{"filterType":"numeric","key":"timestamp","value":"1742745777","negate":false,"numericOperator":">"}]}', - limit="10", - order="desc", - page="1", - sort="createdAt", - ) - - assert_matches_type(MemoryListResponse, memory, path=["response"]) - - @pytest.mark.skip() - @parametrize - async def test_raw_response_list(self, async_client: AsyncSupermemory) -> None: - with pytest.warns(DeprecationWarning): - response = await async_client.memories.with_raw_response.list() - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - memory = await response.parse() - assert_matches_type(MemoryListResponse, memory, path=["response"]) - - @pytest.mark.skip() - @parametrize - async def test_streaming_response_list(self, async_client: AsyncSupermemory) -> None: - with pytest.warns(DeprecationWarning): - async with async_client.memories.with_streaming_response.list() as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - memory = await response.parse() - assert_matches_type(MemoryListResponse, memory, path=["response"]) - - assert cast(Any, response.is_closed) is True - @pytest.mark.skip() @parametrize async def test_method_delete(self, async_client: AsyncSupermemory) -> None: diff --git a/tests/api_resources/test_search.py b/tests/api_resources/test_search.py deleted file mode 100644 index f60a8b74..00000000 --- a/tests/api_resources/test_search.py +++ /dev/null @@ -1,122 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -import os -from typing import Any, cast - -import pytest - -from supermemory import Supermemory, AsyncSupermemory -from tests.utils import assert_matches_type -from supermemory.types import SearchExecuteResponse - -# pyright: reportDeprecated=false - -base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") - - -class TestSearch: - parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - - @pytest.mark.skip() - @parametrize - def test_method_execute(self, client: Supermemory) -> None: - with pytest.warns(DeprecationWarning): - search = client.search.execute( - q="machine learning concepts", - ) - - assert_matches_type(SearchExecuteResponse, search, path=["response"]) - - @pytest.mark.skip() - @parametrize - def test_method_execute_with_all_params(self, client: Supermemory) -> None: - with pytest.warns(DeprecationWarning): - search = client.search.execute( - q="machine learning concepts", - doc_id="doc_xyz789", - ) - - assert_matches_type(SearchExecuteResponse, search, path=["response"]) - - @pytest.mark.skip() - @parametrize - def test_raw_response_execute(self, client: Supermemory) -> None: - with pytest.warns(DeprecationWarning): - response = client.search.with_raw_response.execute( - q="machine learning concepts", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - search = response.parse() - assert_matches_type(SearchExecuteResponse, search, path=["response"]) - - @pytest.mark.skip() - @parametrize - def test_streaming_response_execute(self, client: Supermemory) -> None: - with pytest.warns(DeprecationWarning): - with client.search.with_streaming_response.execute( - q="machine learning concepts", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - search = response.parse() - assert_matches_type(SearchExecuteResponse, search, path=["response"]) - - assert cast(Any, response.is_closed) is True - - -class TestAsyncSearch: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) - - @pytest.mark.skip() - @parametrize - async def test_method_execute(self, async_client: AsyncSupermemory) -> None: - with pytest.warns(DeprecationWarning): - search = await async_client.search.execute( - q="machine learning concepts", - ) - - assert_matches_type(SearchExecuteResponse, search, path=["response"]) - - @pytest.mark.skip() - @parametrize - async def test_method_execute_with_all_params(self, async_client: AsyncSupermemory) -> None: - with pytest.warns(DeprecationWarning): - search = await async_client.search.execute( - q="machine learning concepts", - doc_id="doc_xyz789", - ) - - assert_matches_type(SearchExecuteResponse, search, path=["response"]) - - @pytest.mark.skip() - @parametrize - async def test_raw_response_execute(self, async_client: AsyncSupermemory) -> None: - with pytest.warns(DeprecationWarning): - response = await async_client.search.with_raw_response.execute( - q="machine learning concepts", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - search = await response.parse() - assert_matches_type(SearchExecuteResponse, search, path=["response"]) - - @pytest.mark.skip() - @parametrize - async def test_streaming_response_execute(self, async_client: AsyncSupermemory) -> None: - with pytest.warns(DeprecationWarning): - async with async_client.search.with_streaming_response.execute( - q="machine learning concepts", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - search = await response.parse() - assert_matches_type(SearchExecuteResponse, search, path=["response"]) - - assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/test_settings.py b/tests/api_resources/test_settings.py index a7a8a777..652e9ad6 100644 --- a/tests/api_resources/test_settings.py +++ b/tests/api_resources/test_settings.py @@ -27,10 +27,19 @@ def test_method_update(self, client: Supermemory) -> None: @parametrize def test_method_update_with_all_params(self, client: Supermemory) -> None: setting = client.settings.update( - exclude_items=["x"], - filter_prompt="x", - filter_tags={"foo": ["string"]}, - include_items=["x"], + exclude_items="string", + filter_prompt="filterPrompt", + filter_tags="string", + google_drive_client_id="googleDriveClientId", + google_drive_client_secret="googleDriveClientSecret", + google_drive_custom_key_enabled=True, + include_items="string", + notion_client_id="notionClientId", + notion_client_secret="notionClientSecret", + notion_custom_key_enabled=True, + onedrive_client_id="onedriveClientId", + onedrive_client_secret="onedriveClientSecret", + onedrive_custom_key_enabled=True, should_llm_filter=True, ) assert_matches_type(SettingUpdateResponse, setting, path=["response"]) @@ -99,10 +108,19 @@ async def test_method_update(self, async_client: AsyncSupermemory) -> None: @parametrize async def test_method_update_with_all_params(self, async_client: AsyncSupermemory) -> None: setting = await async_client.settings.update( - exclude_items=["x"], - filter_prompt="x", - filter_tags={"foo": ["string"]}, - include_items=["x"], + exclude_items="string", + filter_prompt="filterPrompt", + filter_tags="string", + google_drive_client_id="googleDriveClientId", + google_drive_client_secret="googleDriveClientSecret", + google_drive_custom_key_enabled=True, + include_items="string", + notion_client_id="notionClientId", + notion_client_secret="notionClientSecret", + notion_custom_key_enabled=True, + onedrive_client_id="onedriveClientId", + onedrive_client_secret="onedriveClientSecret", + onedrive_custom_key_enabled=True, should_llm_filter=True, ) assert_matches_type(SettingUpdateResponse, setting, path=["response"]) From 5aefc85540c70a195c2c59a8e9cdd529ff124a3b Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 21 Jun 2025 03:18:39 +0000 Subject: [PATCH 40/48] feat(api): api update --- .stats.yml | 4 ++-- src/supermemory/types/memory_get_response.py | 12 +++++++++++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/.stats.yml b/.stats.yml index 539bcbfc..8dc875d1 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 8 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/supermemory--inc%2Fsupermemory-ce8810a8f8d5fa2d49c37e64b5bc3847eb102d7cc9154455ee598be5d118614d.yml -openapi_spec_hash: 36e6e52e5cf59f5df66f96afae0afef7 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/supermemory--inc%2Fsupermemory-121e18b2f7d9c4c8e3d6264ba5533cf0e10d566487a69cad5729842652ef509c.yml +openapi_spec_hash: 0836cd0867d256250f8a040e4c6f81dc config_hash: 8477e3ee6fd596ab6ac911d052e4de79 diff --git a/src/supermemory/types/memory_get_response.py b/src/supermemory/types/memory_get_response.py index b52e788c..90bf8f31 100644 --- a/src/supermemory/types/memory_get_response.py +++ b/src/supermemory/types/memory_get_response.py @@ -66,7 +66,17 @@ class MemoryGetResponse(BaseModel): """Title of the memory""" type: Literal[ - "text", "pdf", "tweet", "google_doc", "google_slide", "google_sheet", "image", "video", "notion_doc", "webpage" + "text", + "pdf", + "tweet", + "google_doc", + "google_slide", + "google_sheet", + "image", + "video", + "notion_doc", + "webpage", + "onedrive", ] """Type of the memory""" From d37671920ac604d643c0d9edaaee08aaaea2d881 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 21 Jun 2025 04:04:02 +0000 Subject: [PATCH 41/48] feat(client): add support for aiohttp --- README.md | 34 +++++++++++++++++++ pyproject.toml | 2 ++ requirements-dev.lock | 27 ++++++++++++++++ requirements.lock | 27 ++++++++++++++++ src/supermemory/__init__.py | 3 +- src/supermemory/_base_client.py | 22 +++++++++++++ tests/api_resources/test_connections.py | 4 ++- tests/api_resources/test_memories.py | 4 ++- tests/api_resources/test_settings.py | 4 ++- tests/conftest.py | 43 +++++++++++++++++++++---- 10 files changed, 160 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 2957f876..8557c60b 100644 --- a/README.md +++ b/README.md @@ -68,6 +68,40 @@ asyncio.run(main()) Functionality between the synchronous and asynchronous clients is otherwise identical. +### With aiohttp + +By default, the async client uses `httpx` for HTTP requests. However, for improved concurrency performance you may also use `aiohttp` as the HTTP backend. + +You can enable this by installing `aiohttp`: + +```sh +# install from PyPI +pip install --pre 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 + + +async def main() -> None: + async with AsyncSupermemory( + api_key=os.environ.get("SUPERMEMORY_API_KEY"), # This is the default and can be omitted + http_client=DefaultAioHttpClient(), + ) as client: + response = await client.memories.add( + content="This is a detailed article about machine learning concepts...", + ) + print(response.id) + + +asyncio.run(main()) +``` + ## Using types Nested request parameters are [TypedDicts](https://docs.python.org/3/library/typing.html#typing.TypedDict). Responses are [Pydantic models](https://docs.pydantic.dev) which also provide helper methods for things like: diff --git a/pyproject.toml b/pyproject.toml index 2608b936..8f895d43 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -37,6 +37,8 @@ classifiers = [ Homepage = "https://github.com/supermemoryai/python-sdk" Repository = "https://github.com/supermemoryai/python-sdk" +[project.optional-dependencies] +aiohttp = ["aiohttp", "httpx_aiohttp>=0.1.6"] [tool.rye] managed = true diff --git a/requirements-dev.lock b/requirements-dev.lock index 14d829b6..c63e536d 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -10,6 +10,13 @@ # universal: false -e file:. +aiohappyeyeballs==2.6.1 + # via aiohttp +aiohttp==3.12.8 + # via httpx-aiohttp + # via supermemory +aiosignal==1.3.2 + # via aiohttp annotated-types==0.6.0 # via pydantic anyio==4.4.0 @@ -17,6 +24,10 @@ anyio==4.4.0 # via supermemory argcomplete==3.1.2 # via nox +async-timeout==5.0.1 + # via aiohttp +attrs==25.3.0 + # via aiohttp certifi==2023.7.22 # via httpcore # via httpx @@ -34,16 +45,23 @@ execnet==2.1.1 # via pytest-xdist filelock==3.12.4 # via virtualenv +frozenlist==1.6.2 + # via aiohttp + # via aiosignal h11==0.14.0 # via httpcore httpcore==1.0.2 # via httpx httpx==0.28.1 + # via httpx-aiohttp # via respx # via supermemory +httpx-aiohttp==0.1.6 + # via supermemory idna==3.4 # via anyio # via httpx + # via yarl importlib-metadata==7.0.0 iniconfig==2.0.0 # via pytest @@ -51,6 +69,9 @@ markdown-it-py==3.0.0 # via rich mdurl==0.1.2 # via markdown-it-py +multidict==6.4.4 + # via aiohttp + # via yarl mypy==1.14.1 mypy-extensions==1.0.0 # via mypy @@ -65,6 +86,9 @@ platformdirs==3.11.0 # via virtualenv pluggy==1.5.0 # via pytest +propcache==0.3.1 + # via aiohttp + # via yarl pydantic==2.10.3 # via supermemory pydantic-core==2.27.1 @@ -97,6 +121,7 @@ tomli==2.0.2 # via pytest typing-extensions==4.12.2 # via anyio + # via multidict # via mypy # via pydantic # via pydantic-core @@ -104,5 +129,7 @@ typing-extensions==4.12.2 # via supermemory virtualenv==20.24.5 # via nox +yarl==1.20.0 + # via aiohttp zipp==3.17.0 # via importlib-metadata diff --git a/requirements.lock b/requirements.lock index c3540f77..c54f68b1 100644 --- a/requirements.lock +++ b/requirements.lock @@ -10,11 +10,22 @@ # universal: false -e file:. +aiohappyeyeballs==2.6.1 + # via aiohttp +aiohttp==3.12.8 + # via httpx-aiohttp + # via supermemory +aiosignal==1.3.2 + # via aiohttp annotated-types==0.6.0 # via pydantic anyio==4.4.0 # via httpx # via supermemory +async-timeout==5.0.1 + # via aiohttp +attrs==25.3.0 + # via aiohttp certifi==2023.7.22 # via httpcore # via httpx @@ -22,15 +33,28 @@ distro==1.8.0 # via supermemory exceptiongroup==1.2.2 # via anyio +frozenlist==1.6.2 + # via aiohttp + # via aiosignal h11==0.14.0 # via httpcore httpcore==1.0.2 # via httpx httpx==0.28.1 + # via httpx-aiohttp + # via supermemory +httpx-aiohttp==0.1.6 # via supermemory idna==3.4 # via anyio # via httpx + # via yarl +multidict==6.4.4 + # via aiohttp + # via yarl +propcache==0.3.1 + # via aiohttp + # via yarl pydantic==2.10.3 # via supermemory pydantic-core==2.27.1 @@ -40,6 +64,9 @@ sniffio==1.3.0 # via supermemory typing-extensions==4.12.2 # via anyio + # via multidict # via pydantic # via pydantic-core # via supermemory +yarl==1.20.0 + # via aiohttp diff --git a/src/supermemory/__init__.py b/src/supermemory/__init__.py index 3cc510e4..bb87a0cf 100644 --- a/src/supermemory/__init__.py +++ b/src/supermemory/__init__.py @@ -36,7 +36,7 @@ UnprocessableEntityError, APIResponseValidationError, ) -from ._base_client import DefaultHttpxClient, DefaultAsyncHttpxClient +from ._base_client import DefaultHttpxClient, DefaultAioHttpClient, DefaultAsyncHttpxClient from ._utils._logs import setup_logging as _setup_logging __all__ = [ @@ -78,6 +78,7 @@ "DEFAULT_CONNECTION_LIMITS", "DefaultHttpxClient", "DefaultAsyncHttpxClient", + "DefaultAioHttpClient", ] if not _t.TYPE_CHECKING: diff --git a/src/supermemory/_base_client.py b/src/supermemory/_base_client.py index 01d05a51..9a46a403 100644 --- a/src/supermemory/_base_client.py +++ b/src/supermemory/_base_client.py @@ -1289,6 +1289,24 @@ def __init__(self, **kwargs: Any) -> None: super().__init__(**kwargs) +try: + import httpx_aiohttp +except ImportError: + + class _DefaultAioHttpClient(httpx.AsyncClient): + def __init__(self, **_kwargs: Any) -> None: + raise RuntimeError("To use the aiohttp client you must have installed the package with the `aiohttp` extra") +else: + + class _DefaultAioHttpClient(httpx_aiohttp.HttpxAiohttpClient): # type: ignore + def __init__(self, **kwargs: Any) -> None: + kwargs.setdefault("timeout", DEFAULT_TIMEOUT) + kwargs.setdefault("limits", DEFAULT_CONNECTION_LIMITS) + kwargs.setdefault("follow_redirects", True) + + super().__init__(**kwargs) + + if TYPE_CHECKING: DefaultAsyncHttpxClient = httpx.AsyncClient """An alias to `httpx.AsyncClient` that provides the same defaults that this SDK @@ -1297,8 +1315,12 @@ def __init__(self, **kwargs: Any) -> None: This is useful because overriding the `http_client` with your own instance of `httpx.AsyncClient` will result in httpx's defaults being used, not ours. """ + + DefaultAioHttpClient = httpx.AsyncClient + """An alias to `httpx.AsyncClient` that changes the default HTTP transport to `aiohttp`.""" else: DefaultAsyncHttpxClient = _DefaultAsyncHttpxClient + DefaultAioHttpClient = _DefaultAioHttpClient class AsyncHttpxClientWrapper(DefaultAsyncHttpxClient): diff --git a/tests/api_resources/test_connections.py b/tests/api_resources/test_connections.py index 18492519..bb4ed3e0 100644 --- a/tests/api_resources/test_connections.py +++ b/tests/api_resources/test_connections.py @@ -107,7 +107,9 @@ def test_path_params_get(self, client: Supermemory) -> None: class TestAsyncConnections: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @pytest.mark.skip() @parametrize diff --git a/tests/api_resources/test_memories.py b/tests/api_resources/test_memories.py index 6336b998..c398ab8f 100644 --- a/tests/api_resources/test_memories.py +++ b/tests/api_resources/test_memories.py @@ -224,7 +224,9 @@ def test_path_params_get(self, client: Supermemory) -> None: class TestAsyncMemories: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @pytest.mark.skip() @parametrize diff --git a/tests/api_resources/test_settings.py b/tests/api_resources/test_settings.py index 652e9ad6..ac790b15 100644 --- a/tests/api_resources/test_settings.py +++ b/tests/api_resources/test_settings.py @@ -96,7 +96,9 @@ def test_streaming_response_get(self, client: Supermemory) -> None: class TestAsyncSettings: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @pytest.mark.skip() @parametrize diff --git a/tests/conftest.py b/tests/conftest.py index d6ce7847..2baaab81 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -6,10 +6,12 @@ import logging from typing import TYPE_CHECKING, Iterator, AsyncIterator +import httpx import pytest from pytest_asyncio import is_async_test -from supermemory import Supermemory, AsyncSupermemory +from supermemory import Supermemory, AsyncSupermemory, DefaultAioHttpClient +from supermemory._utils import is_dict if TYPE_CHECKING: from _pytest.fixtures import FixtureRequest # pyright: ignore[reportPrivateImportUsage] @@ -27,6 +29,19 @@ def pytest_collection_modifyitems(items: list[pytest.Function]) -> None: for async_test in pytest_asyncio_tests: async_test.add_marker(session_scope_marker, append=False) + # We skip tests that use both the aiohttp client and respx_mock as respx_mock + # doesn't support custom transports. + for item in items: + if "async_client" not in item.fixturenames or "respx_mock" not in item.fixturenames: + continue + + if not hasattr(item, "callspec"): + continue + + async_client_param = item.callspec.params.get("async_client") + if is_dict(async_client_param) and async_client_param.get("http_client") == "aiohttp": + item.add_marker(pytest.mark.skip(reason="aiohttp client is not compatible with respx_mock")) + base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -45,9 +60,25 @@ def client(request: FixtureRequest) -> Iterator[Supermemory]: @pytest.fixture(scope="session") async def async_client(request: FixtureRequest) -> AsyncIterator[AsyncSupermemory]: - strict = getattr(request, "param", True) - if not isinstance(strict, bool): - raise TypeError(f"Unexpected fixture parameter type {type(strict)}, expected {bool}") - - async with AsyncSupermemory(base_url=base_url, api_key=api_key, _strict_response_validation=strict) as client: + param = getattr(request, "param", True) + + # defaults + strict = True + http_client: None | httpx.AsyncClient = None + + if isinstance(param, bool): + strict = param + elif is_dict(param): + strict = param.get("strict", True) + assert isinstance(strict, bool) + + http_client_type = param.get("http_client", "httpx") + if http_client_type == "aiohttp": + http_client = DefaultAioHttpClient() + else: + raise TypeError(f"Unexpected fixture parameter type {type(param)}, expected bool or dict") + + async with AsyncSupermemory( + base_url=base_url, api_key=api_key, _strict_response_validation=strict, http_client=http_client + ) as client: yield client From fc586f729ac602946e95de2e61b6d27a8c3b1167 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 21 Jun 2025 04:25:01 +0000 Subject: [PATCH 42/48] feat(api): api update --- .stats.yml | 4 +-- README.md | 34 ------------------- pyproject.toml | 2 -- requirements-dev.lock | 27 ---------------- requirements.lock | 27 ---------------- src/supermemory/__init__.py | 3 +- src/supermemory/_base_client.py | 22 ------------- tests/api_resources/test_connections.py | 4 +-- tests/api_resources/test_memories.py | 4 +-- tests/api_resources/test_settings.py | 4 +-- tests/conftest.py | 43 ++++--------------------- 11 files changed, 12 insertions(+), 162 deletions(-) diff --git a/.stats.yml b/.stats.yml index 8dc875d1..8f056938 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 8 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/supermemory--inc%2Fsupermemory-121e18b2f7d9c4c8e3d6264ba5533cf0e10d566487a69cad5729842652ef509c.yml -openapi_spec_hash: 0836cd0867d256250f8a040e4c6f81dc +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/supermemory--inc%2Fsupermemory-4c45e387cbdc7c80d75cdb8eb924cf92a3a48a0c10060fda917b83a7e454aef5.yml +openapi_spec_hash: c859ac2e3429ad3663337b99c722f317 config_hash: 8477e3ee6fd596ab6ac911d052e4de79 diff --git a/README.md b/README.md index 8557c60b..2957f876 100644 --- a/README.md +++ b/README.md @@ -68,40 +68,6 @@ asyncio.run(main()) Functionality between the synchronous and asynchronous clients is otherwise identical. -### With aiohttp - -By default, the async client uses `httpx` for HTTP requests. However, for improved concurrency performance you may also use `aiohttp` as the HTTP backend. - -You can enable this by installing `aiohttp`: - -```sh -# install from PyPI -pip install --pre 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 - - -async def main() -> None: - async with AsyncSupermemory( - api_key=os.environ.get("SUPERMEMORY_API_KEY"), # This is the default and can be omitted - http_client=DefaultAioHttpClient(), - ) as client: - response = await client.memories.add( - content="This is a detailed article about machine learning concepts...", - ) - print(response.id) - - -asyncio.run(main()) -``` - ## Using types Nested request parameters are [TypedDicts](https://docs.python.org/3/library/typing.html#typing.TypedDict). Responses are [Pydantic models](https://docs.pydantic.dev) which also provide helper methods for things like: diff --git a/pyproject.toml b/pyproject.toml index 8f895d43..2608b936 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -37,8 +37,6 @@ classifiers = [ Homepage = "https://github.com/supermemoryai/python-sdk" Repository = "https://github.com/supermemoryai/python-sdk" -[project.optional-dependencies] -aiohttp = ["aiohttp", "httpx_aiohttp>=0.1.6"] [tool.rye] managed = true diff --git a/requirements-dev.lock b/requirements-dev.lock index c63e536d..14d829b6 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -10,13 +10,6 @@ # universal: false -e file:. -aiohappyeyeballs==2.6.1 - # via aiohttp -aiohttp==3.12.8 - # via httpx-aiohttp - # via supermemory -aiosignal==1.3.2 - # via aiohttp annotated-types==0.6.0 # via pydantic anyio==4.4.0 @@ -24,10 +17,6 @@ anyio==4.4.0 # via supermemory argcomplete==3.1.2 # via nox -async-timeout==5.0.1 - # via aiohttp -attrs==25.3.0 - # via aiohttp certifi==2023.7.22 # via httpcore # via httpx @@ -45,23 +34,16 @@ execnet==2.1.1 # via pytest-xdist filelock==3.12.4 # via virtualenv -frozenlist==1.6.2 - # via aiohttp - # via aiosignal h11==0.14.0 # via httpcore httpcore==1.0.2 # via httpx httpx==0.28.1 - # via httpx-aiohttp # via respx # via supermemory -httpx-aiohttp==0.1.6 - # via supermemory idna==3.4 # via anyio # via httpx - # via yarl importlib-metadata==7.0.0 iniconfig==2.0.0 # via pytest @@ -69,9 +51,6 @@ markdown-it-py==3.0.0 # via rich mdurl==0.1.2 # via markdown-it-py -multidict==6.4.4 - # via aiohttp - # via yarl mypy==1.14.1 mypy-extensions==1.0.0 # via mypy @@ -86,9 +65,6 @@ platformdirs==3.11.0 # via virtualenv pluggy==1.5.0 # via pytest -propcache==0.3.1 - # via aiohttp - # via yarl pydantic==2.10.3 # via supermemory pydantic-core==2.27.1 @@ -121,7 +97,6 @@ tomli==2.0.2 # via pytest typing-extensions==4.12.2 # via anyio - # via multidict # via mypy # via pydantic # via pydantic-core @@ -129,7 +104,5 @@ typing-extensions==4.12.2 # via supermemory virtualenv==20.24.5 # via nox -yarl==1.20.0 - # via aiohttp zipp==3.17.0 # via importlib-metadata diff --git a/requirements.lock b/requirements.lock index c54f68b1..c3540f77 100644 --- a/requirements.lock +++ b/requirements.lock @@ -10,22 +10,11 @@ # universal: false -e file:. -aiohappyeyeballs==2.6.1 - # via aiohttp -aiohttp==3.12.8 - # via httpx-aiohttp - # via supermemory -aiosignal==1.3.2 - # via aiohttp annotated-types==0.6.0 # via pydantic anyio==4.4.0 # via httpx # via supermemory -async-timeout==5.0.1 - # via aiohttp -attrs==25.3.0 - # via aiohttp certifi==2023.7.22 # via httpcore # via httpx @@ -33,28 +22,15 @@ distro==1.8.0 # via supermemory exceptiongroup==1.2.2 # via anyio -frozenlist==1.6.2 - # via aiohttp - # via aiosignal h11==0.14.0 # via httpcore httpcore==1.0.2 # via httpx httpx==0.28.1 - # via httpx-aiohttp - # via supermemory -httpx-aiohttp==0.1.6 # via supermemory idna==3.4 # via anyio # via httpx - # via yarl -multidict==6.4.4 - # via aiohttp - # via yarl -propcache==0.3.1 - # via aiohttp - # via yarl pydantic==2.10.3 # via supermemory pydantic-core==2.27.1 @@ -64,9 +40,6 @@ sniffio==1.3.0 # via supermemory typing-extensions==4.12.2 # via anyio - # via multidict # via pydantic # via pydantic-core # via supermemory -yarl==1.20.0 - # via aiohttp diff --git a/src/supermemory/__init__.py b/src/supermemory/__init__.py index bb87a0cf..3cc510e4 100644 --- a/src/supermemory/__init__.py +++ b/src/supermemory/__init__.py @@ -36,7 +36,7 @@ UnprocessableEntityError, APIResponseValidationError, ) -from ._base_client import DefaultHttpxClient, DefaultAioHttpClient, DefaultAsyncHttpxClient +from ._base_client import DefaultHttpxClient, DefaultAsyncHttpxClient from ._utils._logs import setup_logging as _setup_logging __all__ = [ @@ -78,7 +78,6 @@ "DEFAULT_CONNECTION_LIMITS", "DefaultHttpxClient", "DefaultAsyncHttpxClient", - "DefaultAioHttpClient", ] if not _t.TYPE_CHECKING: diff --git a/src/supermemory/_base_client.py b/src/supermemory/_base_client.py index 9a46a403..01d05a51 100644 --- a/src/supermemory/_base_client.py +++ b/src/supermemory/_base_client.py @@ -1289,24 +1289,6 @@ def __init__(self, **kwargs: Any) -> None: super().__init__(**kwargs) -try: - import httpx_aiohttp -except ImportError: - - class _DefaultAioHttpClient(httpx.AsyncClient): - def __init__(self, **_kwargs: Any) -> None: - raise RuntimeError("To use the aiohttp client you must have installed the package with the `aiohttp` extra") -else: - - class _DefaultAioHttpClient(httpx_aiohttp.HttpxAiohttpClient): # type: ignore - def __init__(self, **kwargs: Any) -> None: - kwargs.setdefault("timeout", DEFAULT_TIMEOUT) - kwargs.setdefault("limits", DEFAULT_CONNECTION_LIMITS) - kwargs.setdefault("follow_redirects", True) - - super().__init__(**kwargs) - - if TYPE_CHECKING: DefaultAsyncHttpxClient = httpx.AsyncClient """An alias to `httpx.AsyncClient` that provides the same defaults that this SDK @@ -1315,12 +1297,8 @@ def __init__(self, **kwargs: Any) -> None: This is useful because overriding the `http_client` with your own instance of `httpx.AsyncClient` will result in httpx's defaults being used, not ours. """ - - DefaultAioHttpClient = httpx.AsyncClient - """An alias to `httpx.AsyncClient` that changes the default HTTP transport to `aiohttp`.""" else: DefaultAsyncHttpxClient = _DefaultAsyncHttpxClient - DefaultAioHttpClient = _DefaultAioHttpClient class AsyncHttpxClientWrapper(DefaultAsyncHttpxClient): diff --git a/tests/api_resources/test_connections.py b/tests/api_resources/test_connections.py index bb4ed3e0..18492519 100644 --- a/tests/api_resources/test_connections.py +++ b/tests/api_resources/test_connections.py @@ -107,9 +107,7 @@ def test_path_params_get(self, client: Supermemory) -> None: class TestAsyncConnections: - parametrize = pytest.mark.parametrize( - "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] - ) + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) @pytest.mark.skip() @parametrize diff --git a/tests/api_resources/test_memories.py b/tests/api_resources/test_memories.py index c398ab8f..6336b998 100644 --- a/tests/api_resources/test_memories.py +++ b/tests/api_resources/test_memories.py @@ -224,9 +224,7 @@ def test_path_params_get(self, client: Supermemory) -> None: class TestAsyncMemories: - parametrize = pytest.mark.parametrize( - "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] - ) + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) @pytest.mark.skip() @parametrize diff --git a/tests/api_resources/test_settings.py b/tests/api_resources/test_settings.py index ac790b15..652e9ad6 100644 --- a/tests/api_resources/test_settings.py +++ b/tests/api_resources/test_settings.py @@ -96,9 +96,7 @@ def test_streaming_response_get(self, client: Supermemory) -> None: class TestAsyncSettings: - parametrize = pytest.mark.parametrize( - "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] - ) + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) @pytest.mark.skip() @parametrize diff --git a/tests/conftest.py b/tests/conftest.py index 2baaab81..d6ce7847 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -6,12 +6,10 @@ import logging from typing import TYPE_CHECKING, Iterator, AsyncIterator -import httpx import pytest from pytest_asyncio import is_async_test -from supermemory import Supermemory, AsyncSupermemory, DefaultAioHttpClient -from supermemory._utils import is_dict +from supermemory import Supermemory, AsyncSupermemory if TYPE_CHECKING: from _pytest.fixtures import FixtureRequest # pyright: ignore[reportPrivateImportUsage] @@ -29,19 +27,6 @@ def pytest_collection_modifyitems(items: list[pytest.Function]) -> None: for async_test in pytest_asyncio_tests: async_test.add_marker(session_scope_marker, append=False) - # We skip tests that use both the aiohttp client and respx_mock as respx_mock - # doesn't support custom transports. - for item in items: - if "async_client" not in item.fixturenames or "respx_mock" not in item.fixturenames: - continue - - if not hasattr(item, "callspec"): - continue - - async_client_param = item.callspec.params.get("async_client") - if is_dict(async_client_param) and async_client_param.get("http_client") == "aiohttp": - item.add_marker(pytest.mark.skip(reason="aiohttp client is not compatible with respx_mock")) - base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -60,25 +45,9 @@ def client(request: FixtureRequest) -> Iterator[Supermemory]: @pytest.fixture(scope="session") async def async_client(request: FixtureRequest) -> AsyncIterator[AsyncSupermemory]: - param = getattr(request, "param", True) - - # defaults - strict = True - http_client: None | httpx.AsyncClient = None - - if isinstance(param, bool): - strict = param - elif is_dict(param): - strict = param.get("strict", True) - assert isinstance(strict, bool) - - http_client_type = param.get("http_client", "httpx") - if http_client_type == "aiohttp": - http_client = DefaultAioHttpClient() - else: - raise TypeError(f"Unexpected fixture parameter type {type(param)}, expected bool or dict") - - async with AsyncSupermemory( - base_url=base_url, api_key=api_key, _strict_response_validation=strict, http_client=http_client - ) as client: + strict = getattr(request, "param", True) + if not isinstance(strict, bool): + raise TypeError(f"Unexpected fixture parameter type {type(strict)}, expected {bool}") + + async with AsyncSupermemory(base_url=base_url, api_key=api_key, _strict_response_validation=strict) as client: yield client From c4039a7d5124d2c6719508830bf8067ebbb4fd58 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 21 Jun 2025 04:45:43 +0000 Subject: [PATCH 43/48] chore(internal): codegen related update --- README.md | 34 +++++++++++++++++++ pyproject.toml | 2 ++ requirements-dev.lock | 27 ++++++++++++++++ requirements.lock | 27 ++++++++++++++++ src/supermemory/__init__.py | 3 +- src/supermemory/_base_client.py | 22 +++++++++++++ tests/api_resources/test_connections.py | 4 ++- tests/api_resources/test_memories.py | 4 ++- tests/api_resources/test_settings.py | 4 ++- tests/conftest.py | 43 +++++++++++++++++++++---- 10 files changed, 160 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 2957f876..8557c60b 100644 --- a/README.md +++ b/README.md @@ -68,6 +68,40 @@ asyncio.run(main()) Functionality between the synchronous and asynchronous clients is otherwise identical. +### With aiohttp + +By default, the async client uses `httpx` for HTTP requests. However, for improved concurrency performance you may also use `aiohttp` as the HTTP backend. + +You can enable this by installing `aiohttp`: + +```sh +# install from PyPI +pip install --pre 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 + + +async def main() -> None: + async with AsyncSupermemory( + api_key=os.environ.get("SUPERMEMORY_API_KEY"), # This is the default and can be omitted + http_client=DefaultAioHttpClient(), + ) as client: + response = await client.memories.add( + content="This is a detailed article about machine learning concepts...", + ) + print(response.id) + + +asyncio.run(main()) +``` + ## Using types Nested request parameters are [TypedDicts](https://docs.python.org/3/library/typing.html#typing.TypedDict). Responses are [Pydantic models](https://docs.pydantic.dev) which also provide helper methods for things like: diff --git a/pyproject.toml b/pyproject.toml index 2608b936..8f895d43 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -37,6 +37,8 @@ classifiers = [ Homepage = "https://github.com/supermemoryai/python-sdk" Repository = "https://github.com/supermemoryai/python-sdk" +[project.optional-dependencies] +aiohttp = ["aiohttp", "httpx_aiohttp>=0.1.6"] [tool.rye] managed = true diff --git a/requirements-dev.lock b/requirements-dev.lock index 14d829b6..c63e536d 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -10,6 +10,13 @@ # universal: false -e file:. +aiohappyeyeballs==2.6.1 + # via aiohttp +aiohttp==3.12.8 + # via httpx-aiohttp + # via supermemory +aiosignal==1.3.2 + # via aiohttp annotated-types==0.6.0 # via pydantic anyio==4.4.0 @@ -17,6 +24,10 @@ anyio==4.4.0 # via supermemory argcomplete==3.1.2 # via nox +async-timeout==5.0.1 + # via aiohttp +attrs==25.3.0 + # via aiohttp certifi==2023.7.22 # via httpcore # via httpx @@ -34,16 +45,23 @@ execnet==2.1.1 # via pytest-xdist filelock==3.12.4 # via virtualenv +frozenlist==1.6.2 + # via aiohttp + # via aiosignal h11==0.14.0 # via httpcore httpcore==1.0.2 # via httpx httpx==0.28.1 + # via httpx-aiohttp # via respx # via supermemory +httpx-aiohttp==0.1.6 + # via supermemory idna==3.4 # via anyio # via httpx + # via yarl importlib-metadata==7.0.0 iniconfig==2.0.0 # via pytest @@ -51,6 +69,9 @@ markdown-it-py==3.0.0 # via rich mdurl==0.1.2 # via markdown-it-py +multidict==6.4.4 + # via aiohttp + # via yarl mypy==1.14.1 mypy-extensions==1.0.0 # via mypy @@ -65,6 +86,9 @@ platformdirs==3.11.0 # via virtualenv pluggy==1.5.0 # via pytest +propcache==0.3.1 + # via aiohttp + # via yarl pydantic==2.10.3 # via supermemory pydantic-core==2.27.1 @@ -97,6 +121,7 @@ tomli==2.0.2 # via pytest typing-extensions==4.12.2 # via anyio + # via multidict # via mypy # via pydantic # via pydantic-core @@ -104,5 +129,7 @@ typing-extensions==4.12.2 # via supermemory virtualenv==20.24.5 # via nox +yarl==1.20.0 + # via aiohttp zipp==3.17.0 # via importlib-metadata diff --git a/requirements.lock b/requirements.lock index c3540f77..c54f68b1 100644 --- a/requirements.lock +++ b/requirements.lock @@ -10,11 +10,22 @@ # universal: false -e file:. +aiohappyeyeballs==2.6.1 + # via aiohttp +aiohttp==3.12.8 + # via httpx-aiohttp + # via supermemory +aiosignal==1.3.2 + # via aiohttp annotated-types==0.6.0 # via pydantic anyio==4.4.0 # via httpx # via supermemory +async-timeout==5.0.1 + # via aiohttp +attrs==25.3.0 + # via aiohttp certifi==2023.7.22 # via httpcore # via httpx @@ -22,15 +33,28 @@ distro==1.8.0 # via supermemory exceptiongroup==1.2.2 # via anyio +frozenlist==1.6.2 + # via aiohttp + # via aiosignal h11==0.14.0 # via httpcore httpcore==1.0.2 # via httpx httpx==0.28.1 + # via httpx-aiohttp + # via supermemory +httpx-aiohttp==0.1.6 # via supermemory idna==3.4 # via anyio # via httpx + # via yarl +multidict==6.4.4 + # via aiohttp + # via yarl +propcache==0.3.1 + # via aiohttp + # via yarl pydantic==2.10.3 # via supermemory pydantic-core==2.27.1 @@ -40,6 +64,9 @@ sniffio==1.3.0 # via supermemory typing-extensions==4.12.2 # via anyio + # via multidict # via pydantic # via pydantic-core # via supermemory +yarl==1.20.0 + # via aiohttp diff --git a/src/supermemory/__init__.py b/src/supermemory/__init__.py index 3cc510e4..bb87a0cf 100644 --- a/src/supermemory/__init__.py +++ b/src/supermemory/__init__.py @@ -36,7 +36,7 @@ UnprocessableEntityError, APIResponseValidationError, ) -from ._base_client import DefaultHttpxClient, DefaultAsyncHttpxClient +from ._base_client import DefaultHttpxClient, DefaultAioHttpClient, DefaultAsyncHttpxClient from ._utils._logs import setup_logging as _setup_logging __all__ = [ @@ -78,6 +78,7 @@ "DEFAULT_CONNECTION_LIMITS", "DefaultHttpxClient", "DefaultAsyncHttpxClient", + "DefaultAioHttpClient", ] if not _t.TYPE_CHECKING: diff --git a/src/supermemory/_base_client.py b/src/supermemory/_base_client.py index 01d05a51..9a46a403 100644 --- a/src/supermemory/_base_client.py +++ b/src/supermemory/_base_client.py @@ -1289,6 +1289,24 @@ def __init__(self, **kwargs: Any) -> None: super().__init__(**kwargs) +try: + import httpx_aiohttp +except ImportError: + + class _DefaultAioHttpClient(httpx.AsyncClient): + def __init__(self, **_kwargs: Any) -> None: + raise RuntimeError("To use the aiohttp client you must have installed the package with the `aiohttp` extra") +else: + + class _DefaultAioHttpClient(httpx_aiohttp.HttpxAiohttpClient): # type: ignore + def __init__(self, **kwargs: Any) -> None: + kwargs.setdefault("timeout", DEFAULT_TIMEOUT) + kwargs.setdefault("limits", DEFAULT_CONNECTION_LIMITS) + kwargs.setdefault("follow_redirects", True) + + super().__init__(**kwargs) + + if TYPE_CHECKING: DefaultAsyncHttpxClient = httpx.AsyncClient """An alias to `httpx.AsyncClient` that provides the same defaults that this SDK @@ -1297,8 +1315,12 @@ def __init__(self, **kwargs: Any) -> None: This is useful because overriding the `http_client` with your own instance of `httpx.AsyncClient` will result in httpx's defaults being used, not ours. """ + + DefaultAioHttpClient = httpx.AsyncClient + """An alias to `httpx.AsyncClient` that changes the default HTTP transport to `aiohttp`.""" else: DefaultAsyncHttpxClient = _DefaultAsyncHttpxClient + DefaultAioHttpClient = _DefaultAioHttpClient class AsyncHttpxClientWrapper(DefaultAsyncHttpxClient): diff --git a/tests/api_resources/test_connections.py b/tests/api_resources/test_connections.py index 18492519..bb4ed3e0 100644 --- a/tests/api_resources/test_connections.py +++ b/tests/api_resources/test_connections.py @@ -107,7 +107,9 @@ def test_path_params_get(self, client: Supermemory) -> None: class TestAsyncConnections: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @pytest.mark.skip() @parametrize diff --git a/tests/api_resources/test_memories.py b/tests/api_resources/test_memories.py index 6336b998..c398ab8f 100644 --- a/tests/api_resources/test_memories.py +++ b/tests/api_resources/test_memories.py @@ -224,7 +224,9 @@ def test_path_params_get(self, client: Supermemory) -> None: class TestAsyncMemories: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @pytest.mark.skip() @parametrize diff --git a/tests/api_resources/test_settings.py b/tests/api_resources/test_settings.py index 652e9ad6..ac790b15 100644 --- a/tests/api_resources/test_settings.py +++ b/tests/api_resources/test_settings.py @@ -96,7 +96,9 @@ def test_streaming_response_get(self, client: Supermemory) -> None: class TestAsyncSettings: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @pytest.mark.skip() @parametrize diff --git a/tests/conftest.py b/tests/conftest.py index d6ce7847..2baaab81 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -6,10 +6,12 @@ import logging from typing import TYPE_CHECKING, Iterator, AsyncIterator +import httpx import pytest from pytest_asyncio import is_async_test -from supermemory import Supermemory, AsyncSupermemory +from supermemory import Supermemory, AsyncSupermemory, DefaultAioHttpClient +from supermemory._utils import is_dict if TYPE_CHECKING: from _pytest.fixtures import FixtureRequest # pyright: ignore[reportPrivateImportUsage] @@ -27,6 +29,19 @@ def pytest_collection_modifyitems(items: list[pytest.Function]) -> None: for async_test in pytest_asyncio_tests: async_test.add_marker(session_scope_marker, append=False) + # We skip tests that use both the aiohttp client and respx_mock as respx_mock + # doesn't support custom transports. + for item in items: + if "async_client" not in item.fixturenames or "respx_mock" not in item.fixturenames: + continue + + if not hasattr(item, "callspec"): + continue + + async_client_param = item.callspec.params.get("async_client") + if is_dict(async_client_param) and async_client_param.get("http_client") == "aiohttp": + item.add_marker(pytest.mark.skip(reason="aiohttp client is not compatible with respx_mock")) + base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -45,9 +60,25 @@ def client(request: FixtureRequest) -> Iterator[Supermemory]: @pytest.fixture(scope="session") async def async_client(request: FixtureRequest) -> AsyncIterator[AsyncSupermemory]: - strict = getattr(request, "param", True) - if not isinstance(strict, bool): - raise TypeError(f"Unexpected fixture parameter type {type(strict)}, expected {bool}") - - async with AsyncSupermemory(base_url=base_url, api_key=api_key, _strict_response_validation=strict) as client: + param = getattr(request, "param", True) + + # defaults + strict = True + http_client: None | httpx.AsyncClient = None + + if isinstance(param, bool): + strict = param + elif is_dict(param): + strict = param.get("strict", True) + assert isinstance(strict, bool) + + http_client_type = param.get("http_client", "httpx") + if http_client_type == "aiohttp": + http_client = DefaultAioHttpClient() + else: + raise TypeError(f"Unexpected fixture parameter type {type(param)}, expected bool or dict") + + async with AsyncSupermemory( + base_url=base_url, api_key=api_key, _strict_response_validation=strict, http_client=http_client + ) as client: yield client From 75c22e3f2db8eda857ca184f5bd531ff736a1c8c Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 23 Jun 2025 00:17:29 +0000 Subject: [PATCH 44/48] feat(api): api update --- .stats.yml | 4 +-- README.md | 34 ------------------- pyproject.toml | 2 -- requirements-dev.lock | 27 ---------------- requirements.lock | 27 ---------------- src/supermemory/__init__.py | 3 +- src/supermemory/_base_client.py | 22 ------------- tests/api_resources/test_connections.py | 4 +-- tests/api_resources/test_memories.py | 4 +-- tests/api_resources/test_settings.py | 4 +-- tests/conftest.py | 43 ++++--------------------- 11 files changed, 12 insertions(+), 162 deletions(-) diff --git a/.stats.yml b/.stats.yml index 8f056938..1c2cf690 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 8 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/supermemory--inc%2Fsupermemory-4c45e387cbdc7c80d75cdb8eb924cf92a3a48a0c10060fda917b83a7e454aef5.yml -openapi_spec_hash: c859ac2e3429ad3663337b99c722f317 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/supermemory--inc%2Fsupermemory-8982239645e54c33bfd16c9172a5b651fc810b3491127fa3b0197315acd912f0.yml +openapi_spec_hash: 3f1143321f751dd14c9a835e686de76b config_hash: 8477e3ee6fd596ab6ac911d052e4de79 diff --git a/README.md b/README.md index 8557c60b..2957f876 100644 --- a/README.md +++ b/README.md @@ -68,40 +68,6 @@ asyncio.run(main()) Functionality between the synchronous and asynchronous clients is otherwise identical. -### With aiohttp - -By default, the async client uses `httpx` for HTTP requests. However, for improved concurrency performance you may also use `aiohttp` as the HTTP backend. - -You can enable this by installing `aiohttp`: - -```sh -# install from PyPI -pip install --pre 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 - - -async def main() -> None: - async with AsyncSupermemory( - api_key=os.environ.get("SUPERMEMORY_API_KEY"), # This is the default and can be omitted - http_client=DefaultAioHttpClient(), - ) as client: - response = await client.memories.add( - content="This is a detailed article about machine learning concepts...", - ) - print(response.id) - - -asyncio.run(main()) -``` - ## Using types Nested request parameters are [TypedDicts](https://docs.python.org/3/library/typing.html#typing.TypedDict). Responses are [Pydantic models](https://docs.pydantic.dev) which also provide helper methods for things like: diff --git a/pyproject.toml b/pyproject.toml index 8f895d43..2608b936 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -37,8 +37,6 @@ classifiers = [ Homepage = "https://github.com/supermemoryai/python-sdk" Repository = "https://github.com/supermemoryai/python-sdk" -[project.optional-dependencies] -aiohttp = ["aiohttp", "httpx_aiohttp>=0.1.6"] [tool.rye] managed = true diff --git a/requirements-dev.lock b/requirements-dev.lock index c63e536d..14d829b6 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -10,13 +10,6 @@ # universal: false -e file:. -aiohappyeyeballs==2.6.1 - # via aiohttp -aiohttp==3.12.8 - # via httpx-aiohttp - # via supermemory -aiosignal==1.3.2 - # via aiohttp annotated-types==0.6.0 # via pydantic anyio==4.4.0 @@ -24,10 +17,6 @@ anyio==4.4.0 # via supermemory argcomplete==3.1.2 # via nox -async-timeout==5.0.1 - # via aiohttp -attrs==25.3.0 - # via aiohttp certifi==2023.7.22 # via httpcore # via httpx @@ -45,23 +34,16 @@ execnet==2.1.1 # via pytest-xdist filelock==3.12.4 # via virtualenv -frozenlist==1.6.2 - # via aiohttp - # via aiosignal h11==0.14.0 # via httpcore httpcore==1.0.2 # via httpx httpx==0.28.1 - # via httpx-aiohttp # via respx # via supermemory -httpx-aiohttp==0.1.6 - # via supermemory idna==3.4 # via anyio # via httpx - # via yarl importlib-metadata==7.0.0 iniconfig==2.0.0 # via pytest @@ -69,9 +51,6 @@ markdown-it-py==3.0.0 # via rich mdurl==0.1.2 # via markdown-it-py -multidict==6.4.4 - # via aiohttp - # via yarl mypy==1.14.1 mypy-extensions==1.0.0 # via mypy @@ -86,9 +65,6 @@ platformdirs==3.11.0 # via virtualenv pluggy==1.5.0 # via pytest -propcache==0.3.1 - # via aiohttp - # via yarl pydantic==2.10.3 # via supermemory pydantic-core==2.27.1 @@ -121,7 +97,6 @@ tomli==2.0.2 # via pytest typing-extensions==4.12.2 # via anyio - # via multidict # via mypy # via pydantic # via pydantic-core @@ -129,7 +104,5 @@ typing-extensions==4.12.2 # via supermemory virtualenv==20.24.5 # via nox -yarl==1.20.0 - # via aiohttp zipp==3.17.0 # via importlib-metadata diff --git a/requirements.lock b/requirements.lock index c54f68b1..c3540f77 100644 --- a/requirements.lock +++ b/requirements.lock @@ -10,22 +10,11 @@ # universal: false -e file:. -aiohappyeyeballs==2.6.1 - # via aiohttp -aiohttp==3.12.8 - # via httpx-aiohttp - # via supermemory -aiosignal==1.3.2 - # via aiohttp annotated-types==0.6.0 # via pydantic anyio==4.4.0 # via httpx # via supermemory -async-timeout==5.0.1 - # via aiohttp -attrs==25.3.0 - # via aiohttp certifi==2023.7.22 # via httpcore # via httpx @@ -33,28 +22,15 @@ distro==1.8.0 # via supermemory exceptiongroup==1.2.2 # via anyio -frozenlist==1.6.2 - # via aiohttp - # via aiosignal h11==0.14.0 # via httpcore httpcore==1.0.2 # via httpx httpx==0.28.1 - # via httpx-aiohttp - # via supermemory -httpx-aiohttp==0.1.6 # via supermemory idna==3.4 # via anyio # via httpx - # via yarl -multidict==6.4.4 - # via aiohttp - # via yarl -propcache==0.3.1 - # via aiohttp - # via yarl pydantic==2.10.3 # via supermemory pydantic-core==2.27.1 @@ -64,9 +40,6 @@ sniffio==1.3.0 # via supermemory typing-extensions==4.12.2 # via anyio - # via multidict # via pydantic # via pydantic-core # via supermemory -yarl==1.20.0 - # via aiohttp diff --git a/src/supermemory/__init__.py b/src/supermemory/__init__.py index bb87a0cf..3cc510e4 100644 --- a/src/supermemory/__init__.py +++ b/src/supermemory/__init__.py @@ -36,7 +36,7 @@ UnprocessableEntityError, APIResponseValidationError, ) -from ._base_client import DefaultHttpxClient, DefaultAioHttpClient, DefaultAsyncHttpxClient +from ._base_client import DefaultHttpxClient, DefaultAsyncHttpxClient from ._utils._logs import setup_logging as _setup_logging __all__ = [ @@ -78,7 +78,6 @@ "DEFAULT_CONNECTION_LIMITS", "DefaultHttpxClient", "DefaultAsyncHttpxClient", - "DefaultAioHttpClient", ] if not _t.TYPE_CHECKING: diff --git a/src/supermemory/_base_client.py b/src/supermemory/_base_client.py index 9a46a403..01d05a51 100644 --- a/src/supermemory/_base_client.py +++ b/src/supermemory/_base_client.py @@ -1289,24 +1289,6 @@ def __init__(self, **kwargs: Any) -> None: super().__init__(**kwargs) -try: - import httpx_aiohttp -except ImportError: - - class _DefaultAioHttpClient(httpx.AsyncClient): - def __init__(self, **_kwargs: Any) -> None: - raise RuntimeError("To use the aiohttp client you must have installed the package with the `aiohttp` extra") -else: - - class _DefaultAioHttpClient(httpx_aiohttp.HttpxAiohttpClient): # type: ignore - def __init__(self, **kwargs: Any) -> None: - kwargs.setdefault("timeout", DEFAULT_TIMEOUT) - kwargs.setdefault("limits", DEFAULT_CONNECTION_LIMITS) - kwargs.setdefault("follow_redirects", True) - - super().__init__(**kwargs) - - if TYPE_CHECKING: DefaultAsyncHttpxClient = httpx.AsyncClient """An alias to `httpx.AsyncClient` that provides the same defaults that this SDK @@ -1315,12 +1297,8 @@ def __init__(self, **kwargs: Any) -> None: This is useful because overriding the `http_client` with your own instance of `httpx.AsyncClient` will result in httpx's defaults being used, not ours. """ - - DefaultAioHttpClient = httpx.AsyncClient - """An alias to `httpx.AsyncClient` that changes the default HTTP transport to `aiohttp`.""" else: DefaultAsyncHttpxClient = _DefaultAsyncHttpxClient - DefaultAioHttpClient = _DefaultAioHttpClient class AsyncHttpxClientWrapper(DefaultAsyncHttpxClient): diff --git a/tests/api_resources/test_connections.py b/tests/api_resources/test_connections.py index bb4ed3e0..18492519 100644 --- a/tests/api_resources/test_connections.py +++ b/tests/api_resources/test_connections.py @@ -107,9 +107,7 @@ def test_path_params_get(self, client: Supermemory) -> None: class TestAsyncConnections: - parametrize = pytest.mark.parametrize( - "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] - ) + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) @pytest.mark.skip() @parametrize diff --git a/tests/api_resources/test_memories.py b/tests/api_resources/test_memories.py index c398ab8f..6336b998 100644 --- a/tests/api_resources/test_memories.py +++ b/tests/api_resources/test_memories.py @@ -224,9 +224,7 @@ def test_path_params_get(self, client: Supermemory) -> None: class TestAsyncMemories: - parametrize = pytest.mark.parametrize( - "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] - ) + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) @pytest.mark.skip() @parametrize diff --git a/tests/api_resources/test_settings.py b/tests/api_resources/test_settings.py index ac790b15..652e9ad6 100644 --- a/tests/api_resources/test_settings.py +++ b/tests/api_resources/test_settings.py @@ -96,9 +96,7 @@ def test_streaming_response_get(self, client: Supermemory) -> None: class TestAsyncSettings: - parametrize = pytest.mark.parametrize( - "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] - ) + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) @pytest.mark.skip() @parametrize diff --git a/tests/conftest.py b/tests/conftest.py index 2baaab81..d6ce7847 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -6,12 +6,10 @@ import logging from typing import TYPE_CHECKING, Iterator, AsyncIterator -import httpx import pytest from pytest_asyncio import is_async_test -from supermemory import Supermemory, AsyncSupermemory, DefaultAioHttpClient -from supermemory._utils import is_dict +from supermemory import Supermemory, AsyncSupermemory if TYPE_CHECKING: from _pytest.fixtures import FixtureRequest # pyright: ignore[reportPrivateImportUsage] @@ -29,19 +27,6 @@ def pytest_collection_modifyitems(items: list[pytest.Function]) -> None: for async_test in pytest_asyncio_tests: async_test.add_marker(session_scope_marker, append=False) - # We skip tests that use both the aiohttp client and respx_mock as respx_mock - # doesn't support custom transports. - for item in items: - if "async_client" not in item.fixturenames or "respx_mock" not in item.fixturenames: - continue - - if not hasattr(item, "callspec"): - continue - - async_client_param = item.callspec.params.get("async_client") - if is_dict(async_client_param) and async_client_param.get("http_client") == "aiohttp": - item.add_marker(pytest.mark.skip(reason="aiohttp client is not compatible with respx_mock")) - base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -60,25 +45,9 @@ def client(request: FixtureRequest) -> Iterator[Supermemory]: @pytest.fixture(scope="session") async def async_client(request: FixtureRequest) -> AsyncIterator[AsyncSupermemory]: - param = getattr(request, "param", True) - - # defaults - strict = True - http_client: None | httpx.AsyncClient = None - - if isinstance(param, bool): - strict = param - elif is_dict(param): - strict = param.get("strict", True) - assert isinstance(strict, bool) - - http_client_type = param.get("http_client", "httpx") - if http_client_type == "aiohttp": - http_client = DefaultAioHttpClient() - else: - raise TypeError(f"Unexpected fixture parameter type {type(param)}, expected bool or dict") - - async with AsyncSupermemory( - base_url=base_url, api_key=api_key, _strict_response_validation=strict, http_client=http_client - ) as client: + strict = getattr(request, "param", True) + if not isinstance(strict, bool): + raise TypeError(f"Unexpected fixture parameter type {type(strict)}, expected {bool}") + + async with AsyncSupermemory(base_url=base_url, api_key=api_key, _strict_response_validation=strict) as client: yield client From d792e5fd1c22162e5519bb82e6e913360956ba82 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 23 Jun 2025 01:17:38 +0000 Subject: [PATCH 45/48] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 1c2cf690..8f056938 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 8 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/supermemory--inc%2Fsupermemory-8982239645e54c33bfd16c9172a5b651fc810b3491127fa3b0197315acd912f0.yml -openapi_spec_hash: 3f1143321f751dd14c9a835e686de76b +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/supermemory--inc%2Fsupermemory-4c45e387cbdc7c80d75cdb8eb924cf92a3a48a0c10060fda917b83a7e454aef5.yml +openapi_spec_hash: c859ac2e3429ad3663337b99c722f317 config_hash: 8477e3ee6fd596ab6ac911d052e4de79 From 2b10aa88d82e044d1a7df2bdd266209f4c130c8d Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 24 Jun 2025 02:10:19 +0000 Subject: [PATCH 46/48] chore(internal): codegen related update --- README.md | 34 +++++++++++++++++++ pyproject.toml | 2 ++ requirements-dev.lock | 27 ++++++++++++++++ requirements.lock | 27 ++++++++++++++++ src/supermemory/__init__.py | 3 +- src/supermemory/_base_client.py | 22 +++++++++++++ tests/api_resources/test_connections.py | 4 ++- tests/api_resources/test_memories.py | 4 ++- tests/api_resources/test_settings.py | 4 ++- tests/conftest.py | 43 +++++++++++++++++++++---- 10 files changed, 160 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 2957f876..8557c60b 100644 --- a/README.md +++ b/README.md @@ -68,6 +68,40 @@ asyncio.run(main()) Functionality between the synchronous and asynchronous clients is otherwise identical. +### With aiohttp + +By default, the async client uses `httpx` for HTTP requests. However, for improved concurrency performance you may also use `aiohttp` as the HTTP backend. + +You can enable this by installing `aiohttp`: + +```sh +# install from PyPI +pip install --pre 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 + + +async def main() -> None: + async with AsyncSupermemory( + api_key=os.environ.get("SUPERMEMORY_API_KEY"), # This is the default and can be omitted + http_client=DefaultAioHttpClient(), + ) as client: + response = await client.memories.add( + content="This is a detailed article about machine learning concepts...", + ) + print(response.id) + + +asyncio.run(main()) +``` + ## Using types Nested request parameters are [TypedDicts](https://docs.python.org/3/library/typing.html#typing.TypedDict). Responses are [Pydantic models](https://docs.pydantic.dev) which also provide helper methods for things like: diff --git a/pyproject.toml b/pyproject.toml index 2608b936..8f895d43 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -37,6 +37,8 @@ classifiers = [ Homepage = "https://github.com/supermemoryai/python-sdk" Repository = "https://github.com/supermemoryai/python-sdk" +[project.optional-dependencies] +aiohttp = ["aiohttp", "httpx_aiohttp>=0.1.6"] [tool.rye] managed = true diff --git a/requirements-dev.lock b/requirements-dev.lock index 14d829b6..c63e536d 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -10,6 +10,13 @@ # universal: false -e file:. +aiohappyeyeballs==2.6.1 + # via aiohttp +aiohttp==3.12.8 + # via httpx-aiohttp + # via supermemory +aiosignal==1.3.2 + # via aiohttp annotated-types==0.6.0 # via pydantic anyio==4.4.0 @@ -17,6 +24,10 @@ anyio==4.4.0 # via supermemory argcomplete==3.1.2 # via nox +async-timeout==5.0.1 + # via aiohttp +attrs==25.3.0 + # via aiohttp certifi==2023.7.22 # via httpcore # via httpx @@ -34,16 +45,23 @@ execnet==2.1.1 # via pytest-xdist filelock==3.12.4 # via virtualenv +frozenlist==1.6.2 + # via aiohttp + # via aiosignal h11==0.14.0 # via httpcore httpcore==1.0.2 # via httpx httpx==0.28.1 + # via httpx-aiohttp # via respx # via supermemory +httpx-aiohttp==0.1.6 + # via supermemory idna==3.4 # via anyio # via httpx + # via yarl importlib-metadata==7.0.0 iniconfig==2.0.0 # via pytest @@ -51,6 +69,9 @@ markdown-it-py==3.0.0 # via rich mdurl==0.1.2 # via markdown-it-py +multidict==6.4.4 + # via aiohttp + # via yarl mypy==1.14.1 mypy-extensions==1.0.0 # via mypy @@ -65,6 +86,9 @@ platformdirs==3.11.0 # via virtualenv pluggy==1.5.0 # via pytest +propcache==0.3.1 + # via aiohttp + # via yarl pydantic==2.10.3 # via supermemory pydantic-core==2.27.1 @@ -97,6 +121,7 @@ tomli==2.0.2 # via pytest typing-extensions==4.12.2 # via anyio + # via multidict # via mypy # via pydantic # via pydantic-core @@ -104,5 +129,7 @@ typing-extensions==4.12.2 # via supermemory virtualenv==20.24.5 # via nox +yarl==1.20.0 + # via aiohttp zipp==3.17.0 # via importlib-metadata diff --git a/requirements.lock b/requirements.lock index c3540f77..c54f68b1 100644 --- a/requirements.lock +++ b/requirements.lock @@ -10,11 +10,22 @@ # universal: false -e file:. +aiohappyeyeballs==2.6.1 + # via aiohttp +aiohttp==3.12.8 + # via httpx-aiohttp + # via supermemory +aiosignal==1.3.2 + # via aiohttp annotated-types==0.6.0 # via pydantic anyio==4.4.0 # via httpx # via supermemory +async-timeout==5.0.1 + # via aiohttp +attrs==25.3.0 + # via aiohttp certifi==2023.7.22 # via httpcore # via httpx @@ -22,15 +33,28 @@ distro==1.8.0 # via supermemory exceptiongroup==1.2.2 # via anyio +frozenlist==1.6.2 + # via aiohttp + # via aiosignal h11==0.14.0 # via httpcore httpcore==1.0.2 # via httpx httpx==0.28.1 + # via httpx-aiohttp + # via supermemory +httpx-aiohttp==0.1.6 # via supermemory idna==3.4 # via anyio # via httpx + # via yarl +multidict==6.4.4 + # via aiohttp + # via yarl +propcache==0.3.1 + # via aiohttp + # via yarl pydantic==2.10.3 # via supermemory pydantic-core==2.27.1 @@ -40,6 +64,9 @@ sniffio==1.3.0 # via supermemory typing-extensions==4.12.2 # via anyio + # via multidict # via pydantic # via pydantic-core # via supermemory +yarl==1.20.0 + # via aiohttp diff --git a/src/supermemory/__init__.py b/src/supermemory/__init__.py index 3cc510e4..bb87a0cf 100644 --- a/src/supermemory/__init__.py +++ b/src/supermemory/__init__.py @@ -36,7 +36,7 @@ UnprocessableEntityError, APIResponseValidationError, ) -from ._base_client import DefaultHttpxClient, DefaultAsyncHttpxClient +from ._base_client import DefaultHttpxClient, DefaultAioHttpClient, DefaultAsyncHttpxClient from ._utils._logs import setup_logging as _setup_logging __all__ = [ @@ -78,6 +78,7 @@ "DEFAULT_CONNECTION_LIMITS", "DefaultHttpxClient", "DefaultAsyncHttpxClient", + "DefaultAioHttpClient", ] if not _t.TYPE_CHECKING: diff --git a/src/supermemory/_base_client.py b/src/supermemory/_base_client.py index 01d05a51..9a46a403 100644 --- a/src/supermemory/_base_client.py +++ b/src/supermemory/_base_client.py @@ -1289,6 +1289,24 @@ def __init__(self, **kwargs: Any) -> None: super().__init__(**kwargs) +try: + import httpx_aiohttp +except ImportError: + + class _DefaultAioHttpClient(httpx.AsyncClient): + def __init__(self, **_kwargs: Any) -> None: + raise RuntimeError("To use the aiohttp client you must have installed the package with the `aiohttp` extra") +else: + + class _DefaultAioHttpClient(httpx_aiohttp.HttpxAiohttpClient): # type: ignore + def __init__(self, **kwargs: Any) -> None: + kwargs.setdefault("timeout", DEFAULT_TIMEOUT) + kwargs.setdefault("limits", DEFAULT_CONNECTION_LIMITS) + kwargs.setdefault("follow_redirects", True) + + super().__init__(**kwargs) + + if TYPE_CHECKING: DefaultAsyncHttpxClient = httpx.AsyncClient """An alias to `httpx.AsyncClient` that provides the same defaults that this SDK @@ -1297,8 +1315,12 @@ def __init__(self, **kwargs: Any) -> None: This is useful because overriding the `http_client` with your own instance of `httpx.AsyncClient` will result in httpx's defaults being used, not ours. """ + + DefaultAioHttpClient = httpx.AsyncClient + """An alias to `httpx.AsyncClient` that changes the default HTTP transport to `aiohttp`.""" else: DefaultAsyncHttpxClient = _DefaultAsyncHttpxClient + DefaultAioHttpClient = _DefaultAioHttpClient class AsyncHttpxClientWrapper(DefaultAsyncHttpxClient): diff --git a/tests/api_resources/test_connections.py b/tests/api_resources/test_connections.py index 18492519..bb4ed3e0 100644 --- a/tests/api_resources/test_connections.py +++ b/tests/api_resources/test_connections.py @@ -107,7 +107,9 @@ def test_path_params_get(self, client: Supermemory) -> None: class TestAsyncConnections: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @pytest.mark.skip() @parametrize diff --git a/tests/api_resources/test_memories.py b/tests/api_resources/test_memories.py index 6336b998..c398ab8f 100644 --- a/tests/api_resources/test_memories.py +++ b/tests/api_resources/test_memories.py @@ -224,7 +224,9 @@ def test_path_params_get(self, client: Supermemory) -> None: class TestAsyncMemories: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @pytest.mark.skip() @parametrize diff --git a/tests/api_resources/test_settings.py b/tests/api_resources/test_settings.py index 652e9ad6..ac790b15 100644 --- a/tests/api_resources/test_settings.py +++ b/tests/api_resources/test_settings.py @@ -96,7 +96,9 @@ def test_streaming_response_get(self, client: Supermemory) -> None: class TestAsyncSettings: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) @pytest.mark.skip() @parametrize diff --git a/tests/conftest.py b/tests/conftest.py index d6ce7847..2baaab81 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -6,10 +6,12 @@ import logging from typing import TYPE_CHECKING, Iterator, AsyncIterator +import httpx import pytest from pytest_asyncio import is_async_test -from supermemory import Supermemory, AsyncSupermemory +from supermemory import Supermemory, AsyncSupermemory, DefaultAioHttpClient +from supermemory._utils import is_dict if TYPE_CHECKING: from _pytest.fixtures import FixtureRequest # pyright: ignore[reportPrivateImportUsage] @@ -27,6 +29,19 @@ def pytest_collection_modifyitems(items: list[pytest.Function]) -> None: for async_test in pytest_asyncio_tests: async_test.add_marker(session_scope_marker, append=False) + # We skip tests that use both the aiohttp client and respx_mock as respx_mock + # doesn't support custom transports. + for item in items: + if "async_client" not in item.fixturenames or "respx_mock" not in item.fixturenames: + continue + + if not hasattr(item, "callspec"): + continue + + async_client_param = item.callspec.params.get("async_client") + if is_dict(async_client_param) and async_client_param.get("http_client") == "aiohttp": + item.add_marker(pytest.mark.skip(reason="aiohttp client is not compatible with respx_mock")) + base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -45,9 +60,25 @@ def client(request: FixtureRequest) -> Iterator[Supermemory]: @pytest.fixture(scope="session") async def async_client(request: FixtureRequest) -> AsyncIterator[AsyncSupermemory]: - strict = getattr(request, "param", True) - if not isinstance(strict, bool): - raise TypeError(f"Unexpected fixture parameter type {type(strict)}, expected {bool}") - - async with AsyncSupermemory(base_url=base_url, api_key=api_key, _strict_response_validation=strict) as client: + param = getattr(request, "param", True) + + # defaults + strict = True + http_client: None | httpx.AsyncClient = None + + if isinstance(param, bool): + strict = param + elif is_dict(param): + strict = param.get("strict", True) + assert isinstance(strict, bool) + + http_client_type = param.get("http_client", "httpx") + if http_client_type == "aiohttp": + http_client = DefaultAioHttpClient() + else: + raise TypeError(f"Unexpected fixture parameter type {type(param)}, expected bool or dict") + + async with AsyncSupermemory( + base_url=base_url, api_key=api_key, _strict_response_validation=strict, http_client=http_client + ) as client: yield client From 394e639fb904cd4c27b299f5960fd2f02f159b10 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 24 Jun 2025 04:11:45 +0000 Subject: [PATCH 47/48] chore(tests): skip some failing tests on the latest python versions --- tests/test_client.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/test_client.py b/tests/test_client.py index 5d2288bf..ecc086ec 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -191,6 +191,7 @@ def test_copy_signature(self) -> None: copy_param = copy_signature.parameters.get(name) assert copy_param is not None, f"copy() signature is missing the {name} param" + @pytest.mark.skipif(sys.version_info >= (3, 10), reason="fails because of a memory leak that started from 3.12") def test_copy_build_request(self) -> None: options = FinalRequestOptions(method="get", url="/foo") @@ -1013,6 +1014,7 @@ def test_copy_signature(self) -> None: copy_param = copy_signature.parameters.get(name) assert copy_param is not None, f"copy() signature is missing the {name} param" + @pytest.mark.skipif(sys.version_info >= (3, 10), reason="fails because of a memory leak that started from 3.12") def test_copy_build_request(self) -> None: options = FinalRequestOptions(method="get", url="/foo") From 95a924d286f10778d24ebf5ea9f2a34fd8385224 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 24 Jun 2025 04:12:09 +0000 Subject: [PATCH 48/48] release: 3.0.0-alpha.2 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 58 +++++++++++++++++++++++++++++++++++ pyproject.toml | 2 +- src/supermemory/_version.py | 2 +- 4 files changed, 61 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 2f23d818..4ae7e4a0 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "3.0.0-alpha.1" + ".": "3.0.0-alpha.2" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index ecf6170f..68ff2aa2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,63 @@ # Changelog +## 3.0.0-alpha.2 (2025-06-24) + +Full Changelog: [v3.0.0-alpha.1...v3.0.0-alpha.2](https://github.com/supermemoryai/python-sdk/compare/v3.0.0-alpha.1...v3.0.0-alpha.2) + +### Features + +* **api:** api update ([75c22e3](https://github.com/supermemoryai/python-sdk/commit/75c22e3f2db8eda857ca184f5bd531ff736a1c8c)) +* **api:** api update ([fc586f7](https://github.com/supermemoryai/python-sdk/commit/fc586f729ac602946e95de2e61b6d27a8c3b1167)) +* **api:** api update ([5aefc85](https://github.com/supermemoryai/python-sdk/commit/5aefc85540c70a195c2c59a8e9cdd529ff124a3b)) +* **api:** api update ([200e97a](https://github.com/supermemoryai/python-sdk/commit/200e97acf9f2be46a3ffef069912da9e4d05a951)) +* **api:** api update ([2d9b26c](https://github.com/supermemoryai/python-sdk/commit/2d9b26ce58cf533ffbff6d7a002c8504752c1e6a)) +* **api:** api update ([c7e0c2c](https://github.com/supermemoryai/python-sdk/commit/c7e0c2ccae941136705f2328fe536870789cf52a)) +* **api:** api update ([486888d](https://github.com/supermemoryai/python-sdk/commit/486888da89d570779e132f3b810c7d1a4f59082a)) +* **api:** api update ([378085f](https://github.com/supermemoryai/python-sdk/commit/378085f0090ebf25bbc46d0e3947737457ae3a30)) +* **api:** api update ([7b7711f](https://github.com/supermemoryai/python-sdk/commit/7b7711f282eebbaf4edb462b1dc2ed5dfb1bc0fc)) +* **api:** api update ([c194b2e](https://github.com/supermemoryai/python-sdk/commit/c194b2ecbe42ebe5997aab4bc96fb7120a5f6b9c)) +* **api:** api update ([d517cbc](https://github.com/supermemoryai/python-sdk/commit/d517cbce5d74b7cd92d8921d16ba1bb025848549)) +* **api:** api update ([fa9f582](https://github.com/supermemoryai/python-sdk/commit/fa9f582226ed9dee9ea8196b78b9312938093465)) +* **api:** api update ([d1b2a97](https://github.com/supermemoryai/python-sdk/commit/d1b2a9719deb080c4b64ae97893ba139ecaebf68)) +* **api:** api update ([02180e2](https://github.com/supermemoryai/python-sdk/commit/02180e296f97a30626b183ba533f45879981cc62)) +* **api:** api update ([51586e6](https://github.com/supermemoryai/python-sdk/commit/51586e68f9ec7b04b7aba149142f6e550113c0b9)) +* **api:** api update ([cd4782d](https://github.com/supermemoryai/python-sdk/commit/cd4782de2b4f05196e6b3f37768de900a6a9e81d)) +* **api:** api update ([f58d93d](https://github.com/supermemoryai/python-sdk/commit/f58d93dc249798bdf62c00335c6b9bfdf8430795)) +* **api:** api update ([e95b77e](https://github.com/supermemoryai/python-sdk/commit/e95b77ee719a76f0eb22c56f07e90f05712de7cd)) +* **api:** api update ([beffc5e](https://github.com/supermemoryai/python-sdk/commit/beffc5e09b5aee9c1517453b2f5574881ecf5dd7)) +* **api:** api update ([173a337](https://github.com/supermemoryai/python-sdk/commit/173a337626cbd75d951d10a04e91261ed3a2a384)) +* **api:** api update ([02e8b00](https://github.com/supermemoryai/python-sdk/commit/02e8b0072cb84a52124f61f5d8f64d69809c7833)) +* **api:** api update ([1788d47](https://github.com/supermemoryai/python-sdk/commit/1788d476d76fd786ed495ee5f8048c301fae0799)) +* **api:** api update ([5bc5a86](https://github.com/supermemoryai/python-sdk/commit/5bc5a8611c00f3c77c1e3787f578c91577ddfbb0)) +* **api:** api update ([0c627bd](https://github.com/supermemoryai/python-sdk/commit/0c627bde6c7f6ef8d55e94993a612f34c75c7f80)) +* **client:** add follow_redirects request option ([abd637d](https://github.com/supermemoryai/python-sdk/commit/abd637db1e3642c0dcc609bf4cac3d0a2bdad061)) +* **client:** add support for aiohttp ([d376719](https://github.com/supermemoryai/python-sdk/commit/d37671920ac604d643c0d9edaaee08aaaea2d881)) + + +### Bug Fixes + +* **client:** correctly parse binary response | stream ([b236e85](https://github.com/supermemoryai/python-sdk/commit/b236e8552e9393048b3541c2c41d969fd5a88ac0)) +* **tests:** fix: tests which call HTTP endpoints directly with the example parameters ([f9bf3c1](https://github.com/supermemoryai/python-sdk/commit/f9bf3c135c6a6236c8ef0ee5d538843021448b9d)) + + +### Chores + +* **ci:** enable for pull requests ([ec1b12b](https://github.com/supermemoryai/python-sdk/commit/ec1b12b9447ff582d80a0dedf75dc5c924aee6e4)) +* **docs:** grammar improvements ([1f9f018](https://github.com/supermemoryai/python-sdk/commit/1f9f01876a2754521ab8cc75c92634b05221f6c5)) +* **docs:** remove reference to rye shell ([23840c8](https://github.com/supermemoryai/python-sdk/commit/23840c8b8a67cb3b60e94c1eb74994d28b3508f1)) +* **internal:** codegen related update ([2b10aa8](https://github.com/supermemoryai/python-sdk/commit/2b10aa88d82e044d1a7df2bdd266209f4c130c8d)) +* **internal:** codegen related update ([c4039a7](https://github.com/supermemoryai/python-sdk/commit/c4039a7d5124d2c6719508830bf8067ebbb4fd58)) +* **internal:** update conftest.py ([355810b](https://github.com/supermemoryai/python-sdk/commit/355810b458222ddf405ec130cf43887cf7b2138d)) +* **readme:** update badges ([9415b50](https://github.com/supermemoryai/python-sdk/commit/9415b50222cea2e827a8e9c13525ad8e19df9ff2)) +* **tests:** add tests for httpx client instantiation & proxies ([a462f22](https://github.com/supermemoryai/python-sdk/commit/a462f2240dac23bf780f540ba39da3febbc561e7)) +* **tests:** run tests in parallel ([79f6359](https://github.com/supermemoryai/python-sdk/commit/79f6359beb267f85e273a5a3017283d9e231e78a)) +* **tests:** skip some failing tests on the latest python versions ([394e639](https://github.com/supermemoryai/python-sdk/commit/394e639fb904cd4c27b299f5960fd2f02f159b10)) + + +### Documentation + +* **client:** fix httpx.Timeout documentation reference ([be3ccbd](https://github.com/supermemoryai/python-sdk/commit/be3ccbdebe4974592c670cb43ed572ced78e60a3)) + ## 3.0.0-alpha.1 (2025-05-17) Full Changelog: [v0.1.0-alpha.1...v3.0.0-alpha.1](https://github.com/supermemoryai/python-sdk/compare/v0.1.0-alpha.1...v3.0.0-alpha.1) diff --git a/pyproject.toml b/pyproject.toml index 8f895d43..51b28cf1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "supermemory" -version = "3.0.0-alpha.1" +version = "3.0.0-alpha.2" 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 c8cc1993..dfc97c46 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.0.0-alpha.1" # x-release-please-version +__version__ = "3.0.0-alpha.2" # x-release-please-version