From 10e12a2f0453c3e5a2eaeee86a3da9738c161e50 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Thu, 26 Jun 2025 07:05:37 +0000
Subject: [PATCH 1/2] feat(api): api update
---
.stats.yml | 6 +-
README.md | 17 ++++
api.md | 8 +-
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, 218 insertions(+), 9 deletions(-)
create mode 100644 src/supermemory/types/memory_upload_file_params.py
create mode 100644 src/supermemory/types/memory_upload_file_response.py
diff --git a/.stats.yml b/.stats.yml
index 8f056938..561e86c8 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
+configured_endpoints: 9
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/supermemory--inc%2Fsupermemory-0e2874da641d72b5833ebef8cc792d86250d397b96eeedba7d4759ffabc076de.yml
+openapi_spec_hash: f13ea02b49134e11025cb18f3d45d313
config_hash: 8477e3ee6fd596ab6ac911d052e4de79
diff --git a/README.md b/README.md
index 8557c60b..4458331d 100644
--- a/README.md
+++ b/README.md
@@ -111,6 +111,23 @@ 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 77c6c399..fa4e72a2 100644
--- a/api.md
+++ b/api.md
@@ -3,7 +3,12 @@
Types:
```python
-from supermemory.types import MemoryUpdateResponse, MemoryAddResponse, MemoryGetResponse
+from supermemory.types import (
+ MemoryUpdateResponse,
+ MemoryAddResponse,
+ MemoryGetResponse,
+ MemoryUploadFileResponse,
+)
```
Methods:
@@ -12,6 +17,7 @@ Methods:
- client.memories.delete(id) -> None
- client.memories.add(\*\*params) -> MemoryAddResponse
- client.memories.get(id) -> MemoryGetResponse
+- client.memories.upload_file(\*\*params) -> MemoryUploadFileResponse
# Settings
diff --git a/src/supermemory/_files.py b/src/supermemory/_files.py
index 715cc207..0dcf63d3 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."
+ 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"
) from None
diff --git a/src/supermemory/resources/memories.py b/src/supermemory/resources/memories.py
index 02cf1801..6fe1d56a 100644
--- a/src/supermemory/resources/memories.py
+++ b/src/supermemory/resources/memories.py
@@ -2,13 +2,13 @@
from __future__ import annotations
-from typing import Dict, List, Union
+from typing import Dict, List, Union, Mapping, cast
import httpx
-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 ..types import memory_add_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 .._compat import cached_property
from .._resource import SyncAPIResource, AsyncAPIResource
from .._response import (
@@ -21,6 +21,7 @@
from ..types.memory_add_response import MemoryAddResponse
from ..types.memory_get_response import MemoryGetResponse
from ..types.memory_update_response import MemoryUpdateResponse
+from ..types.memory_upload_file_response import MemoryUploadFileResponse
__all__ = ["MemoriesResource", "AsyncMemoriesResource"]
@@ -243,6 +244,45 @@ 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
@@ -462,6 +502,45 @@ 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:
@@ -479,6 +558,9 @@ 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:
@@ -497,6 +579,9 @@ 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:
@@ -515,6 +600,9 @@ 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:
@@ -533,3 +621,6 @@ 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 6ca2ae1a..b87bc6d6 100644
--- a/src/supermemory/types/__init__.py
+++ b/src/supermemory/types/__init__.py
@@ -12,4 +12,6 @@
from .connection_get_response import ConnectionGetResponse as ConnectionGetResponse
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
new file mode 100644
index 00000000..aa6c082a
--- /dev/null
+++ b/src/supermemory/types/memory_upload_file_params.py
@@ -0,0 +1,13 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import 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
new file mode 100644
index 00000000..f67b958f
--- /dev/null
+++ b/src/supermemory/types/memory_upload_file_response.py
@@ -0,0 +1,11 @@
+# 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 c398ab8f..a9e1c726 100644
--- a/tests/api_resources/test_memories.py
+++ b/tests/api_resources/test_memories.py
@@ -13,6 +13,7 @@
MemoryAddResponse,
MemoryGetResponse,
MemoryUpdateResponse,
+ MemoryUploadFileResponse,
)
base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
@@ -222,6 +223,40 @@ 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(
@@ -428,3 +463,37 @@ 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 8e9fb4269be93a45121a6ca0a2880d33158115a8 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Thu, 26 Jun 2025 12:55:49 +0000
Subject: [PATCH 2/2] release: 3.0.0-alpha.19
---
.release-please-manifest.json | 2 +-
CHANGELOG.md | 8 ++++++++
pyproject.toml | 2 +-
src/supermemory/_version.py | 2 +-
4 files changed, 11 insertions(+), 3 deletions(-)
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index 4ae7e4a0..ef0780c0 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "3.0.0-alpha.2"
+ ".": "3.0.0-alpha.19"
}
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 68ff2aa2..f6bfd4b5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,13 @@
# Changelog
+## 3.0.0-alpha.19 (2025-06-26)
+
+Full Changelog: [v3.0.0-alpha.2...v3.0.0-alpha.19](https://github.com/supermemoryai/python-sdk/compare/v3.0.0-alpha.2...v3.0.0-alpha.19)
+
+### Features
+
+* **api:** api update ([10e12a2](https://github.com/supermemoryai/python-sdk/commit/10e12a2f0453c3e5a2eaeee86a3da9738c161e50))
+
## 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)
diff --git a/pyproject.toml b/pyproject.toml
index 51b28cf1..40c7241e 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "supermemory"
-version = "3.0.0-alpha.2"
+version = "3.0.0-alpha.19"
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 dfc97c46..d5e7dc08 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.2" # x-release-please-version
+__version__ = "3.0.0-alpha.19" # x-release-please-version