diff --git a/.release-please-manifest.json b/.release-please-manifest.json index e36acabe..e0dc5001 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "3.0.0-alpha.30" + ".": "3.1.0" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index cac7cf67..ebe1bcee 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-new-f181eaeb22a42d197dbd9c45fa61bf9a9b78a91d3334fc0f841494dc73d1a203.yml -openapi_spec_hash: bb8262ebcdea53979cf1cafbc2c68dc8 -config_hash: 9b9291a6c872b063900a46386729ba3c +configured_endpoints: 18 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/supermemory--inc%2Fsupermemory-new-b7bf8402df5372a3fac3cb55e083d7c09867328639cb7e2ceb7826de125ccff7.yml +openapi_spec_hash: a91c06034ad9e8a7315927d6f2e18521 +config_hash: 5deef1e3a49e3a7816348fbf7ba259bf diff --git a/CHANGELOG.md b/CHANGELOG.md index 87bd87cc..065277d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,23 @@ # Changelog +## 3.1.0 (2025-09-20) + +Full Changelog: [v3.0.0-alpha.30...v3.1.0](https://github.com/supermemoryai/python-sdk/compare/v3.0.0-alpha.30...v3.1.0) + +### Features + +* **api:** api update ([19c5dd3](https://github.com/supermemoryai/python-sdk/commit/19c5dd364b496d9a7858e1764000aba6d71613c6)) +* **api:** api update ([3082481](https://github.com/supermemoryai/python-sdk/commit/30824814233753584a89c56a2efb69d72995be02)) +* **api:** manual updates ([e92cb27](https://github.com/supermemoryai/python-sdk/commit/e92cb27550c2190a6804a2865ae359ff79fa1866)) +* **api:** manual updates ([251fdf1](https://github.com/supermemoryai/python-sdk/commit/251fdf116f5f1b0c1ecfb74bef94ba08f7921dca)) + + +### Chores + +* do not install brew dependencies in ./scripts/bootstrap by default ([fb748cf](https://github.com/supermemoryai/python-sdk/commit/fb748cf2375b1ce1eddd2584503e81d3b8386443)) +* **internal:** update pydantic dependency ([8c480f7](https://github.com/supermemoryai/python-sdk/commit/8c480f70cf3d0e55c4c5bc3b77d83bd6b21c4fdf)) +* **types:** change optional parameter type from NotGiven to Omit ([3031781](https://github.com/supermemoryai/python-sdk/commit/30317810d7089dbdf59f5e143bd66b1a0495b2b6)) + ## 3.0.0-alpha.30 (2025-09-15) Full Changelog: [v3.0.0-alpha.29...v3.0.0-alpha.30](https://github.com/supermemoryai/python-sdk/compare/v3.0.0-alpha.29...v3.0.0-alpha.30) diff --git a/README.md b/README.md index 05e12990..113af8d0 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ The REST API documentation can be found on [docs.supermemory.ai](https://docs.su ```sh # install from PyPI -pip install --pre supermemory +pip install supermemory ``` ## Usage @@ -77,7 +77,7 @@ You can enable this by installing `aiohttp`: ```sh # install from PyPI -pip install --pre supermemory[aiohttp] +pip install supermemory[aiohttp] ``` Then you can enable it by instantiating the client with `http_client=DefaultAioHttpClient()`: @@ -127,6 +127,23 @@ response = client.search.memories( print(response.include) ``` +## 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. @@ -143,8 +160,8 @@ from supermemory import Supermemory client = Supermemory() try: - client.search.documents( - q="machine learning concepts", + client.memories.add( + content="This is a detailed article about machine learning concepts...", ) except supermemory.APIConnectionError as e: print("The server could not be reached") @@ -188,8 +205,8 @@ client = Supermemory( ) # Or, configure per-request: -client.with_options(max_retries=5).search.documents( - q="machine learning concepts", +client.with_options(max_retries=5).memories.add( + content="This is a detailed article about machine learning concepts...", ) ``` @@ -213,8 +230,8 @@ client = Supermemory( ) # Override per-request: -client.with_options(timeout=5.0).search.documents( - q="machine learning concepts", +client.with_options(timeout=5.0).memories.add( + content="This is a detailed article about machine learning concepts...", ) ``` @@ -256,13 +273,13 @@ The "raw" Response object can be accessed by prefixing `.with_raw_response.` to from supermemory import Supermemory client = Supermemory() -response = client.search.with_raw_response.documents( - q="machine learning concepts", +response = client.memories.with_raw_response.add( + content="This is a detailed article about machine learning concepts...", ) print(response.headers.get('X-My-Header')) -search = response.parse() # get the object that `search.documents()` would have returned -print(search.results) +memory = response.parse() # get the object that `memories.add()` would have returned +print(memory.id) ``` These methods return an [`APIResponse`](https://github.com/supermemoryai/python-sdk/tree/main/src/supermemory/_response.py) object. @@ -276,8 +293,8 @@ The above interface eagerly reads the full response body when you make the reque To stream the response body, use `.with_streaming_response` instead, which requires a context manager and only reads the response body once you call `.read()`, `.text()`, `.json()`, `.iter_bytes()`, `.iter_text()`, `.iter_lines()` or `.parse()`. In the async client, these are async methods. ```python -with client.search.with_streaming_response.documents( - q="machine learning concepts", +with client.memories.with_streaming_response.add( + content="This is a detailed article about machine learning concepts...", ) as response: print(response.headers.get("X-My-Header")) diff --git a/api.md b/api.md index 083ba491..7aa6afa9 100644 --- a/api.md +++ b/api.md @@ -1,3 +1,49 @@ +# Memories + +Types: + +```python +from supermemory.types import ( + MemoryUpdateResponse, + MemoryListResponse, + MemoryAddResponse, + MemoryGetResponse, + MemoryUploadFileResponse, +) +``` + +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 +- client.memories.upload_file(\*\*params) -> MemoryUploadFileResponse + +# Documents + +Types: + +```python +from supermemory.types import ( + DocumentUpdateResponse, + DocumentListResponse, + DocumentAddResponse, + DocumentGetResponse, + DocumentUploadFileResponse, +) +``` + +Methods: + +- client.documents.update(id, \*\*params) -> DocumentUpdateResponse +- client.documents.list(\*\*params) -> DocumentListResponse +- client.documents.delete(id) -> None +- client.documents.add(\*\*params) -> DocumentAddResponse +- client.documents.get(id) -> DocumentGetResponse +- client.documents.upload_file(\*\*params) -> DocumentUploadFileResponse + # Search Types: diff --git a/pyproject.toml b/pyproject.toml index b941471b..46943a25 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "supermemory" -version = "3.0.0-alpha.30" +version = "3.1.0" description = "The official Python library for the supermemory API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/requirements-dev.lock b/requirements-dev.lock index b46f0c64..38e12e51 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -88,9 +88,9 @@ pluggy==1.5.0 propcache==0.3.1 # via aiohttp # via yarl -pydantic==2.10.3 +pydantic==2.11.9 # via supermemory -pydantic-core==2.27.1 +pydantic-core==2.33.2 # via pydantic pygments==2.18.0 # via rich @@ -126,6 +126,9 @@ typing-extensions==4.12.2 # via pydantic-core # via pyright # via supermemory + # via typing-inspection +typing-inspection==0.4.1 + # via pydantic virtualenv==20.24.5 # via nox yarl==1.20.0 diff --git a/requirements.lock b/requirements.lock index 22670ed7..de2de686 100644 --- a/requirements.lock +++ b/requirements.lock @@ -55,9 +55,9 @@ multidict==6.4.4 propcache==0.3.1 # via aiohttp # via yarl -pydantic==2.10.3 +pydantic==2.11.9 # via supermemory -pydantic-core==2.27.1 +pydantic-core==2.33.2 # via pydantic sniffio==1.3.0 # via anyio @@ -68,5 +68,8 @@ typing-extensions==4.12.2 # via pydantic # via pydantic-core # via supermemory + # via typing-inspection +typing-inspection==0.4.1 + # via pydantic yarl==1.20.0 # via aiohttp diff --git a/scripts/bootstrap b/scripts/bootstrap index e84fe62c..b430fee3 100755 --- a/scripts/bootstrap +++ b/scripts/bootstrap @@ -4,10 +4,18 @@ set -e cd "$(dirname "$0")/.." -if ! command -v rye >/dev/null 2>&1 && [ -f "Brewfile" ] && [ "$(uname -s)" = "Darwin" ]; then +if [ -f "Brewfile" ] && [ "$(uname -s)" = "Darwin" ] && [ "$SKIP_BREW" != "1" ] && [ -t 0 ]; then brew bundle check >/dev/null 2>&1 || { - echo "==> Installing Homebrew dependencies…" - brew bundle + echo -n "==> Install Homebrew dependencies? (y/N): " + read -r response + case "$response" in + [yY][eE][sS]|[yY]) + brew bundle + ;; + *) + ;; + esac + echo } fi diff --git a/src/supermemory/__init__.py b/src/supermemory/__init__.py index bb87a0cf..78f13680 100644 --- a/src/supermemory/__init__.py +++ b/src/supermemory/__init__.py @@ -3,7 +3,7 @@ import typing as _t from . import types -from ._types import NOT_GIVEN, Omit, NoneType, NotGiven, Transport, ProxiesTypes +from ._types import NOT_GIVEN, Omit, NoneType, NotGiven, Transport, ProxiesTypes, omit, not_given from ._utils import file_from_path from ._client import ( Client, @@ -48,7 +48,9 @@ "ProxiesTypes", "NotGiven", "NOT_GIVEN", + "not_given", "Omit", + "omit", "SupermemoryError", "APIError", "APIStatusError", diff --git a/src/supermemory/_base_client.py b/src/supermemory/_base_client.py index fbcaf523..4c1fceb0 100644 --- a/src/supermemory/_base_client.py +++ b/src/supermemory/_base_client.py @@ -42,7 +42,6 @@ from ._qs import Querystring from ._files import to_httpx_files, async_to_httpx_files from ._types import ( - NOT_GIVEN, Body, Omit, Query, @@ -57,6 +56,7 @@ RequestOptions, HttpxRequestFiles, ModelBuilderProtocol, + not_given, ) from ._utils import is_dict, is_list, asyncify, is_given, lru_cache, is_mapping from ._compat import PYDANTIC_V1, model_copy, model_dump @@ -145,9 +145,9 @@ def __init__( def __init__( self, *, - url: URL | NotGiven = NOT_GIVEN, - json: Body | NotGiven = NOT_GIVEN, - params: Query | NotGiven = NOT_GIVEN, + url: URL | NotGiven = not_given, + json: Body | NotGiven = not_given, + params: Query | NotGiven = not_given, ) -> None: self.url = url self.json = json @@ -595,7 +595,7 @@ def _maybe_override_cast_to(self, cast_to: type[ResponseT], options: FinalReques # we internally support defining a temporary header to override the # default `cast_to` type for use with `.with_raw_response` and `.with_streaming_response` # see _response.py for implementation details - override_cast_to = headers.pop(OVERRIDE_CAST_TO_HEADER, NOT_GIVEN) + override_cast_to = headers.pop(OVERRIDE_CAST_TO_HEADER, not_given) if is_given(override_cast_to): options.headers = headers return cast(Type[ResponseT], override_cast_to) @@ -825,7 +825,7 @@ def __init__( version: str, base_url: str | URL, max_retries: int = DEFAULT_MAX_RETRIES, - timeout: float | Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | Timeout | None | NotGiven = not_given, http_client: httpx.Client | None = None, custom_headers: Mapping[str, str] | None = None, custom_query: Mapping[str, object] | None = None, @@ -1356,7 +1356,7 @@ def __init__( base_url: str | URL, _strict_response_validation: bool, max_retries: int = DEFAULT_MAX_RETRIES, - timeout: float | Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | Timeout | None | NotGiven = not_given, http_client: httpx.AsyncClient | None = None, custom_headers: Mapping[str, str] | None = None, custom_query: Mapping[str, object] | None = None, @@ -1818,8 +1818,8 @@ def make_request_options( extra_query: Query | None = None, extra_body: Body | None = None, idempotency_key: str | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - post_parser: PostParser | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + post_parser: PostParser | NotGiven = not_given, ) -> RequestOptions: """Create a dict of type RequestOptions without keys of NotGiven values.""" options: RequestOptions = {} diff --git a/src/supermemory/_client.py b/src/supermemory/_client.py index 222d8fa6..0de41593 100644 --- a/src/supermemory/_client.py +++ b/src/supermemory/_client.py @@ -3,7 +3,7 @@ from __future__ import annotations import os -from typing import Any, Union, Mapping +from typing import Any, Mapping from typing_extensions import Self, override import httpx @@ -11,17 +11,17 @@ from . import _exceptions from ._qs import Querystring from ._types import ( - NOT_GIVEN, Omit, Timeout, NotGiven, Transport, ProxiesTypes, RequestOptions, + not_given, ) from ._utils import is_given, get_async_library from ._version import __version__ -from .resources import search, settings, connections +from .resources import search, memories, settings, documents, connections from ._streaming import Stream as Stream, AsyncStream as AsyncStream from ._exceptions import APIStatusError, SupermemoryError from ._base_client import ( @@ -43,6 +43,8 @@ class Supermemory(SyncAPIClient): + memories: memories.MemoriesResource + documents: documents.DocumentsResource search: search.SearchResource settings: settings.SettingsResource connections: connections.ConnectionsResource @@ -57,7 +59,7 @@ def __init__( *, api_key: str | None = None, base_url: str | httpx.URL | None = None, - timeout: Union[float, Timeout, None, NotGiven] = NOT_GIVEN, + timeout: float | Timeout | None | NotGiven = not_given, max_retries: int = DEFAULT_MAX_RETRIES, default_headers: Mapping[str, str] | None = None, default_query: Mapping[str, object] | None = None, @@ -103,6 +105,8 @@ def __init__( _strict_response_validation=_strict_response_validation, ) + self.memories = memories.MemoriesResource(self) + self.documents = documents.DocumentsResource(self) self.search = search.SearchResource(self) self.settings = settings.SettingsResource(self) self.connections = connections.ConnectionsResource(self) @@ -134,9 +138,9 @@ def copy( *, api_key: str | None = None, base_url: str | httpx.URL | None = None, - timeout: float | Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | Timeout | None | NotGiven = not_given, http_client: httpx.Client | None = None, - max_retries: int | NotGiven = NOT_GIVEN, + max_retries: int | NotGiven = not_given, default_headers: Mapping[str, str] | None = None, set_default_headers: Mapping[str, str] | None = None, default_query: Mapping[str, object] | None = None, @@ -215,6 +219,8 @@ def _make_status_error( class AsyncSupermemory(AsyncAPIClient): + memories: memories.AsyncMemoriesResource + documents: documents.AsyncDocumentsResource search: search.AsyncSearchResource settings: settings.AsyncSettingsResource connections: connections.AsyncConnectionsResource @@ -229,7 +235,7 @@ def __init__( *, api_key: str | None = None, base_url: str | httpx.URL | None = None, - timeout: Union[float, Timeout, None, NotGiven] = NOT_GIVEN, + timeout: float | Timeout | None | NotGiven = not_given, max_retries: int = DEFAULT_MAX_RETRIES, default_headers: Mapping[str, str] | None = None, default_query: Mapping[str, object] | None = None, @@ -275,6 +281,8 @@ def __init__( _strict_response_validation=_strict_response_validation, ) + self.memories = memories.AsyncMemoriesResource(self) + self.documents = documents.AsyncDocumentsResource(self) self.search = search.AsyncSearchResource(self) self.settings = settings.AsyncSettingsResource(self) self.connections = connections.AsyncConnectionsResource(self) @@ -306,9 +314,9 @@ def copy( *, api_key: str | None = None, base_url: str | httpx.URL | None = None, - timeout: float | Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | Timeout | None | NotGiven = not_given, http_client: httpx.AsyncClient | None = None, - max_retries: int | NotGiven = NOT_GIVEN, + max_retries: int | NotGiven = not_given, default_headers: Mapping[str, str] | None = None, set_default_headers: Mapping[str, str] | None = None, default_query: Mapping[str, object] | None = None, @@ -388,6 +396,8 @@ def _make_status_error( class SupermemoryWithRawResponse: def __init__(self, client: Supermemory) -> None: + self.memories = memories.MemoriesResourceWithRawResponse(client.memories) + self.documents = documents.DocumentsResourceWithRawResponse(client.documents) self.search = search.SearchResourceWithRawResponse(client.search) self.settings = settings.SettingsResourceWithRawResponse(client.settings) self.connections = connections.ConnectionsResourceWithRawResponse(client.connections) @@ -395,6 +405,8 @@ def __init__(self, client: Supermemory) -> None: class AsyncSupermemoryWithRawResponse: def __init__(self, client: AsyncSupermemory) -> None: + self.memories = memories.AsyncMemoriesResourceWithRawResponse(client.memories) + self.documents = documents.AsyncDocumentsResourceWithRawResponse(client.documents) self.search = search.AsyncSearchResourceWithRawResponse(client.search) self.settings = settings.AsyncSettingsResourceWithRawResponse(client.settings) self.connections = connections.AsyncConnectionsResourceWithRawResponse(client.connections) @@ -402,6 +414,8 @@ def __init__(self, client: AsyncSupermemory) -> None: class SupermemoryWithStreamedResponse: def __init__(self, client: Supermemory) -> None: + self.memories = memories.MemoriesResourceWithStreamingResponse(client.memories) + self.documents = documents.DocumentsResourceWithStreamingResponse(client.documents) self.search = search.SearchResourceWithStreamingResponse(client.search) self.settings = settings.SettingsResourceWithStreamingResponse(client.settings) self.connections = connections.ConnectionsResourceWithStreamingResponse(client.connections) @@ -409,6 +423,8 @@ def __init__(self, client: Supermemory) -> None: class AsyncSupermemoryWithStreamedResponse: def __init__(self, client: AsyncSupermemory) -> None: + self.memories = memories.AsyncMemoriesResourceWithStreamingResponse(client.memories) + self.documents = documents.AsyncDocumentsResourceWithStreamingResponse(client.documents) self.search = search.AsyncSearchResourceWithStreamingResponse(client.search) self.settings = settings.AsyncSettingsResourceWithStreamingResponse(client.settings) self.connections = connections.AsyncConnectionsResourceWithStreamingResponse(client.connections) diff --git a/src/supermemory/_files.py b/src/supermemory/_files.py index cc14c14f..ae7c4650 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/_models.py b/src/supermemory/_models.py index 3a6017ef..6a3cd1d2 100644 --- a/src/supermemory/_models.py +++ b/src/supermemory/_models.py @@ -256,7 +256,7 @@ def model_dump( mode: Literal["json", "python"] | str = "python", include: IncEx | None = None, exclude: IncEx | None = None, - by_alias: bool = False, + by_alias: bool | None = None, exclude_unset: bool = False, exclude_defaults: bool = False, exclude_none: bool = False, @@ -264,6 +264,7 @@ def model_dump( warnings: bool | Literal["none", "warn", "error"] = True, context: dict[str, Any] | None = None, serialize_as_any: bool = False, + fallback: Callable[[Any], Any] | None = None, ) -> dict[str, Any]: """Usage docs: https://docs.pydantic.dev/2.4/concepts/serialization/#modelmodel_dump @@ -295,10 +296,12 @@ def model_dump( raise ValueError("context is only supported in Pydantic v2") if serialize_as_any != False: raise ValueError("serialize_as_any is only supported in Pydantic v2") + if fallback is not None: + raise ValueError("fallback is only supported in Pydantic v2") dumped = super().dict( # pyright: ignore[reportDeprecated] include=include, exclude=exclude, - by_alias=by_alias, + by_alias=by_alias if by_alias is not None else False, exclude_unset=exclude_unset, exclude_defaults=exclude_defaults, exclude_none=exclude_none, @@ -313,13 +316,14 @@ def model_dump_json( indent: int | None = None, include: IncEx | None = None, exclude: IncEx | None = None, - by_alias: bool = False, + by_alias: bool | None = None, exclude_unset: bool = False, exclude_defaults: bool = False, exclude_none: bool = False, round_trip: bool = False, warnings: bool | Literal["none", "warn", "error"] = True, context: dict[str, Any] | None = None, + fallback: Callable[[Any], Any] | None = None, serialize_as_any: bool = False, ) -> str: """Usage docs: https://docs.pydantic.dev/2.4/concepts/serialization/#modelmodel_dump_json @@ -348,11 +352,13 @@ def model_dump_json( raise ValueError("context is only supported in Pydantic v2") if serialize_as_any != False: raise ValueError("serialize_as_any is only supported in Pydantic v2") + if fallback is not None: + raise ValueError("fallback is only supported in Pydantic v2") return super().json( # type: ignore[reportDeprecated] indent=indent, include=include, exclude=exclude, - by_alias=by_alias, + by_alias=by_alias if by_alias is not None else False, exclude_unset=exclude_unset, exclude_defaults=exclude_defaults, exclude_none=exclude_none, diff --git a/src/supermemory/_qs.py b/src/supermemory/_qs.py index 274320ca..ada6fd3f 100644 --- a/src/supermemory/_qs.py +++ b/src/supermemory/_qs.py @@ -4,7 +4,7 @@ from urllib.parse import parse_qs, urlencode from typing_extensions import Literal, get_args -from ._types import NOT_GIVEN, NotGiven, NotGivenOr +from ._types import NotGiven, not_given from ._utils import flatten _T = TypeVar("_T") @@ -41,8 +41,8 @@ def stringify( self, params: Params, *, - array_format: NotGivenOr[ArrayFormat] = NOT_GIVEN, - nested_format: NotGivenOr[NestedFormat] = NOT_GIVEN, + array_format: ArrayFormat | NotGiven = not_given, + nested_format: NestedFormat | NotGiven = not_given, ) -> str: return urlencode( self.stringify_items( @@ -56,8 +56,8 @@ def stringify_items( self, params: Params, *, - array_format: NotGivenOr[ArrayFormat] = NOT_GIVEN, - nested_format: NotGivenOr[NestedFormat] = NOT_GIVEN, + array_format: ArrayFormat | NotGiven = not_given, + nested_format: NestedFormat | NotGiven = not_given, ) -> list[tuple[str, str]]: opts = Options( qs=self, @@ -143,8 +143,8 @@ def __init__( self, qs: Querystring = _qs, *, - array_format: NotGivenOr[ArrayFormat] = NOT_GIVEN, - nested_format: NotGivenOr[NestedFormat] = NOT_GIVEN, + array_format: ArrayFormat | NotGiven = not_given, + nested_format: NestedFormat | NotGiven = not_given, ) -> None: self.array_format = qs.array_format if isinstance(array_format, NotGiven) else array_format self.nested_format = qs.nested_format if isinstance(nested_format, NotGiven) else nested_format diff --git a/src/supermemory/_types.py b/src/supermemory/_types.py index 24417563..9a02d83d 100644 --- a/src/supermemory/_types.py +++ b/src/supermemory/_types.py @@ -117,18 +117,21 @@ class RequestOptions(TypedDict, total=False): # Sentinel class used until PEP 0661 is accepted class NotGiven: """ - A sentinel singleton class used to distinguish omitted keyword arguments - from those passed in with the value None (which may have different behavior). + For parameters with a meaningful None value, we need to distinguish between + the user explicitly passing None, and the user not passing the parameter at + all. + + User code shouldn't need to use not_given directly. For example: ```py - def get(timeout: Union[int, NotGiven, None] = NotGiven()) -> Response: ... + def create(timeout: Timeout | None | NotGiven = not_given): ... - get(timeout=1) # 1s timeout - get(timeout=None) # No timeout - get() # Default timeout behavior, which may not be statically known at the method definition. + create(timeout=1) # 1s timeout + create(timeout=None) # No timeout + create() # Default timeout behavior ``` """ @@ -140,13 +143,14 @@ def __repr__(self) -> str: return "NOT_GIVEN" -NotGivenOr = Union[_T, NotGiven] +not_given = NotGiven() +# for backwards compatibility: NOT_GIVEN = NotGiven() class Omit: - """In certain situations you need to be able to represent a case where a default value has - to be explicitly removed and `None` is not an appropriate substitute, for example: + """ + To explicitly omit something from being sent in a request, use `omit`. ```py # as the default `Content-Type` header is `application/json` that will be sent @@ -156,8 +160,8 @@ class Omit: # to look something like: 'multipart/form-data; boundary=0d8382fcf5f8c3be01ca2e11002d2983' client.post(..., headers={"Content-Type": "multipart/form-data"}) - # instead you can remove the default `application/json` header by passing Omit - client.post(..., headers={"Content-Type": Omit()}) + # instead you can remove the default `application/json` header by passing omit + client.post(..., headers={"Content-Type": omit}) ``` """ @@ -165,6 +169,9 @@ def __bool__(self) -> Literal[False]: return False +omit = Omit() + + @runtime_checkable class ModelBuilderProtocol(Protocol): @classmethod diff --git a/src/supermemory/_utils/_transform.py b/src/supermemory/_utils/_transform.py index c19124f0..52075492 100644 --- a/src/supermemory/_utils/_transform.py +++ b/src/supermemory/_utils/_transform.py @@ -268,7 +268,7 @@ def _transform_typeddict( annotations = get_type_hints(expected_type, include_extras=True) for key, value in data.items(): if not is_given(value): - # we don't need to include `NotGiven` values here as they'll + # we don't need to include omitted values here as they'll # be stripped out before the request is sent anyway continue @@ -434,7 +434,7 @@ async def _async_transform_typeddict( annotations = get_type_hints(expected_type, include_extras=True) for key, value in data.items(): if not is_given(value): - # we don't need to include `NotGiven` values here as they'll + # we don't need to include omitted values here as they'll # be stripped out before the request is sent anyway continue diff --git a/src/supermemory/_utils/_utils.py b/src/supermemory/_utils/_utils.py index f0818595..50d59269 100644 --- a/src/supermemory/_utils/_utils.py +++ b/src/supermemory/_utils/_utils.py @@ -21,7 +21,7 @@ import sniffio -from .._types import NotGiven, FileTypes, NotGivenOr, HeadersLike +from .._types import Omit, NotGiven, FileTypes, HeadersLike _T = TypeVar("_T") _TupleT = TypeVar("_TupleT", bound=Tuple[object, ...]) @@ -63,7 +63,7 @@ def _extract_items( try: key = path[index] except IndexError: - if isinstance(obj, NotGiven): + if not is_given(obj): # no value was provided - we can safely ignore return [] @@ -126,8 +126,8 @@ def _extract_items( return [] -def is_given(obj: NotGivenOr[_T]) -> TypeGuard[_T]: - return not isinstance(obj, NotGiven) +def is_given(obj: _T | NotGiven | Omit) -> TypeGuard[_T]: + return not isinstance(obj, NotGiven) and not isinstance(obj, Omit) # Type safe methods for narrowing types with TypeVars. diff --git a/src/supermemory/_version.py b/src/supermemory/_version.py index 0e7aa74e..941e3486 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.30" # x-release-please-version +__version__ = "3.1.0" # x-release-please-version diff --git a/src/supermemory/resources/__init__.py b/src/supermemory/resources/__init__.py index de24910f..ca24f73f 100644 --- a/src/supermemory/resources/__init__.py +++ b/src/supermemory/resources/__init__.py @@ -8,6 +8,14 @@ SearchResourceWithStreamingResponse, AsyncSearchResourceWithStreamingResponse, ) +from .memories import ( + MemoriesResource, + AsyncMemoriesResource, + MemoriesResourceWithRawResponse, + AsyncMemoriesResourceWithRawResponse, + MemoriesResourceWithStreamingResponse, + AsyncMemoriesResourceWithStreamingResponse, +) from .settings import ( SettingsResource, AsyncSettingsResource, @@ -16,6 +24,14 @@ SettingsResourceWithStreamingResponse, AsyncSettingsResourceWithStreamingResponse, ) +from .documents import ( + DocumentsResource, + AsyncDocumentsResource, + DocumentsResourceWithRawResponse, + AsyncDocumentsResourceWithRawResponse, + DocumentsResourceWithStreamingResponse, + AsyncDocumentsResourceWithStreamingResponse, +) from .connections import ( ConnectionsResource, AsyncConnectionsResource, @@ -26,6 +42,18 @@ ) __all__ = [ + "MemoriesResource", + "AsyncMemoriesResource", + "MemoriesResourceWithRawResponse", + "AsyncMemoriesResourceWithRawResponse", + "MemoriesResourceWithStreamingResponse", + "AsyncMemoriesResourceWithStreamingResponse", + "DocumentsResource", + "AsyncDocumentsResource", + "DocumentsResourceWithRawResponse", + "AsyncDocumentsResourceWithRawResponse", + "DocumentsResourceWithStreamingResponse", + "AsyncDocumentsResourceWithStreamingResponse", "SearchResource", "AsyncSearchResource", "SearchResourceWithRawResponse", diff --git a/src/supermemory/resources/connections.py b/src/supermemory/resources/connections.py index dfd73a81..f56c31da 100644 --- a/src/supermemory/resources/connections.py +++ b/src/supermemory/resources/connections.py @@ -15,7 +15,7 @@ connection_list_documents_params, connection_delete_by_provider_params, ) -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven, SequenceNotStr +from .._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given from .._utils import maybe_transform, async_maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource @@ -61,16 +61,16 @@ def create( self, provider: Literal["notion", "google-drive", "onedrive"], *, - container_tags: SequenceNotStr[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, + container_tags: SequenceNotStr[str] | Omit = omit, + document_limit: int | Omit = omit, + metadata: Optional[Dict[str, Union[str, float, bool]]] | Omit = omit, + redirect_url: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ConnectionCreateResponse: """ Initialize connection and get authorization URL @@ -106,13 +106,13 @@ def create( def list( self, *, - container_tags: SequenceNotStr[str] | NotGiven = NOT_GIVEN, + container_tags: SequenceNotStr[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # 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, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ConnectionListResponse: """ List all connections @@ -146,7 +146,7 @@ def delete_by_id( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ConnectionDeleteByIDResponse: """ Delete a specific connection by ID @@ -180,7 +180,7 @@ def delete_by_provider( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ConnectionDeleteByProviderResponse: """ Delete connection for a specific provider and container tags @@ -219,7 +219,7 @@ def get_by_id( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ConnectionGetByIDResponse: """ Get connection details with id @@ -253,7 +253,7 @@ def get_by_tags( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ConnectionGetByTagsResponse: """ Get connection details with provider and container tags @@ -286,13 +286,13 @@ def import_( self, provider: Literal["notion", "google-drive", "onedrive"], *, - container_tags: SequenceNotStr[str] | NotGiven = NOT_GIVEN, + container_tags: SequenceNotStr[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # 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, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> str: """ Initiate a manual sync of connections @@ -324,13 +324,13 @@ def list_documents( self, provider: Literal["notion", "google-drive", "onedrive"], *, - container_tags: SequenceNotStr[str] | NotGiven = NOT_GIVEN, + container_tags: SequenceNotStr[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # 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, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ConnectionListDocumentsResponse: """ List documents indexed for a provider and container tags @@ -384,16 +384,16 @@ async def create( self, provider: Literal["notion", "google-drive", "onedrive"], *, - container_tags: SequenceNotStr[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, + container_tags: SequenceNotStr[str] | Omit = omit, + document_limit: int | Omit = omit, + metadata: Optional[Dict[str, Union[str, float, bool]]] | Omit = omit, + redirect_url: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ConnectionCreateResponse: """ Initialize connection and get authorization URL @@ -429,13 +429,13 @@ async def create( async def list( self, *, - container_tags: SequenceNotStr[str] | NotGiven = NOT_GIVEN, + container_tags: SequenceNotStr[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # 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, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ConnectionListResponse: """ List all connections @@ -471,7 +471,7 @@ async def delete_by_id( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ConnectionDeleteByIDResponse: """ Delete a specific connection by ID @@ -505,7 +505,7 @@ async def delete_by_provider( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ConnectionDeleteByProviderResponse: """ Delete connection for a specific provider and container tags @@ -544,7 +544,7 @@ async def get_by_id( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ConnectionGetByIDResponse: """ Get connection details with id @@ -578,7 +578,7 @@ async def get_by_tags( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ConnectionGetByTagsResponse: """ Get connection details with provider and container tags @@ -611,13 +611,13 @@ async def import_( self, provider: Literal["notion", "google-drive", "onedrive"], *, - container_tags: SequenceNotStr[str] | NotGiven = NOT_GIVEN, + container_tags: SequenceNotStr[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # 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, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> str: """ Initiate a manual sync of connections @@ -651,13 +651,13 @@ async def list_documents( self, provider: Literal["notion", "google-drive", "onedrive"], *, - container_tags: SequenceNotStr[str] | NotGiven = NOT_GIVEN, + container_tags: SequenceNotStr[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # 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, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ConnectionListDocumentsResponse: """ List documents indexed for a provider and container tags diff --git a/src/supermemory/resources/documents.py b/src/supermemory/resources/documents.py new file mode 100644 index 00000000..ac9c4ad0 --- /dev/null +++ b/src/supermemory/resources/documents.py @@ -0,0 +1,894 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union, Mapping, cast +from typing_extensions import Literal + +import httpx + +from ..types import document_add_params, document_list_params, document_update_params, document_upload_file_params +from .._types import Body, Omit, Query, Headers, NoneType, NotGiven, FileTypes, SequenceNotStr, omit, not_given +from .._utils import extract_files, maybe_transform, deepcopy_minimal, async_maybe_transform +from .._compat import cached_property +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.document_add_response import DocumentAddResponse +from ..types.document_get_response import DocumentGetResponse +from ..types.document_list_response import DocumentListResponse +from ..types.document_update_response import DocumentUpdateResponse +from ..types.document_upload_file_response import DocumentUploadFileResponse + +__all__ = ["DocumentsResource", "AsyncDocumentsResource"] + + +class DocumentsResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> DocumentsResourceWithRawResponse: + """ + 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 DocumentsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> DocumentsResourceWithStreamingResponse: + """ + 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 DocumentsResourceWithStreamingResponse(self) + + def update( + self, + id: str, + *, + container_tag: str | Omit = omit, + container_tags: SequenceNotStr[str] | Omit = omit, + content: str | Omit = omit, + custom_id: str | Omit = omit, + file_type: str | Omit = omit, + metadata: Dict[str, Union[str, float, bool, SequenceNotStr[str]]] | Omit = omit, + mime_type: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> DocumentUpdateResponse: + """ + Update a document with any content type (text, url, file, etc.) and metadata + + Args: + container_tag: Optional tag this document 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 + documents. + + container_tags: (DEPRECATED: Use containerTag instead) Optional tags this document 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 documents. + + content: The content to extract and process into a document. 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. + + custom_id: Optional custom ID of the document. This could be an ID from your database that + will uniquely identify this document. + + file_type: + Optional file type override to force specific processing behavior. Valid values: + text, pdf, tweet, google_doc, google_slide, google_sheet, image, video, + notion_doc, webpage, onedrive + + metadata: Optional metadata for the document. This is used to store additional information + about the document. You can use this to store any additional information you + need about the document. Metadata can be filtered through. Keys must be strings + and are case sensitive. Values can be strings, numbers, or booleans. You cannot + nest objects. + + mime_type: Required when fileType is 'image' or 'video'. Specifies the exact MIME type to + use (e.g., 'image/png', 'image/jpeg', 'video/mp4', 'video/webm') + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._patch( + f"/v3/documents/{id}", + body=maybe_transform( + { + "container_tag": container_tag, + "container_tags": container_tags, + "content": content, + "custom_id": custom_id, + "file_type": file_type, + "metadata": metadata, + "mime_type": mime_type, + }, + document_update_params.DocumentUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=DocumentUpdateResponse, + ) + + def list( + self, + *, + container_tags: SequenceNotStr[str] | Omit = omit, + filters: document_list_params.Filters | Omit = omit, + include_content: bool | Omit = omit, + limit: Union[str, float] | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + page: Union[str, float] | Omit = omit, + sort: Literal["createdAt", "updatedAt"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> DocumentListResponse: + """ + Retrieves a paginated list of documents with their metadata and workflow status + + Args: + container_tags: Optional tags this document 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 + documents. + + filters: Optional filters to apply to the search. Can be a JSON string or Query object. + + include_content: Whether to include the content field in the response. Warning: This can make + responses significantly larger. + + 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._post( + "/v3/documents/list", + body=maybe_transform( + { + "container_tags": container_tags, + "filters": filters, + "include_content": include_content, + "limit": limit, + "order": order, + "page": page, + "sort": sort, + }, + document_list_params.DocumentListParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=DocumentListResponse, + ) + + def delete( + self, + id: str, + *, + # 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, + ) -> None: + """ + Delete a document by ID or customId + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not 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/documents/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + def add( + self, + *, + content: str, + container_tag: str | Omit = omit, + container_tags: SequenceNotStr[str] | Omit = omit, + custom_id: str | Omit = omit, + file_type: str | Omit = omit, + metadata: Dict[str, Union[str, float, bool, SequenceNotStr[str]]] | Omit = omit, + mime_type: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> DocumentAddResponse: + """ + Add a document with any content type (text, url, file, etc.) and metadata + + Args: + content: The content to extract and process into a document. 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_tag: Optional tag this document 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 + documents. + + container_tags: (DEPRECATED: Use containerTag instead) Optional tags this document 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 documents. + + custom_id: Optional custom ID of the document. This could be an ID from your database that + will uniquely identify this document. + + file_type: + Optional file type override to force specific processing behavior. Valid values: + text, pdf, tweet, google_doc, google_slide, google_sheet, image, video, + notion_doc, webpage, onedrive + + metadata: Optional metadata for the document. This is used to store additional information + about the document. You can use this to store any additional information you + need about the document. Metadata can be filtered through. Keys must be strings + and are case sensitive. Values can be strings, numbers, or booleans. You cannot + nest objects. + + mime_type: Required when fileType is 'image' or 'video'. Specifies the exact MIME type to + use (e.g., 'image/png', 'image/jpeg', 'video/mp4', 'video/webm') + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/v3/documents", + body=maybe_transform( + { + "content": content, + "container_tag": container_tag, + "container_tags": container_tags, + "custom_id": custom_id, + "file_type": file_type, + "metadata": metadata, + "mime_type": mime_type, + }, + document_add_params.DocumentAddParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=DocumentAddResponse, + ) + + def get( + self, + id: str, + *, + # 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, + ) -> DocumentGetResponse: + """ + Get a document by ID + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._get( + f"/v3/documents/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=DocumentGetResponse, + ) + + def upload_file( + self, + *, + file: FileTypes, + container_tags: str | Omit = omit, + file_type: str | Omit = omit, + mime_type: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> DocumentUploadFileResponse: + """ + Upload a file to be processed + + Args: + file: File to upload and process + + container_tags: Optional JSON string of container tags array. This can be an ID for your user, a + project ID, or any other identifier you wish to use to group documents. + + file_type: + Optional file type override to force specific processing behavior. Valid values: + text, pdf, tweet, google_doc, google_slide, google_sheet, image, video, + notion_doc, webpage, onedrive + + mime_type: Required when fileType is 'image' or 'video'. Specifies the exact MIME type to + use (e.g., 'image/png', 'image/jpeg', 'video/mp4', 'video/webm') + + 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, + "container_tags": container_tags, + "file_type": file_type, + "mime_type": mime_type, + } + ) + 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/documents/file", + body=maybe_transform(body, document_upload_file_params.DocumentUploadFileParams), + files=files, + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=DocumentUploadFileResponse, + ) + + +class AsyncDocumentsResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncDocumentsResourceWithRawResponse: + """ + 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 AsyncDocumentsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncDocumentsResourceWithStreamingResponse: + """ + 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 AsyncDocumentsResourceWithStreamingResponse(self) + + async def update( + self, + id: str, + *, + container_tag: str | Omit = omit, + container_tags: SequenceNotStr[str] | Omit = omit, + content: str | Omit = omit, + custom_id: str | Omit = omit, + file_type: str | Omit = omit, + metadata: Dict[str, Union[str, float, bool, SequenceNotStr[str]]] | Omit = omit, + mime_type: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> DocumentUpdateResponse: + """ + Update a document with any content type (text, url, file, etc.) and metadata + + Args: + container_tag: Optional tag this document 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 + documents. + + container_tags: (DEPRECATED: Use containerTag instead) Optional tags this document 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 documents. + + content: The content to extract and process into a document. 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. + + custom_id: Optional custom ID of the document. This could be an ID from your database that + will uniquely identify this document. + + file_type: + Optional file type override to force specific processing behavior. Valid values: + text, pdf, tweet, google_doc, google_slide, google_sheet, image, video, + notion_doc, webpage, onedrive + + metadata: Optional metadata for the document. This is used to store additional information + about the document. You can use this to store any additional information you + need about the document. Metadata can be filtered through. Keys must be strings + and are case sensitive. Values can be strings, numbers, or booleans. You cannot + nest objects. + + mime_type: Required when fileType is 'image' or 'video'. Specifies the exact MIME type to + use (e.g., 'image/png', 'image/jpeg', 'video/mp4', 'video/webm') + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._patch( + f"/v3/documents/{id}", + body=await async_maybe_transform( + { + "container_tag": container_tag, + "container_tags": container_tags, + "content": content, + "custom_id": custom_id, + "file_type": file_type, + "metadata": metadata, + "mime_type": mime_type, + }, + document_update_params.DocumentUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=DocumentUpdateResponse, + ) + + async def list( + self, + *, + container_tags: SequenceNotStr[str] | Omit = omit, + filters: document_list_params.Filters | Omit = omit, + include_content: bool | Omit = omit, + limit: Union[str, float] | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + page: Union[str, float] | Omit = omit, + sort: Literal["createdAt", "updatedAt"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> DocumentListResponse: + """ + Retrieves a paginated list of documents with their metadata and workflow status + + Args: + container_tags: Optional tags this document 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 + documents. + + filters: Optional filters to apply to the search. Can be a JSON string or Query object. + + include_content: Whether to include the content field in the response. Warning: This can make + responses significantly larger. + + 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._post( + "/v3/documents/list", + body=await async_maybe_transform( + { + "container_tags": container_tags, + "filters": filters, + "include_content": include_content, + "limit": limit, + "order": order, + "page": page, + "sort": sort, + }, + document_list_params.DocumentListParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=DocumentListResponse, + ) + + async def delete( + self, + id: str, + *, + # 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, + ) -> None: + """ + Delete a document by ID or customId + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not 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/documents/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + async def add( + self, + *, + content: str, + container_tag: str | Omit = omit, + container_tags: SequenceNotStr[str] | Omit = omit, + custom_id: str | Omit = omit, + file_type: str | Omit = omit, + metadata: Dict[str, Union[str, float, bool, SequenceNotStr[str]]] | Omit = omit, + mime_type: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> DocumentAddResponse: + """ + Add a document with any content type (text, url, file, etc.) and metadata + + Args: + content: The content to extract and process into a document. 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_tag: Optional tag this document 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 + documents. + + container_tags: (DEPRECATED: Use containerTag instead) Optional tags this document 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 documents. + + custom_id: Optional custom ID of the document. This could be an ID from your database that + will uniquely identify this document. + + file_type: + Optional file type override to force specific processing behavior. Valid values: + text, pdf, tweet, google_doc, google_slide, google_sheet, image, video, + notion_doc, webpage, onedrive + + metadata: Optional metadata for the document. This is used to store additional information + about the document. You can use this to store any additional information you + need about the document. Metadata can be filtered through. Keys must be strings + and are case sensitive. Values can be strings, numbers, or booleans. You cannot + nest objects. + + mime_type: Required when fileType is 'image' or 'video'. Specifies the exact MIME type to + use (e.g., 'image/png', 'image/jpeg', 'video/mp4', 'video/webm') + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/v3/documents", + body=await async_maybe_transform( + { + "content": content, + "container_tag": container_tag, + "container_tags": container_tags, + "custom_id": custom_id, + "file_type": file_type, + "metadata": metadata, + "mime_type": mime_type, + }, + document_add_params.DocumentAddParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=DocumentAddResponse, + ) + + async def get( + self, + id: str, + *, + # 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, + ) -> DocumentGetResponse: + """ + Get a document by ID + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._get( + f"/v3/documents/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=DocumentGetResponse, + ) + + async def upload_file( + self, + *, + file: FileTypes, + container_tags: str | Omit = omit, + file_type: str | Omit = omit, + mime_type: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> DocumentUploadFileResponse: + """ + Upload a file to be processed + + Args: + file: File to upload and process + + container_tags: Optional JSON string of container tags array. This can be an ID for your user, a + project ID, or any other identifier you wish to use to group documents. + + file_type: + Optional file type override to force specific processing behavior. Valid values: + text, pdf, tweet, google_doc, google_slide, google_sheet, image, video, + notion_doc, webpage, onedrive + + mime_type: Required when fileType is 'image' or 'video'. Specifies the exact MIME type to + use (e.g., 'image/png', 'image/jpeg', 'video/mp4', 'video/webm') + + 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, + "container_tags": container_tags, + "file_type": file_type, + "mime_type": mime_type, + } + ) + 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/documents/file", + body=await async_maybe_transform(body, document_upload_file_params.DocumentUploadFileParams), + files=files, + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=DocumentUploadFileResponse, + ) + + +class DocumentsResourceWithRawResponse: + def __init__(self, documents: DocumentsResource) -> None: + self._documents = documents + + self.update = to_raw_response_wrapper( + documents.update, + ) + self.list = to_raw_response_wrapper( + documents.list, + ) + self.delete = to_raw_response_wrapper( + documents.delete, + ) + self.add = to_raw_response_wrapper( + documents.add, + ) + self.get = to_raw_response_wrapper( + documents.get, + ) + self.upload_file = to_raw_response_wrapper( + documents.upload_file, + ) + + +class AsyncDocumentsResourceWithRawResponse: + def __init__(self, documents: AsyncDocumentsResource) -> None: + self._documents = documents + + self.update = async_to_raw_response_wrapper( + documents.update, + ) + self.list = async_to_raw_response_wrapper( + documents.list, + ) + self.delete = async_to_raw_response_wrapper( + documents.delete, + ) + self.add = async_to_raw_response_wrapper( + documents.add, + ) + self.get = async_to_raw_response_wrapper( + documents.get, + ) + self.upload_file = async_to_raw_response_wrapper( + documents.upload_file, + ) + + +class DocumentsResourceWithStreamingResponse: + def __init__(self, documents: DocumentsResource) -> None: + self._documents = documents + + self.update = to_streamed_response_wrapper( + documents.update, + ) + self.list = to_streamed_response_wrapper( + documents.list, + ) + self.delete = to_streamed_response_wrapper( + documents.delete, + ) + self.add = to_streamed_response_wrapper( + documents.add, + ) + self.get = to_streamed_response_wrapper( + documents.get, + ) + self.upload_file = to_streamed_response_wrapper( + documents.upload_file, + ) + + +class AsyncDocumentsResourceWithStreamingResponse: + def __init__(self, documents: AsyncDocumentsResource) -> None: + self._documents = documents + + self.update = async_to_streamed_response_wrapper( + documents.update, + ) + self.list = async_to_streamed_response_wrapper( + documents.list, + ) + self.delete = async_to_streamed_response_wrapper( + documents.delete, + ) + self.add = async_to_streamed_response_wrapper( + documents.add, + ) + self.get = async_to_streamed_response_wrapper( + documents.get, + ) + self.upload_file = async_to_streamed_response_wrapper( + documents.upload_file, + ) diff --git a/src/supermemory/resources/memories.py b/src/supermemory/resources/memories.py new file mode 100644 index 00000000..36689baf --- /dev/null +++ b/src/supermemory/resources/memories.py @@ -0,0 +1,894 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union, Mapping, cast +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 Body, Omit, Query, Headers, NoneType, NotGiven, FileTypes, SequenceNotStr, omit, not_given +from .._utils import extract_files, maybe_transform, deepcopy_minimal, async_maybe_transform +from .._compat import cached_property +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.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 +from ..types.memory_upload_file_response import MemoryUploadFileResponse + +__all__ = ["MemoriesResource", "AsyncMemoriesResource"] + + +class MemoriesResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> MemoriesResourceWithRawResponse: + """ + 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 MemoriesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> MemoriesResourceWithStreamingResponse: + """ + 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 MemoriesResourceWithStreamingResponse(self) + + def update( + self, + id: str, + *, + container_tag: str | Omit = omit, + container_tags: SequenceNotStr[str] | Omit = omit, + content: str | Omit = omit, + custom_id: str | Omit = omit, + file_type: str | Omit = omit, + metadata: Dict[str, Union[str, float, bool, SequenceNotStr[str]]] | Omit = omit, + mime_type: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> MemoryUpdateResponse: + """ + Update a document with any content type (text, url, file, etc.) and metadata + + Args: + container_tag: Optional tag this document 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 + documents. + + container_tags: (DEPRECATED: Use containerTag instead) Optional tags this document 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 documents. + + content: The content to extract and process into a document. 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. + + custom_id: Optional custom ID of the document. This could be an ID from your database that + will uniquely identify this document. + + file_type: + Optional file type override to force specific processing behavior. Valid values: + text, pdf, tweet, google_doc, google_slide, google_sheet, image, video, + notion_doc, webpage, onedrive + + metadata: Optional metadata for the document. This is used to store additional information + about the document. You can use this to store any additional information you + need about the document. Metadata can be filtered through. Keys must be strings + and are case sensitive. Values can be strings, numbers, or booleans. You cannot + nest objects. + + mime_type: Required when fileType is 'image' or 'video'. Specifies the exact MIME type to + use (e.g., 'image/png', 'image/jpeg', 'video/mp4', 'video/webm') + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._patch( + f"/v3/documents/{id}", + body=maybe_transform( + { + "container_tag": container_tag, + "container_tags": container_tags, + "content": content, + "custom_id": custom_id, + "file_type": file_type, + "metadata": metadata, + "mime_type": mime_type, + }, + memory_update_params.MemoryUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=MemoryUpdateResponse, + ) + + def list( + self, + *, + container_tags: SequenceNotStr[str] | Omit = omit, + filters: memory_list_params.Filters | Omit = omit, + include_content: bool | Omit = omit, + limit: Union[str, float] | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + page: Union[str, float] | Omit = omit, + sort: Literal["createdAt", "updatedAt"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> MemoryListResponse: + """ + Retrieves a paginated list of documents with their metadata and workflow status + + Args: + container_tags: Optional tags this document 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 + documents. + + filters: Optional filters to apply to the search. Can be a JSON string or Query object. + + include_content: Whether to include the content field in the response. Warning: This can make + responses significantly larger. + + 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._post( + "/v3/documents/list", + body=maybe_transform( + { + "container_tags": container_tags, + "filters": filters, + "include_content": include_content, + "limit": limit, + "order": order, + "page": page, + "sort": sort, + }, + memory_list_params.MemoryListParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=MemoryListResponse, + ) + + def delete( + self, + id: str, + *, + # 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, + ) -> None: + """ + Delete a document by ID or customId + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not 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/documents/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + def add( + self, + *, + content: str, + container_tag: str | Omit = omit, + container_tags: SequenceNotStr[str] | Omit = omit, + custom_id: str | Omit = omit, + file_type: str | Omit = omit, + metadata: Dict[str, Union[str, float, bool, SequenceNotStr[str]]] | Omit = omit, + mime_type: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> MemoryAddResponse: + """ + Add a document with any content type (text, url, file, etc.) and metadata + + Args: + content: The content to extract and process into a document. 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_tag: Optional tag this document 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 + documents. + + container_tags: (DEPRECATED: Use containerTag instead) Optional tags this document 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 documents. + + custom_id: Optional custom ID of the document. This could be an ID from your database that + will uniquely identify this document. + + file_type: + Optional file type override to force specific processing behavior. Valid values: + text, pdf, tweet, google_doc, google_slide, google_sheet, image, video, + notion_doc, webpage, onedrive + + metadata: Optional metadata for the document. This is used to store additional information + about the document. You can use this to store any additional information you + need about the document. Metadata can be filtered through. Keys must be strings + and are case sensitive. Values can be strings, numbers, or booleans. You cannot + nest objects. + + mime_type: Required when fileType is 'image' or 'video'. Specifies the exact MIME type to + use (e.g., 'image/png', 'image/jpeg', 'video/mp4', 'video/webm') + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/v3/documents", + body=maybe_transform( + { + "content": content, + "container_tag": container_tag, + "container_tags": container_tags, + "custom_id": custom_id, + "file_type": file_type, + "metadata": metadata, + "mime_type": mime_type, + }, + memory_add_params.MemoryAddParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=MemoryAddResponse, + ) + + def get( + self, + id: str, + *, + # 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, + ) -> MemoryGetResponse: + """ + Get a document by ID + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._get( + f"/v3/documents/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=MemoryGetResponse, + ) + + def upload_file( + self, + *, + file: FileTypes, + container_tags: str | Omit = omit, + file_type: str | Omit = omit, + mime_type: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> MemoryUploadFileResponse: + """ + Upload a file to be processed + + Args: + file: File to upload and process + + container_tags: Optional JSON string of container tags array. This can be an ID for your user, a + project ID, or any other identifier you wish to use to group documents. + + file_type: + Optional file type override to force specific processing behavior. Valid values: + text, pdf, tweet, google_doc, google_slide, google_sheet, image, video, + notion_doc, webpage, onedrive + + mime_type: Required when fileType is 'image' or 'video'. Specifies the exact MIME type to + use (e.g., 'image/png', 'image/jpeg', 'video/mp4', 'video/webm') + + 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, + "container_tags": container_tags, + "file_type": file_type, + "mime_type": mime_type, + } + ) + 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/documents/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 + def with_raw_response(self) -> AsyncMemoriesResourceWithRawResponse: + """ + 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 AsyncMemoriesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncMemoriesResourceWithStreamingResponse: + """ + 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 AsyncMemoriesResourceWithStreamingResponse(self) + + async def update( + self, + id: str, + *, + container_tag: str | Omit = omit, + container_tags: SequenceNotStr[str] | Omit = omit, + content: str | Omit = omit, + custom_id: str | Omit = omit, + file_type: str | Omit = omit, + metadata: Dict[str, Union[str, float, bool, SequenceNotStr[str]]] | Omit = omit, + mime_type: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> MemoryUpdateResponse: + """ + Update a document with any content type (text, url, file, etc.) and metadata + + Args: + container_tag: Optional tag this document 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 + documents. + + container_tags: (DEPRECATED: Use containerTag instead) Optional tags this document 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 documents. + + content: The content to extract and process into a document. 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. + + custom_id: Optional custom ID of the document. This could be an ID from your database that + will uniquely identify this document. + + file_type: + Optional file type override to force specific processing behavior. Valid values: + text, pdf, tweet, google_doc, google_slide, google_sheet, image, video, + notion_doc, webpage, onedrive + + metadata: Optional metadata for the document. This is used to store additional information + about the document. You can use this to store any additional information you + need about the document. Metadata can be filtered through. Keys must be strings + and are case sensitive. Values can be strings, numbers, or booleans. You cannot + nest objects. + + mime_type: Required when fileType is 'image' or 'video'. Specifies the exact MIME type to + use (e.g., 'image/png', 'image/jpeg', 'video/mp4', 'video/webm') + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._patch( + f"/v3/documents/{id}", + body=await async_maybe_transform( + { + "container_tag": container_tag, + "container_tags": container_tags, + "content": content, + "custom_id": custom_id, + "file_type": file_type, + "metadata": metadata, + "mime_type": mime_type, + }, + memory_update_params.MemoryUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=MemoryUpdateResponse, + ) + + async def list( + self, + *, + container_tags: SequenceNotStr[str] | Omit = omit, + filters: memory_list_params.Filters | Omit = omit, + include_content: bool | Omit = omit, + limit: Union[str, float] | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + page: Union[str, float] | Omit = omit, + sort: Literal["createdAt", "updatedAt"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> MemoryListResponse: + """ + Retrieves a paginated list of documents with their metadata and workflow status + + Args: + container_tags: Optional tags this document 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 + documents. + + filters: Optional filters to apply to the search. Can be a JSON string or Query object. + + include_content: Whether to include the content field in the response. Warning: This can make + responses significantly larger. + + 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._post( + "/v3/documents/list", + body=await async_maybe_transform( + { + "container_tags": container_tags, + "filters": filters, + "include_content": include_content, + "limit": limit, + "order": order, + "page": page, + "sort": sort, + }, + memory_list_params.MemoryListParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=MemoryListResponse, + ) + + async def delete( + self, + id: str, + *, + # 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, + ) -> None: + """ + Delete a document by ID or customId + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not 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/documents/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + async def add( + self, + *, + content: str, + container_tag: str | Omit = omit, + container_tags: SequenceNotStr[str] | Omit = omit, + custom_id: str | Omit = omit, + file_type: str | Omit = omit, + metadata: Dict[str, Union[str, float, bool, SequenceNotStr[str]]] | Omit = omit, + mime_type: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> MemoryAddResponse: + """ + Add a document with any content type (text, url, file, etc.) and metadata + + Args: + content: The content to extract and process into a document. 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_tag: Optional tag this document 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 + documents. + + container_tags: (DEPRECATED: Use containerTag instead) Optional tags this document 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 documents. + + custom_id: Optional custom ID of the document. This could be an ID from your database that + will uniquely identify this document. + + file_type: + Optional file type override to force specific processing behavior. Valid values: + text, pdf, tweet, google_doc, google_slide, google_sheet, image, video, + notion_doc, webpage, onedrive + + metadata: Optional metadata for the document. This is used to store additional information + about the document. You can use this to store any additional information you + need about the document. Metadata can be filtered through. Keys must be strings + and are case sensitive. Values can be strings, numbers, or booleans. You cannot + nest objects. + + mime_type: Required when fileType is 'image' or 'video'. Specifies the exact MIME type to + use (e.g., 'image/png', 'image/jpeg', 'video/mp4', 'video/webm') + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/v3/documents", + body=await async_maybe_transform( + { + "content": content, + "container_tag": container_tag, + "container_tags": container_tags, + "custom_id": custom_id, + "file_type": file_type, + "metadata": metadata, + "mime_type": mime_type, + }, + memory_add_params.MemoryAddParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=MemoryAddResponse, + ) + + async def get( + self, + id: str, + *, + # 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, + ) -> MemoryGetResponse: + """ + Get a document by ID + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._get( + f"/v3/documents/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=MemoryGetResponse, + ) + + async def upload_file( + self, + *, + file: FileTypes, + container_tags: str | Omit = omit, + file_type: str | Omit = omit, + mime_type: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> MemoryUploadFileResponse: + """ + Upload a file to be processed + + Args: + file: File to upload and process + + container_tags: Optional JSON string of container tags array. This can be an ID for your user, a + project ID, or any other identifier you wish to use to group documents. + + file_type: + Optional file type override to force specific processing behavior. Valid values: + text, pdf, tweet, google_doc, google_slide, google_sheet, image, video, + notion_doc, webpage, onedrive + + mime_type: Required when fileType is 'image' or 'video'. Specifies the exact MIME type to + use (e.g., 'image/png', 'image/jpeg', 'video/mp4', 'video/webm') + + 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, + "container_tags": container_tags, + "file_type": file_type, + "mime_type": mime_type, + } + ) + 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/documents/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: + self._memories = memories + + self.update = to_raw_response_wrapper( + memories.update, + ) + self.list = to_raw_response_wrapper( + memories.list, + ) + self.delete = to_raw_response_wrapper( + memories.delete, + ) + self.add = to_raw_response_wrapper( + memories.add, + ) + self.get = to_raw_response_wrapper( + memories.get, + ) + self.upload_file = to_raw_response_wrapper( + memories.upload_file, + ) + + +class AsyncMemoriesResourceWithRawResponse: + def __init__(self, memories: AsyncMemoriesResource) -> None: + self._memories = memories + + self.update = async_to_raw_response_wrapper( + memories.update, + ) + self.list = async_to_raw_response_wrapper( + memories.list, + ) + self.delete = async_to_raw_response_wrapper( + memories.delete, + ) + self.add = async_to_raw_response_wrapper( + memories.add, + ) + self.get = async_to_raw_response_wrapper( + memories.get, + ) + self.upload_file = async_to_raw_response_wrapper( + memories.upload_file, + ) + + +class MemoriesResourceWithStreamingResponse: + def __init__(self, memories: MemoriesResource) -> None: + self._memories = memories + + self.update = to_streamed_response_wrapper( + memories.update, + ) + self.list = to_streamed_response_wrapper( + memories.list, + ) + self.delete = to_streamed_response_wrapper( + memories.delete, + ) + self.add = to_streamed_response_wrapper( + memories.add, + ) + self.get = to_streamed_response_wrapper( + memories.get, + ) + self.upload_file = to_streamed_response_wrapper( + memories.upload_file, + ) + + +class AsyncMemoriesResourceWithStreamingResponse: + def __init__(self, memories: AsyncMemoriesResource) -> None: + self._memories = memories + + self.update = async_to_streamed_response_wrapper( + memories.update, + ) + self.list = async_to_streamed_response_wrapper( + memories.list, + ) + self.delete = async_to_streamed_response_wrapper( + memories.delete, + ) + self.add = async_to_streamed_response_wrapper( + memories.add, + ) + 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/resources/search.py b/src/supermemory/resources/search.py index 6d9eecb4..15867142 100644 --- a/src/supermemory/resources/search.py +++ b/src/supermemory/resources/search.py @@ -8,7 +8,7 @@ import httpx from ..types import search_execute_params, search_memories_params, search_documents_params -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven, SequenceNotStr +from .._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given from .._utils import maybe_transform, async_maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource @@ -50,24 +50,24 @@ def documents( self, *, q: str, - categories_filter: List[Literal["technology", "science", "business", "health"]] | NotGiven = NOT_GIVEN, - chunk_threshold: float | NotGiven = NOT_GIVEN, - container_tags: SequenceNotStr[str] | NotGiven = NOT_GIVEN, - doc_id: str | NotGiven = NOT_GIVEN, - document_threshold: float | NotGiven = NOT_GIVEN, - filters: search_documents_params.Filters | NotGiven = NOT_GIVEN, - include_full_docs: bool | 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, + categories_filter: List[Literal["technology", "science", "business", "health"]] | Omit = omit, + chunk_threshold: float | Omit = omit, + container_tags: SequenceNotStr[str] | Omit = omit, + doc_id: str | Omit = omit, + document_threshold: float | Omit = omit, + filters: search_documents_params.Filters | Omit = omit, + include_full_docs: bool | Omit = omit, + include_summary: bool | Omit = omit, + limit: int | Omit = omit, + only_matching_chunks: bool | Omit = omit, + rerank: bool | Omit = omit, + rewrite_query: bool | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SearchDocumentsResponse: """ Search memories with advanced filtering @@ -91,7 +91,7 @@ def documents( most documents, more results), 1 is most sensitive (returns lesser documents, accurate results) - filters: Optional filters to apply to the search + filters: Optional filters to apply to the search. Can be a JSON string or Query object. include_full_docs: If true, include full document in the response. This is helpful if you want a chatbot to know the full context of the document. @@ -149,24 +149,24 @@ def execute( self, *, q: str, - categories_filter: List[Literal["technology", "science", "business", "health"]] | NotGiven = NOT_GIVEN, - chunk_threshold: float | NotGiven = NOT_GIVEN, - container_tags: SequenceNotStr[str] | NotGiven = NOT_GIVEN, - doc_id: str | NotGiven = NOT_GIVEN, - document_threshold: float | NotGiven = NOT_GIVEN, - filters: search_execute_params.Filters | NotGiven = NOT_GIVEN, - include_full_docs: bool | 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, + categories_filter: List[Literal["technology", "science", "business", "health"]] | Omit = omit, + chunk_threshold: float | Omit = omit, + container_tags: SequenceNotStr[str] | Omit = omit, + doc_id: str | Omit = omit, + document_threshold: float | Omit = omit, + filters: search_execute_params.Filters | Omit = omit, + include_full_docs: bool | Omit = omit, + include_summary: bool | Omit = omit, + limit: int | Omit = omit, + only_matching_chunks: bool | Omit = omit, + rerank: bool | Omit = omit, + rewrite_query: bool | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SearchExecuteResponse: """ Search memories with advanced filtering @@ -190,7 +190,7 @@ def execute( most documents, more results), 1 is most sensitive (returns lesser documents, accurate results) - filters: Optional filters to apply to the search + filters: Optional filters to apply to the search. Can be a JSON string or Query object. include_full_docs: If true, include full document in the response. This is helpful if you want a chatbot to know the full context of the document. @@ -248,19 +248,19 @@ def memories( self, *, q: str, - container_tag: str | NotGiven = NOT_GIVEN, - filters: search_memories_params.Filters | NotGiven = NOT_GIVEN, - include: search_memories_params.Include | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - rerank: bool | NotGiven = NOT_GIVEN, - rewrite_query: bool | NotGiven = NOT_GIVEN, - threshold: float | NotGiven = NOT_GIVEN, + container_tag: str | Omit = omit, + filters: search_memories_params.Filters | Omit = omit, + include: search_memories_params.Include | Omit = omit, + limit: int | Omit = omit, + rerank: bool | Omit = omit, + rewrite_query: bool | Omit = omit, + threshold: float | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SearchMemoriesResponse: """ Search memory entries - Low latency for conversational @@ -271,7 +271,7 @@ def memories( container_tag: Optional tag this search should be containerized by. This can be an ID for your user, a project ID, or any other identifier you wish to use to filter memories. - filters: Optional filters to apply to the search + filters: Optional filters to apply to the search. Can be a JSON string or Query object. limit: Maximum number of results to return @@ -339,24 +339,24 @@ async def documents( self, *, q: str, - categories_filter: List[Literal["technology", "science", "business", "health"]] | NotGiven = NOT_GIVEN, - chunk_threshold: float | NotGiven = NOT_GIVEN, - container_tags: SequenceNotStr[str] | NotGiven = NOT_GIVEN, - doc_id: str | NotGiven = NOT_GIVEN, - document_threshold: float | NotGiven = NOT_GIVEN, - filters: search_documents_params.Filters | NotGiven = NOT_GIVEN, - include_full_docs: bool | 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, + categories_filter: List[Literal["technology", "science", "business", "health"]] | Omit = omit, + chunk_threshold: float | Omit = omit, + container_tags: SequenceNotStr[str] | Omit = omit, + doc_id: str | Omit = omit, + document_threshold: float | Omit = omit, + filters: search_documents_params.Filters | Omit = omit, + include_full_docs: bool | Omit = omit, + include_summary: bool | Omit = omit, + limit: int | Omit = omit, + only_matching_chunks: bool | Omit = omit, + rerank: bool | Omit = omit, + rewrite_query: bool | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SearchDocumentsResponse: """ Search memories with advanced filtering @@ -380,7 +380,7 @@ async def documents( most documents, more results), 1 is most sensitive (returns lesser documents, accurate results) - filters: Optional filters to apply to the search + filters: Optional filters to apply to the search. Can be a JSON string or Query object. include_full_docs: If true, include full document in the response. This is helpful if you want a chatbot to know the full context of the document. @@ -438,24 +438,24 @@ async def execute( self, *, q: str, - categories_filter: List[Literal["technology", "science", "business", "health"]] | NotGiven = NOT_GIVEN, - chunk_threshold: float | NotGiven = NOT_GIVEN, - container_tags: SequenceNotStr[str] | NotGiven = NOT_GIVEN, - doc_id: str | NotGiven = NOT_GIVEN, - document_threshold: float | NotGiven = NOT_GIVEN, - filters: search_execute_params.Filters | NotGiven = NOT_GIVEN, - include_full_docs: bool | 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, + categories_filter: List[Literal["technology", "science", "business", "health"]] | Omit = omit, + chunk_threshold: float | Omit = omit, + container_tags: SequenceNotStr[str] | Omit = omit, + doc_id: str | Omit = omit, + document_threshold: float | Omit = omit, + filters: search_execute_params.Filters | Omit = omit, + include_full_docs: bool | Omit = omit, + include_summary: bool | Omit = omit, + limit: int | Omit = omit, + only_matching_chunks: bool | Omit = omit, + rerank: bool | Omit = omit, + rewrite_query: bool | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SearchExecuteResponse: """ Search memories with advanced filtering @@ -479,7 +479,7 @@ async def execute( most documents, more results), 1 is most sensitive (returns lesser documents, accurate results) - filters: Optional filters to apply to the search + filters: Optional filters to apply to the search. Can be a JSON string or Query object. include_full_docs: If true, include full document in the response. This is helpful if you want a chatbot to know the full context of the document. @@ -537,19 +537,19 @@ async def memories( self, *, q: str, - container_tag: str | NotGiven = NOT_GIVEN, - filters: search_memories_params.Filters | NotGiven = NOT_GIVEN, - include: search_memories_params.Include | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - rerank: bool | NotGiven = NOT_GIVEN, - rewrite_query: bool | NotGiven = NOT_GIVEN, - threshold: float | NotGiven = NOT_GIVEN, + container_tag: str | Omit = omit, + filters: search_memories_params.Filters | Omit = omit, + include: search_memories_params.Include | Omit = omit, + limit: int | Omit = omit, + rerank: bool | Omit = omit, + rewrite_query: bool | Omit = omit, + threshold: float | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SearchMemoriesResponse: """ Search memory entries - Low latency for conversational @@ -560,7 +560,7 @@ async def memories( container_tag: Optional tag this search should be containerized by. This can be an ID for your user, a project ID, or any other identifier you wish to use to filter memories. - filters: Optional filters to apply to the search + filters: Optional filters to apply to the search. Can be a JSON string or Query object. limit: Maximum number of results to return diff --git a/src/supermemory/resources/settings.py b/src/supermemory/resources/settings.py index 26db07d7..ff223f03 100644 --- a/src/supermemory/resources/settings.py +++ b/src/supermemory/resources/settings.py @@ -7,7 +7,7 @@ import httpx from ..types import setting_update_params -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given from .._utils import maybe_transform, async_maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource @@ -47,25 +47,25 @@ def with_streaming_response(self) -> SettingsResourceWithStreamingResponse: def update( self, *, - exclude_items: Union[str, float, bool, Dict[str, object], Iterable[object], None] | NotGiven = NOT_GIVEN, - filter_prompt: Optional[str] | 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, + exclude_items: Union[str, float, bool, Dict[str, object], Iterable[object], None] | Omit = omit, + filter_prompt: Optional[str] | Omit = omit, + google_drive_client_id: Optional[str] | Omit = omit, + google_drive_client_secret: Optional[str] | Omit = omit, + google_drive_custom_key_enabled: Optional[bool] | Omit = omit, + include_items: Union[str, float, bool, Dict[str, object], Iterable[object], None] | Omit = omit, + notion_client_id: Optional[str] | Omit = omit, + notion_client_secret: Optional[str] | Omit = omit, + notion_custom_key_enabled: Optional[bool] | Omit = omit, + onedrive_client_id: Optional[str] | Omit = omit, + onedrive_client_secret: Optional[str] | Omit = omit, + onedrive_custom_key_enabled: Optional[bool] | Omit = omit, + should_llm_filter: Optional[bool] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SettingUpdateResponse: """ Update settings for an organization @@ -113,7 +113,7 @@ def get( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SettingGetResponse: """Get settings for an organization""" return self._get( @@ -148,25 +148,25 @@ def with_streaming_response(self) -> AsyncSettingsResourceWithStreamingResponse: async def update( self, *, - exclude_items: Union[str, float, bool, Dict[str, object], Iterable[object], None] | NotGiven = NOT_GIVEN, - filter_prompt: Optional[str] | 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, + exclude_items: Union[str, float, bool, Dict[str, object], Iterable[object], None] | Omit = omit, + filter_prompt: Optional[str] | Omit = omit, + google_drive_client_id: Optional[str] | Omit = omit, + google_drive_client_secret: Optional[str] | Omit = omit, + google_drive_custom_key_enabled: Optional[bool] | Omit = omit, + include_items: Union[str, float, bool, Dict[str, object], Iterable[object], None] | Omit = omit, + notion_client_id: Optional[str] | Omit = omit, + notion_client_secret: Optional[str] | Omit = omit, + notion_custom_key_enabled: Optional[bool] | Omit = omit, + onedrive_client_id: Optional[str] | Omit = omit, + onedrive_client_secret: Optional[str] | Omit = omit, + onedrive_custom_key_enabled: Optional[bool] | Omit = omit, + should_llm_filter: Optional[bool] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SettingUpdateResponse: """ Update settings for an organization @@ -214,7 +214,7 @@ async def get( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SettingGetResponse: """Get settings for an organization""" return await self._get( diff --git a/src/supermemory/types/__init__.py b/src/supermemory/types/__init__.py index 6a34d7ba..beace710 100644 --- a/src/supermemory/types/__init__.py +++ b/src/supermemory/types/__init__.py @@ -2,10 +2,23 @@ from __future__ import annotations +from .memory_add_params import MemoryAddParams as MemoryAddParams +from .memory_list_params import MemoryListParams as MemoryListParams +from .document_add_params import DocumentAddParams as DocumentAddParams +from .memory_add_response import MemoryAddResponse as MemoryAddResponse +from .memory_get_response import MemoryGetResponse as MemoryGetResponse +from .document_list_params import DocumentListParams as DocumentListParams +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 .document_add_response import DocumentAddResponse as DocumentAddResponse +from .document_get_response import DocumentGetResponse as DocumentGetResponse 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 .document_list_response import DocumentListResponse as DocumentListResponse +from .document_update_params import DocumentUpdateParams as DocumentUpdateParams +from .memory_update_response import MemoryUpdateResponse as MemoryUpdateResponse from .search_memories_params import SearchMemoriesParams as SearchMemoriesParams from .search_documents_params import SearchDocumentsParams as SearchDocumentsParams from .search_execute_response import SearchExecuteResponse as SearchExecuteResponse @@ -13,12 +26,17 @@ from .connection_create_params import ConnectionCreateParams as ConnectionCreateParams from .connection_import_params import ConnectionImportParams as ConnectionImportParams from .connection_list_response import ConnectionListResponse as ConnectionListResponse +from .document_update_response import DocumentUpdateResponse as DocumentUpdateResponse from .search_memories_response import SearchMemoriesResponse as SearchMemoriesResponse +from .memory_upload_file_params import MemoryUploadFileParams as MemoryUploadFileParams from .search_documents_response import SearchDocumentsResponse as SearchDocumentsResponse from .connection_create_response import ConnectionCreateResponse as ConnectionCreateResponse from .connection_import_response import ConnectionImportResponse as ConnectionImportResponse +from .document_upload_file_params import DocumentUploadFileParams as DocumentUploadFileParams +from .memory_upload_file_response import MemoryUploadFileResponse as MemoryUploadFileResponse from .connection_get_by_id_response import ConnectionGetByIDResponse as ConnectionGetByIDResponse from .connection_get_by_tags_params import ConnectionGetByTagsParams as ConnectionGetByTagsParams +from .document_upload_file_response import DocumentUploadFileResponse as DocumentUploadFileResponse from .connection_get_by_tags_response import ConnectionGetByTagsResponse as ConnectionGetByTagsResponse from .connection_delete_by_id_response import ConnectionDeleteByIDResponse as ConnectionDeleteByIDResponse from .connection_list_documents_params import ConnectionListDocumentsParams as ConnectionListDocumentsParams diff --git a/src/supermemory/types/document_add_params.py b/src/supermemory/types/document_add_params.py new file mode 100644 index 00000000..c07a50f5 --- /dev/null +++ b/src/supermemory/types/document_add_params.py @@ -0,0 +1,69 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union +from typing_extensions import Required, Annotated, TypedDict + +from .._types import SequenceNotStr +from .._utils import PropertyInfo + +__all__ = ["DocumentAddParams"] + + +class DocumentAddParams(TypedDict, total=False): + content: Required[str] + """The content to extract and process into a document. + + 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_tag: Annotated[str, PropertyInfo(alias="containerTag")] + """Optional tag this document 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 documents. + """ + + container_tags: Annotated[SequenceNotStr[str], PropertyInfo(alias="containerTags")] + """ + (DEPRECATED: Use containerTag instead) Optional tags this document 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 documents. + """ + + custom_id: Annotated[str, PropertyInfo(alias="customId")] + """Optional custom ID of the document. + + This could be an ID from your database that will uniquely identify this + document. + """ + + file_type: Annotated[str, PropertyInfo(alias="fileType")] + """Optional file type override to force specific processing behavior. + + Valid values: text, pdf, tweet, google_doc, google_slide, google_sheet, image, + video, notion_doc, webpage, onedrive + """ + + metadata: Dict[str, Union[str, float, bool, SequenceNotStr[str]]] + """Optional metadata for the document. + + This is used to store additional information about the document. You can use + this to store any additional information you need about the document. Metadata + can be filtered through. Keys must be strings and are case sensitive. Values can + be strings, numbers, or booleans. You cannot nest objects. + """ + + mime_type: Annotated[str, PropertyInfo(alias="mimeType")] + """Required when fileType is 'image' or 'video'. + + Specifies the exact MIME type to use (e.g., 'image/png', 'image/jpeg', + 'video/mp4', 'video/webm') + """ diff --git a/src/supermemory/types/document_add_response.py b/src/supermemory/types/document_add_response.py new file mode 100644 index 00000000..5a123794 --- /dev/null +++ b/src/supermemory/types/document_add_response.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .._models import BaseModel + +__all__ = ["DocumentAddResponse"] + + +class DocumentAddResponse(BaseModel): + id: str + + status: str diff --git a/src/supermemory/types/document_get_response.py b/src/supermemory/types/document_get_response.py new file mode 100644 index 00000000..f812b5a8 --- /dev/null +++ b/src/supermemory/types/document_get_response.py @@ -0,0 +1,104 @@ +# 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__ = ["DocumentGetResponse"] + + +class DocumentGetResponse(BaseModel): + id: str + """Unique identifier of the document.""" + + connection_id: Optional[str] = FieldInfo(alias="connectionId", default=None) + """Optional ID of connection the document was created from. + + This is useful for identifying the source of the document. + """ + + content: Optional[str] = None + """The content to extract and process into a document. + + 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""" + + custom_id: Optional[str] = FieldInfo(alias="customId", default=None) + """Optional custom ID of the document. + + This could be an ID from your database that will uniquely identify this + document. + """ + + metadata: Union[str, float, bool, Dict[str, object], List[object], None] = None + """Optional metadata for the document. + + This is used to store additional information about the document. You can use + this to store any additional information you need about the document. Metadata + can be filtered through. Keys must be strings and are case sensitive. Values can + be strings, numbers, or booleans. You cannot nest objects. + """ + + og_image: Optional[str] = FieldInfo(alias="ogImage", default=None) + + source: Optional[str] = None + """Source of the document""" + + status: Literal["unknown", "queued", "extracting", "chunking", "embedding", "indexing", "done", "failed"] + """Status of the document""" + + summary: Optional[str] = None + """Summary of the document content""" + + summary_embedding_model: Optional[str] = FieldInfo(alias="summaryEmbeddingModel", default=None) + + summary_embedding_model_new: Optional[str] = FieldInfo(alias="summaryEmbeddingModelNew", default=None) + + summary_embedding_new: Optional[List[float]] = FieldInfo(alias="summaryEmbeddingNew", default=None) + + title: Optional[str] = None + """Title of the document""" + + type: Literal[ + "text", + "pdf", + "tweet", + "google_doc", + "google_slide", + "google_sheet", + "image", + "video", + "notion_doc", + "webpage", + "onedrive", + ] + """Type of the document""" + + updated_at: datetime = FieldInfo(alias="updatedAt") + """Last update timestamp""" + + container_tags: Optional[List[str]] = FieldInfo(alias="containerTags", default=None) + """Optional tags this document 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 documents. + """ + + raw: None = None + """Raw content of the document""" + + url: Optional[str] = None + """URL of the document""" diff --git a/src/supermemory/types/document_list_params.py b/src/supermemory/types/document_list_params.py new file mode 100644 index 00000000..4ce7914e --- /dev/null +++ b/src/supermemory/types/document_list_params.py @@ -0,0 +1,111 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable +from typing_extensions import Literal, Required, Annotated, TypeAlias, TypedDict + +from .._types import SequenceNotStr +from .._utils import PropertyInfo + +__all__ = [ + "DocumentListParams", + "Filters", + "FiltersOr", + "FiltersOrOr", + "FiltersOrOrUnionMember0", + "FiltersOrOrOr", + "FiltersOrOrAnd", + "FiltersAnd", + "FiltersAndAnd", + "FiltersAndAndUnionMember0", + "FiltersAndAndOr", + "FiltersAndAndAnd", +] + + +class DocumentListParams(TypedDict, total=False): + container_tags: Annotated[SequenceNotStr[str], PropertyInfo(alias="containerTags")] + """Optional tags this document 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 documents. + """ + + filters: Filters + """Optional filters to apply to the search. Can be a JSON string or Query object.""" + + include_content: Annotated[bool, PropertyInfo(alias="includeContent")] + """Whether to include the content field in the response. + + Warning: This can make responses significantly larger. + """ + + 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""" + + +class FiltersOrOrUnionMember0(TypedDict, total=False): + key: Required[str] + + value: Required[str] + + filter_type: Annotated[Literal["metadata", "numeric", "array_contains"], PropertyInfo(alias="filterType")] + + negate: Union[bool, Literal["true", "false"]] + + numeric_operator: Annotated[Literal[">", "<", ">=", "<=", "="], PropertyInfo(alias="numericOperator")] + + +class FiltersOrOrOr(TypedDict, total=False): + or_: Required[Annotated[Iterable[object], PropertyInfo(alias="OR")]] + + +class FiltersOrOrAnd(TypedDict, total=False): + and_: Required[Annotated[Iterable[object], PropertyInfo(alias="AND")]] + + +FiltersOrOr: TypeAlias = Union[FiltersOrOrUnionMember0, FiltersOrOrOr, FiltersOrOrAnd] + + +class FiltersOr(TypedDict, total=False): + or_: Required[Annotated[Iterable[FiltersOrOr], PropertyInfo(alias="OR")]] + + +class FiltersAndAndUnionMember0(TypedDict, total=False): + key: Required[str] + + value: Required[str] + + filter_type: Annotated[Literal["metadata", "numeric", "array_contains"], PropertyInfo(alias="filterType")] + + negate: Union[bool, Literal["true", "false"]] + + numeric_operator: Annotated[Literal[">", "<", ">=", "<=", "="], PropertyInfo(alias="numericOperator")] + + +class FiltersAndAndOr(TypedDict, total=False): + or_: Required[Annotated[Iterable[object], PropertyInfo(alias="OR")]] + + +class FiltersAndAndAnd(TypedDict, total=False): + and_: Required[Annotated[Iterable[object], PropertyInfo(alias="AND")]] + + +FiltersAndAnd: TypeAlias = Union[FiltersAndAndUnionMember0, FiltersAndAndOr, FiltersAndAndAnd] + + +class FiltersAnd(TypedDict, total=False): + and_: Required[Annotated[Iterable[FiltersAndAnd], PropertyInfo(alias="AND")]] + + +Filters: TypeAlias = Union[FiltersOr, FiltersAnd] diff --git a/src/supermemory/types/document_list_response.py b/src/supermemory/types/document_list_response.py new file mode 100644 index 00000000..afb58f49 --- /dev/null +++ b/src/supermemory/types/document_list_response.py @@ -0,0 +1,95 @@ +# 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__ = ["DocumentListResponse", "Memory", "Pagination"] + + +class Memory(BaseModel): + id: str + """Unique identifier of the document.""" + + connection_id: Optional[str] = FieldInfo(alias="connectionId", default=None) + """Optional ID of connection the document was created from. + + This is useful for identifying the source of the document. + """ + + created_at: datetime = FieldInfo(alias="createdAt") + """Creation timestamp""" + + custom_id: Optional[str] = FieldInfo(alias="customId", default=None) + """Optional custom ID of the document. + + This could be an ID from your database that will uniquely identify this + document. + """ + + metadata: Union[str, float, bool, Dict[str, object], List[object], None] = None + """Optional metadata for the document. + + This is used to store additional information about the document. You can use + this to store any additional information you need about the document. 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 document""" + + summary: Optional[str] = None + """Summary of the document content""" + + title: Optional[str] = None + """Title of the document""" + + type: Literal[ + "text", + "pdf", + "tweet", + "google_doc", + "google_slide", + "google_sheet", + "image", + "video", + "notion_doc", + "webpage", + "onedrive", + ] + """Type of the document""" + + updated_at: datetime = FieldInfo(alias="updatedAt") + """Last update timestamp""" + + container_tags: Optional[List[str]] = FieldInfo(alias="containerTags", default=None) + """Optional tags this document 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 documents. + """ + + content: Optional[str] = None + """Content of the document (only included when includeContent=true)""" + + +class Pagination(BaseModel): + current_page: float = FieldInfo(alias="currentPage") + + limit: float + + total_items: float = FieldInfo(alias="totalItems") + + total_pages: float = FieldInfo(alias="totalPages") + + +class DocumentListResponse(BaseModel): + memories: List[Memory] + + pagination: Pagination + """Pagination metadata""" diff --git a/src/supermemory/types/document_update_params.py b/src/supermemory/types/document_update_params.py new file mode 100644 index 00000000..4b4b1218 --- /dev/null +++ b/src/supermemory/types/document_update_params.py @@ -0,0 +1,69 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union +from typing_extensions import Annotated, TypedDict + +from .._types import SequenceNotStr +from .._utils import PropertyInfo + +__all__ = ["DocumentUpdateParams"] + + +class DocumentUpdateParams(TypedDict, total=False): + container_tag: Annotated[str, PropertyInfo(alias="containerTag")] + """Optional tag this document 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 documents. + """ + + container_tags: Annotated[SequenceNotStr[str], PropertyInfo(alias="containerTags")] + """ + (DEPRECATED: Use containerTag instead) Optional tags this document 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 documents. + """ + + content: str + """The content to extract and process into a document. + + 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. + """ + + custom_id: Annotated[str, PropertyInfo(alias="customId")] + """Optional custom ID of the document. + + This could be an ID from your database that will uniquely identify this + document. + """ + + file_type: Annotated[str, PropertyInfo(alias="fileType")] + """Optional file type override to force specific processing behavior. + + Valid values: text, pdf, tweet, google_doc, google_slide, google_sheet, image, + video, notion_doc, webpage, onedrive + """ + + metadata: Dict[str, Union[str, float, bool, SequenceNotStr[str]]] + """Optional metadata for the document. + + This is used to store additional information about the document. You can use + this to store any additional information you need about the document. Metadata + can be filtered through. Keys must be strings and are case sensitive. Values can + be strings, numbers, or booleans. You cannot nest objects. + """ + + mime_type: Annotated[str, PropertyInfo(alias="mimeType")] + """Required when fileType is 'image' or 'video'. + + Specifies the exact MIME type to use (e.g., 'image/png', 'image/jpeg', + 'video/mp4', 'video/webm') + """ diff --git a/src/supermemory/types/document_update_response.py b/src/supermemory/types/document_update_response.py new file mode 100644 index 00000000..3b27289e --- /dev/null +++ b/src/supermemory/types/document_update_response.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .._models import BaseModel + +__all__ = ["DocumentUpdateResponse"] + + +class DocumentUpdateResponse(BaseModel): + id: str + + status: str diff --git a/src/supermemory/types/document_upload_file_params.py b/src/supermemory/types/document_upload_file_params.py new file mode 100644 index 00000000..95c97d2d --- /dev/null +++ b/src/supermemory/types/document_upload_file_params.py @@ -0,0 +1,36 @@ +# 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 .._types import FileTypes +from .._utils import PropertyInfo + +__all__ = ["DocumentUploadFileParams"] + + +class DocumentUploadFileParams(TypedDict, total=False): + file: Required[FileTypes] + """File to upload and process""" + + container_tags: Annotated[str, PropertyInfo(alias="containerTags")] + """Optional JSON string of container tags array. + + This can be an ID for your user, a project ID, or any other identifier you wish + to use to group documents. + """ + + file_type: Annotated[str, PropertyInfo(alias="fileType")] + """Optional file type override to force specific processing behavior. + + Valid values: text, pdf, tweet, google_doc, google_slide, google_sheet, image, + video, notion_doc, webpage, onedrive + """ + + mime_type: Annotated[str, PropertyInfo(alias="mimeType")] + """Required when fileType is 'image' or 'video'. + + Specifies the exact MIME type to use (e.g., 'image/png', 'image/jpeg', + 'video/mp4', 'video/webm') + """ diff --git a/src/supermemory/types/document_upload_file_response.py b/src/supermemory/types/document_upload_file_response.py new file mode 100644 index 00000000..5801bcab --- /dev/null +++ b/src/supermemory/types/document_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__ = ["DocumentUploadFileResponse"] + + +class DocumentUploadFileResponse(BaseModel): + id: str + + status: str diff --git a/src/supermemory/types/memory_add_params.py b/src/supermemory/types/memory_add_params.py new file mode 100644 index 00000000..21ed5bb4 --- /dev/null +++ b/src/supermemory/types/memory_add_params.py @@ -0,0 +1,69 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union +from typing_extensions import Required, Annotated, TypedDict + +from .._types import SequenceNotStr +from .._utils import PropertyInfo + +__all__ = ["MemoryAddParams"] + + +class MemoryAddParams(TypedDict, total=False): + content: Required[str] + """The content to extract and process into a document. + + 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_tag: Annotated[str, PropertyInfo(alias="containerTag")] + """Optional tag this document 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 documents. + """ + + container_tags: Annotated[SequenceNotStr[str], PropertyInfo(alias="containerTags")] + """ + (DEPRECATED: Use containerTag instead) Optional tags this document 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 documents. + """ + + custom_id: Annotated[str, PropertyInfo(alias="customId")] + """Optional custom ID of the document. + + This could be an ID from your database that will uniquely identify this + document. + """ + + file_type: Annotated[str, PropertyInfo(alias="fileType")] + """Optional file type override to force specific processing behavior. + + Valid values: text, pdf, tweet, google_doc, google_slide, google_sheet, image, + video, notion_doc, webpage, onedrive + """ + + metadata: Dict[str, Union[str, float, bool, SequenceNotStr[str]]] + """Optional metadata for the document. + + This is used to store additional information about the document. You can use + this to store any additional information you need about the document. Metadata + can be filtered through. Keys must be strings and are case sensitive. Values can + be strings, numbers, or booleans. You cannot nest objects. + """ + + mime_type: Annotated[str, PropertyInfo(alias="mimeType")] + """Required when fileType is 'image' or 'video'. + + Specifies the exact MIME type to use (e.g., 'image/png', 'image/jpeg', + 'video/mp4', 'video/webm') + """ diff --git a/src/supermemory/types/memory_add_response.py b/src/supermemory/types/memory_add_response.py new file mode 100644 index 00000000..704918e4 --- /dev/null +++ b/src/supermemory/types/memory_add_response.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .._models import BaseModel + +__all__ = ["MemoryAddResponse"] + + +class MemoryAddResponse(BaseModel): + id: str + + status: str diff --git a/src/supermemory/types/memory_get_response.py b/src/supermemory/types/memory_get_response.py new file mode 100644 index 00000000..a495ade9 --- /dev/null +++ b/src/supermemory/types/memory_get_response.py @@ -0,0 +1,104 @@ +# 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"] + + +class MemoryGetResponse(BaseModel): + id: str + """Unique identifier of the document.""" + + connection_id: Optional[str] = FieldInfo(alias="connectionId", default=None) + """Optional ID of connection the document was created from. + + This is useful for identifying the source of the document. + """ + + content: Optional[str] = None + """The content to extract and process into a document. + + 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""" + + custom_id: Optional[str] = FieldInfo(alias="customId", default=None) + """Optional custom ID of the document. + + This could be an ID from your database that will uniquely identify this + document. + """ + + metadata: Union[str, float, bool, Dict[str, object], List[object], None] = None + """Optional metadata for the document. + + This is used to store additional information about the document. You can use + this to store any additional information you need about the document. Metadata + can be filtered through. Keys must be strings and are case sensitive. Values can + be strings, numbers, or booleans. You cannot nest objects. + """ + + og_image: Optional[str] = FieldInfo(alias="ogImage", default=None) + + source: Optional[str] = None + """Source of the document""" + + status: Literal["unknown", "queued", "extracting", "chunking", "embedding", "indexing", "done", "failed"] + """Status of the document""" + + summary: Optional[str] = None + """Summary of the document content""" + + summary_embedding_model: Optional[str] = FieldInfo(alias="summaryEmbeddingModel", default=None) + + summary_embedding_model_new: Optional[str] = FieldInfo(alias="summaryEmbeddingModelNew", default=None) + + summary_embedding_new: Optional[List[float]] = FieldInfo(alias="summaryEmbeddingNew", default=None) + + title: Optional[str] = None + """Title of the document""" + + type: Literal[ + "text", + "pdf", + "tweet", + "google_doc", + "google_slide", + "google_sheet", + "image", + "video", + "notion_doc", + "webpage", + "onedrive", + ] + """Type of the document""" + + updated_at: datetime = FieldInfo(alias="updatedAt") + """Last update timestamp""" + + container_tags: Optional[List[str]] = FieldInfo(alias="containerTags", default=None) + """Optional tags this document 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 documents. + """ + + raw: None = None + """Raw content of the document""" + + url: Optional[str] = None + """URL of the document""" diff --git a/src/supermemory/types/memory_list_params.py b/src/supermemory/types/memory_list_params.py new file mode 100644 index 00000000..636663ff --- /dev/null +++ b/src/supermemory/types/memory_list_params.py @@ -0,0 +1,111 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable +from typing_extensions import Literal, Required, Annotated, TypeAlias, TypedDict + +from .._types import SequenceNotStr +from .._utils import PropertyInfo + +__all__ = [ + "MemoryListParams", + "Filters", + "FiltersOr", + "FiltersOrOr", + "FiltersOrOrUnionMember0", + "FiltersOrOrOr", + "FiltersOrOrAnd", + "FiltersAnd", + "FiltersAndAnd", + "FiltersAndAndUnionMember0", + "FiltersAndAndOr", + "FiltersAndAndAnd", +] + + +class MemoryListParams(TypedDict, total=False): + container_tags: Annotated[SequenceNotStr[str], PropertyInfo(alias="containerTags")] + """Optional tags this document 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 documents. + """ + + filters: Filters + """Optional filters to apply to the search. Can be a JSON string or Query object.""" + + include_content: Annotated[bool, PropertyInfo(alias="includeContent")] + """Whether to include the content field in the response. + + Warning: This can make responses significantly larger. + """ + + 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""" + + +class FiltersOrOrUnionMember0(TypedDict, total=False): + key: Required[str] + + value: Required[str] + + filter_type: Annotated[Literal["metadata", "numeric", "array_contains"], PropertyInfo(alias="filterType")] + + negate: Union[bool, Literal["true", "false"]] + + numeric_operator: Annotated[Literal[">", "<", ">=", "<=", "="], PropertyInfo(alias="numericOperator")] + + +class FiltersOrOrOr(TypedDict, total=False): + or_: Required[Annotated[Iterable[object], PropertyInfo(alias="OR")]] + + +class FiltersOrOrAnd(TypedDict, total=False): + and_: Required[Annotated[Iterable[object], PropertyInfo(alias="AND")]] + + +FiltersOrOr: TypeAlias = Union[FiltersOrOrUnionMember0, FiltersOrOrOr, FiltersOrOrAnd] + + +class FiltersOr(TypedDict, total=False): + or_: Required[Annotated[Iterable[FiltersOrOr], PropertyInfo(alias="OR")]] + + +class FiltersAndAndUnionMember0(TypedDict, total=False): + key: Required[str] + + value: Required[str] + + filter_type: Annotated[Literal["metadata", "numeric", "array_contains"], PropertyInfo(alias="filterType")] + + negate: Union[bool, Literal["true", "false"]] + + numeric_operator: Annotated[Literal[">", "<", ">=", "<=", "="], PropertyInfo(alias="numericOperator")] + + +class FiltersAndAndOr(TypedDict, total=False): + or_: Required[Annotated[Iterable[object], PropertyInfo(alias="OR")]] + + +class FiltersAndAndAnd(TypedDict, total=False): + and_: Required[Annotated[Iterable[object], PropertyInfo(alias="AND")]] + + +FiltersAndAnd: TypeAlias = Union[FiltersAndAndUnionMember0, FiltersAndAndOr, FiltersAndAndAnd] + + +class FiltersAnd(TypedDict, total=False): + and_: Required[Annotated[Iterable[FiltersAndAnd], PropertyInfo(alias="AND")]] + + +Filters: TypeAlias = Union[FiltersOr, FiltersAnd] diff --git a/src/supermemory/types/memory_list_response.py b/src/supermemory/types/memory_list_response.py new file mode 100644 index 00000000..5eead3af --- /dev/null +++ b/src/supermemory/types/memory_list_response.py @@ -0,0 +1,95 @@ +# 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 document.""" + + connection_id: Optional[str] = FieldInfo(alias="connectionId", default=None) + """Optional ID of connection the document was created from. + + This is useful for identifying the source of the document. + """ + + created_at: datetime = FieldInfo(alias="createdAt") + """Creation timestamp""" + + custom_id: Optional[str] = FieldInfo(alias="customId", default=None) + """Optional custom ID of the document. + + This could be an ID from your database that will uniquely identify this + document. + """ + + metadata: Union[str, float, bool, Dict[str, object], List[object], None] = None + """Optional metadata for the document. + + This is used to store additional information about the document. You can use + this to store any additional information you need about the document. 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 document""" + + summary: Optional[str] = None + """Summary of the document content""" + + title: Optional[str] = None + """Title of the document""" + + type: Literal[ + "text", + "pdf", + "tweet", + "google_doc", + "google_slide", + "google_sheet", + "image", + "video", + "notion_doc", + "webpage", + "onedrive", + ] + """Type of the document""" + + updated_at: datetime = FieldInfo(alias="updatedAt") + """Last update timestamp""" + + container_tags: Optional[List[str]] = FieldInfo(alias="containerTags", default=None) + """Optional tags this document 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 documents. + """ + + content: Optional[str] = None + """Content of the document (only included when includeContent=true)""" + + +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/memory_update_params.py b/src/supermemory/types/memory_update_params.py new file mode 100644 index 00000000..1a1308a1 --- /dev/null +++ b/src/supermemory/types/memory_update_params.py @@ -0,0 +1,69 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union +from typing_extensions import Annotated, TypedDict + +from .._types import SequenceNotStr +from .._utils import PropertyInfo + +__all__ = ["MemoryUpdateParams"] + + +class MemoryUpdateParams(TypedDict, total=False): + container_tag: Annotated[str, PropertyInfo(alias="containerTag")] + """Optional tag this document 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 documents. + """ + + container_tags: Annotated[SequenceNotStr[str], PropertyInfo(alias="containerTags")] + """ + (DEPRECATED: Use containerTag instead) Optional tags this document 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 documents. + """ + + content: str + """The content to extract and process into a document. + + 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. + """ + + custom_id: Annotated[str, PropertyInfo(alias="customId")] + """Optional custom ID of the document. + + This could be an ID from your database that will uniquely identify this + document. + """ + + file_type: Annotated[str, PropertyInfo(alias="fileType")] + """Optional file type override to force specific processing behavior. + + Valid values: text, pdf, tweet, google_doc, google_slide, google_sheet, image, + video, notion_doc, webpage, onedrive + """ + + metadata: Dict[str, Union[str, float, bool, SequenceNotStr[str]]] + """Optional metadata for the document. + + This is used to store additional information about the document. You can use + this to store any additional information you need about the document. Metadata + can be filtered through. Keys must be strings and are case sensitive. Values can + be strings, numbers, or booleans. You cannot nest objects. + """ + + mime_type: Annotated[str, PropertyInfo(alias="mimeType")] + """Required when fileType is 'image' or 'video'. + + Specifies the exact MIME type to use (e.g., 'image/png', 'image/jpeg', + 'video/mp4', 'video/webm') + """ diff --git a/src/supermemory/types/memory_update_response.py b/src/supermemory/types/memory_update_response.py new file mode 100644 index 00000000..132b8cf9 --- /dev/null +++ b/src/supermemory/types/memory_update_response.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .._models import BaseModel + +__all__ = ["MemoryUpdateResponse"] + + +class MemoryUpdateResponse(BaseModel): + id: str + + status: str 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..6e8a07aa --- /dev/null +++ b/src/supermemory/types/memory_upload_file_params.py @@ -0,0 +1,36 @@ +# 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 .._types import FileTypes +from .._utils import PropertyInfo + +__all__ = ["MemoryUploadFileParams"] + + +class MemoryUploadFileParams(TypedDict, total=False): + file: Required[FileTypes] + """File to upload and process""" + + container_tags: Annotated[str, PropertyInfo(alias="containerTags")] + """Optional JSON string of container tags array. + + This can be an ID for your user, a project ID, or any other identifier you wish + to use to group documents. + """ + + file_type: Annotated[str, PropertyInfo(alias="fileType")] + """Optional file type override to force specific processing behavior. + + Valid values: text, pdf, tweet, google_doc, google_slide, google_sheet, image, + video, notion_doc, webpage, onedrive + """ + + mime_type: Annotated[str, PropertyInfo(alias="mimeType")] + """Required when fileType is 'image' or 'video'. + + Specifies the exact MIME type to use (e.g., 'image/png', 'image/jpeg', + 'video/mp4', 'video/webm') + """ 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/src/supermemory/types/search_documents_params.py b/src/supermemory/types/search_documents_params.py index 13e0a642..a621bdaa 100644 --- a/src/supermemory/types/search_documents_params.py +++ b/src/supermemory/types/search_documents_params.py @@ -2,13 +2,26 @@ from __future__ import annotations -from typing import Dict, List, Union, Iterable +from typing import List, Union, Iterable from typing_extensions import Literal, Required, Annotated, TypeAlias, TypedDict from .._types import SequenceNotStr from .._utils import PropertyInfo -__all__ = ["SearchDocumentsParams", "Filters", "FiltersUnionMember0"] +__all__ = [ + "SearchDocumentsParams", + "Filters", + "FiltersOr", + "FiltersOrOr", + "FiltersOrOrUnionMember0", + "FiltersOrOrOr", + "FiltersOrOrAnd", + "FiltersAnd", + "FiltersAndAnd", + "FiltersAndAndUnionMember0", + "FiltersAndAndOr", + "FiltersAndAndAnd", +] class SearchDocumentsParams(TypedDict, total=False): @@ -48,7 +61,7 @@ class SearchDocumentsParams(TypedDict, total=False): """ filters: Filters - """Optional filters to apply to the search""" + """Optional filters to apply to the search. Can be a JSON string or Query object.""" include_full_docs: Annotated[bool, PropertyInfo(alias="includeFullDocs")] """If true, include full document in the response. @@ -85,10 +98,58 @@ class SearchDocumentsParams(TypedDict, total=False): """ -class FiltersUnionMember0(TypedDict, total=False): - and_: Annotated[Iterable[object], PropertyInfo(alias="AND")] +class FiltersOrOrUnionMember0(TypedDict, total=False): + key: Required[str] - or_: Annotated[Iterable[object], PropertyInfo(alias="OR")] + value: Required[str] + filter_type: Annotated[Literal["metadata", "numeric", "array_contains"], PropertyInfo(alias="filterType")] -Filters: TypeAlias = Union[FiltersUnionMember0, Dict[str, object]] + negate: Union[bool, Literal["true", "false"]] + + numeric_operator: Annotated[Literal[">", "<", ">=", "<=", "="], PropertyInfo(alias="numericOperator")] + + +class FiltersOrOrOr(TypedDict, total=False): + or_: Required[Annotated[Iterable[object], PropertyInfo(alias="OR")]] + + +class FiltersOrOrAnd(TypedDict, total=False): + and_: Required[Annotated[Iterable[object], PropertyInfo(alias="AND")]] + + +FiltersOrOr: TypeAlias = Union[FiltersOrOrUnionMember0, FiltersOrOrOr, FiltersOrOrAnd] + + +class FiltersOr(TypedDict, total=False): + or_: Required[Annotated[Iterable[FiltersOrOr], PropertyInfo(alias="OR")]] + + +class FiltersAndAndUnionMember0(TypedDict, total=False): + key: Required[str] + + value: Required[str] + + filter_type: Annotated[Literal["metadata", "numeric", "array_contains"], PropertyInfo(alias="filterType")] + + negate: Union[bool, Literal["true", "false"]] + + numeric_operator: Annotated[Literal[">", "<", ">=", "<=", "="], PropertyInfo(alias="numericOperator")] + + +class FiltersAndAndOr(TypedDict, total=False): + or_: Required[Annotated[Iterable[object], PropertyInfo(alias="OR")]] + + +class FiltersAndAndAnd(TypedDict, total=False): + and_: Required[Annotated[Iterable[object], PropertyInfo(alias="AND")]] + + +FiltersAndAnd: TypeAlias = Union[FiltersAndAndUnionMember0, FiltersAndAndOr, FiltersAndAndAnd] + + +class FiltersAnd(TypedDict, total=False): + and_: Required[Annotated[Iterable[FiltersAndAnd], PropertyInfo(alias="AND")]] + + +Filters: TypeAlias = Union[FiltersOr, FiltersAnd] diff --git a/src/supermemory/types/search_execute_params.py b/src/supermemory/types/search_execute_params.py index 48032181..67d9d87b 100644 --- a/src/supermemory/types/search_execute_params.py +++ b/src/supermemory/types/search_execute_params.py @@ -2,13 +2,26 @@ from __future__ import annotations -from typing import Dict, List, Union, Iterable +from typing import List, Union, Iterable from typing_extensions import Literal, Required, Annotated, TypeAlias, TypedDict from .._types import SequenceNotStr from .._utils import PropertyInfo -__all__ = ["SearchExecuteParams", "Filters", "FiltersUnionMember0"] +__all__ = [ + "SearchExecuteParams", + "Filters", + "FiltersOr", + "FiltersOrOr", + "FiltersOrOrUnionMember0", + "FiltersOrOrOr", + "FiltersOrOrAnd", + "FiltersAnd", + "FiltersAndAnd", + "FiltersAndAndUnionMember0", + "FiltersAndAndOr", + "FiltersAndAndAnd", +] class SearchExecuteParams(TypedDict, total=False): @@ -48,7 +61,7 @@ class SearchExecuteParams(TypedDict, total=False): """ filters: Filters - """Optional filters to apply to the search""" + """Optional filters to apply to the search. Can be a JSON string or Query object.""" include_full_docs: Annotated[bool, PropertyInfo(alias="includeFullDocs")] """If true, include full document in the response. @@ -85,10 +98,58 @@ class SearchExecuteParams(TypedDict, total=False): """ -class FiltersUnionMember0(TypedDict, total=False): - and_: Annotated[Iterable[object], PropertyInfo(alias="AND")] +class FiltersOrOrUnionMember0(TypedDict, total=False): + key: Required[str] - or_: Annotated[Iterable[object], PropertyInfo(alias="OR")] + value: Required[str] + filter_type: Annotated[Literal["metadata", "numeric", "array_contains"], PropertyInfo(alias="filterType")] -Filters: TypeAlias = Union[FiltersUnionMember0, Dict[str, object]] + negate: Union[bool, Literal["true", "false"]] + + numeric_operator: Annotated[Literal[">", "<", ">=", "<=", "="], PropertyInfo(alias="numericOperator")] + + +class FiltersOrOrOr(TypedDict, total=False): + or_: Required[Annotated[Iterable[object], PropertyInfo(alias="OR")]] + + +class FiltersOrOrAnd(TypedDict, total=False): + and_: Required[Annotated[Iterable[object], PropertyInfo(alias="AND")]] + + +FiltersOrOr: TypeAlias = Union[FiltersOrOrUnionMember0, FiltersOrOrOr, FiltersOrOrAnd] + + +class FiltersOr(TypedDict, total=False): + or_: Required[Annotated[Iterable[FiltersOrOr], PropertyInfo(alias="OR")]] + + +class FiltersAndAndUnionMember0(TypedDict, total=False): + key: Required[str] + + value: Required[str] + + filter_type: Annotated[Literal["metadata", "numeric", "array_contains"], PropertyInfo(alias="filterType")] + + negate: Union[bool, Literal["true", "false"]] + + numeric_operator: Annotated[Literal[">", "<", ">=", "<=", "="], PropertyInfo(alias="numericOperator")] + + +class FiltersAndAndOr(TypedDict, total=False): + or_: Required[Annotated[Iterable[object], PropertyInfo(alias="OR")]] + + +class FiltersAndAndAnd(TypedDict, total=False): + and_: Required[Annotated[Iterable[object], PropertyInfo(alias="AND")]] + + +FiltersAndAnd: TypeAlias = Union[FiltersAndAndUnionMember0, FiltersAndAndOr, FiltersAndAndAnd] + + +class FiltersAnd(TypedDict, total=False): + and_: Required[Annotated[Iterable[FiltersAndAnd], PropertyInfo(alias="AND")]] + + +Filters: TypeAlias = Union[FiltersOr, FiltersAnd] diff --git a/src/supermemory/types/search_memories_params.py b/src/supermemory/types/search_memories_params.py index 060184d1..5751cd34 100644 --- a/src/supermemory/types/search_memories_params.py +++ b/src/supermemory/types/search_memories_params.py @@ -2,12 +2,26 @@ from __future__ import annotations -from typing import Dict, Union, Iterable -from typing_extensions import Required, Annotated, TypeAlias, TypedDict +from typing import Union, Iterable +from typing_extensions import Literal, Required, Annotated, TypeAlias, TypedDict from .._utils import PropertyInfo -__all__ = ["SearchMemoriesParams", "Filters", "FiltersUnionMember0", "Include"] +__all__ = [ + "SearchMemoriesParams", + "Filters", + "FiltersOr", + "FiltersOrOr", + "FiltersOrOrUnionMember0", + "FiltersOrOrOr", + "FiltersOrOrAnd", + "FiltersAnd", + "FiltersAndAnd", + "FiltersAndAndUnionMember0", + "FiltersAndAndOr", + "FiltersAndAndAnd", + "Include", +] class SearchMemoriesParams(TypedDict, total=False): @@ -22,7 +36,7 @@ class SearchMemoriesParams(TypedDict, total=False): """ filters: Filters - """Optional filters to apply to the search""" + """Optional filters to apply to the search. Can be a JSON string or Query object.""" include: Include @@ -49,18 +63,73 @@ class SearchMemoriesParams(TypedDict, total=False): """ -class FiltersUnionMember0(TypedDict, total=False): - and_: Annotated[Iterable[object], PropertyInfo(alias="AND")] +class FiltersOrOrUnionMember0(TypedDict, total=False): + key: Required[str] - or_: Annotated[Iterable[object], PropertyInfo(alias="OR")] + value: Required[str] + filter_type: Annotated[Literal["metadata", "numeric", "array_contains"], PropertyInfo(alias="filterType")] -Filters: TypeAlias = Union[FiltersUnionMember0, Dict[str, object]] + negate: Union[bool, Literal["true", "false"]] + + numeric_operator: Annotated[Literal[">", "<", ">=", "<=", "="], PropertyInfo(alias="numericOperator")] + + +class FiltersOrOrOr(TypedDict, total=False): + or_: Required[Annotated[Iterable[object], PropertyInfo(alias="OR")]] + + +class FiltersOrOrAnd(TypedDict, total=False): + and_: Required[Annotated[Iterable[object], PropertyInfo(alias="AND")]] + + +FiltersOrOr: TypeAlias = Union[FiltersOrOrUnionMember0, FiltersOrOrOr, FiltersOrOrAnd] + + +class FiltersOr(TypedDict, total=False): + or_: Required[Annotated[Iterable[FiltersOrOr], PropertyInfo(alias="OR")]] + + +class FiltersAndAndUnionMember0(TypedDict, total=False): + key: Required[str] + + value: Required[str] + + filter_type: Annotated[Literal["metadata", "numeric", "array_contains"], PropertyInfo(alias="filterType")] + + negate: Union[bool, Literal["true", "false"]] + + numeric_operator: Annotated[Literal[">", "<", ">=", "<=", "="], PropertyInfo(alias="numericOperator")] + + +class FiltersAndAndOr(TypedDict, total=False): + or_: Required[Annotated[Iterable[object], PropertyInfo(alias="OR")]] + + +class FiltersAndAndAnd(TypedDict, total=False): + and_: Required[Annotated[Iterable[object], PropertyInfo(alias="AND")]] + + +FiltersAndAnd: TypeAlias = Union[FiltersAndAndUnionMember0, FiltersAndAndOr, FiltersAndAndAnd] + + +class FiltersAnd(TypedDict, total=False): + and_: Required[Annotated[Iterable[FiltersAndAnd], PropertyInfo(alias="AND")]] + + +Filters: TypeAlias = Union[FiltersOr, FiltersAnd] class Include(TypedDict, total=False): documents: bool + forgotten_memories: Annotated[bool, PropertyInfo(alias="forgottenMemories")] + """If true, include forgotten memories in search results. + + Forgotten memories are memories that have been explicitly forgotten or have + passed their expiration date. + """ + related_memories: Annotated[bool, PropertyInfo(alias="relatedMemories")] summaries: bool diff --git a/tests/api_resources/test_documents.py b/tests/api_resources/test_documents.py new file mode 100644 index 00000000..c192d7e2 --- /dev/null +++ b/tests/api_resources/test_documents.py @@ -0,0 +1,644 @@ +# 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 ( + DocumentAddResponse, + DocumentGetResponse, + DocumentListResponse, + DocumentUpdateResponse, + DocumentUploadFileResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestDocuments: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_update(self, client: Supermemory) -> None: + document = client.documents.update( + id="id", + ) + assert_matches_type(DocumentUpdateResponse, document, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_update_with_all_params(self, client: Supermemory) -> None: + document = client.documents.update( + id="id", + container_tag="user_123", + container_tags=["user_123", "project_123"], + content="This is a detailed article about machine learning concepts...", + custom_id="mem_abc123", + file_type="pdf", + metadata={ + "category": "technology", + "isPublic": True, + "readingTime": 5, + "source": "web", + "tag_1": "ai", + "tag_2": "machine-learning", + }, + mime_type="image/png", + ) + assert_matches_type(DocumentUpdateResponse, document, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_update(self, client: Supermemory) -> None: + response = client.documents.with_raw_response.update( + id="id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + document = response.parse() + assert_matches_type(DocumentUpdateResponse, document, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_update(self, client: Supermemory) -> None: + with client.documents.with_streaming_response.update( + id="id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + document = response.parse() + assert_matches_type(DocumentUpdateResponse, document, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @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 ''"): + client.documents.with_raw_response.update( + id="", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list(self, client: Supermemory) -> None: + document = client.documents.list() + assert_matches_type(DocumentListResponse, document, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list_with_all_params(self, client: Supermemory) -> None: + document = client.documents.list( + container_tags=["user_123", "project_123"], + filters={ + "and_": [ + { + "key": "group", + "value": "jira_users", + "filter_type": "metadata", + "negate": False, + "numeric_operator": ">", + }, + { + "key": "timestamp", + "value": "1742745777", + "filter_type": "numeric", + "negate": False, + "numeric_operator": ">", + }, + ] + }, + include_content=False, + limit=10, + order="desc", + page=1, + sort="createdAt", + ) + assert_matches_type(DocumentListResponse, document, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_list(self, client: Supermemory) -> None: + response = client.documents.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + document = response.parse() + assert_matches_type(DocumentListResponse, document, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_list(self, client: Supermemory) -> None: + with client.documents.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + document = response.parse() + assert_matches_type(DocumentListResponse, document, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_delete(self, client: Supermemory) -> None: + document = client.documents.delete( + "id", + ) + assert document is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_delete(self, client: Supermemory) -> None: + response = client.documents.with_raw_response.delete( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + document = response.parse() + assert document is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_delete(self, client: Supermemory) -> None: + with client.documents.with_streaming_response.delete( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + document = response.parse() + assert document is None + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_delete(self, client: Supermemory) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.documents.with_raw_response.delete( + "", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_add(self, client: Supermemory) -> None: + document = client.documents.add( + content="This is a detailed article about machine learning concepts...", + ) + assert_matches_type(DocumentAddResponse, document, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_add_with_all_params(self, client: Supermemory) -> None: + document = client.documents.add( + content="This is a detailed article about machine learning concepts...", + container_tag="user_123", + container_tags=["user_123", "project_123"], + custom_id="mem_abc123", + file_type="pdf", + metadata={ + "category": "technology", + "isPublic": True, + "readingTime": 5, + "source": "web", + "tag_1": "ai", + "tag_2": "machine-learning", + }, + mime_type="image/png", + ) + assert_matches_type(DocumentAddResponse, document, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_add(self, client: Supermemory) -> None: + response = client.documents.with_raw_response.add( + content="This is a detailed article about machine learning concepts...", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + document = response.parse() + assert_matches_type(DocumentAddResponse, document, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_add(self, client: Supermemory) -> None: + with client.documents.with_streaming_response.add( + content="This is a detailed article about machine learning concepts...", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + document = response.parse() + assert_matches_type(DocumentAddResponse, document, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_get(self, client: Supermemory) -> None: + document = client.documents.get( + "id", + ) + assert_matches_type(DocumentGetResponse, document, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_get(self, client: Supermemory) -> None: + response = client.documents.with_raw_response.get( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + document = response.parse() + assert_matches_type(DocumentGetResponse, document, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_get(self, client: Supermemory) -> None: + with client.documents.with_streaming_response.get( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + document = response.parse() + assert_matches_type(DocumentGetResponse, document, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_get(self, client: Supermemory) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.documents.with_raw_response.get( + "", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_upload_file(self, client: Supermemory) -> None: + document = client.documents.upload_file( + file=b"raw file contents", + ) + assert_matches_type(DocumentUploadFileResponse, document, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_upload_file_with_all_params(self, client: Supermemory) -> None: + document = client.documents.upload_file( + file=b"raw file contents", + container_tags='["user_123", "project_123"]', + file_type="image", + mime_type="image/png", + ) + assert_matches_type(DocumentUploadFileResponse, document, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_upload_file(self, client: Supermemory) -> None: + response = client.documents.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" + document = response.parse() + assert_matches_type(DocumentUploadFileResponse, document, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_upload_file(self, client: Supermemory) -> None: + with client.documents.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" + + document = response.parse() + assert_matches_type(DocumentUploadFileResponse, document, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncDocuments: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_update(self, async_client: AsyncSupermemory) -> None: + document = await async_client.documents.update( + id="id", + ) + assert_matches_type(DocumentUpdateResponse, document, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_update_with_all_params(self, async_client: AsyncSupermemory) -> None: + document = await async_client.documents.update( + id="id", + container_tag="user_123", + container_tags=["user_123", "project_123"], + content="This is a detailed article about machine learning concepts...", + custom_id="mem_abc123", + file_type="pdf", + metadata={ + "category": "technology", + "isPublic": True, + "readingTime": 5, + "source": "web", + "tag_1": "ai", + "tag_2": "machine-learning", + }, + mime_type="image/png", + ) + assert_matches_type(DocumentUpdateResponse, document, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_update(self, async_client: AsyncSupermemory) -> None: + response = await async_client.documents.with_raw_response.update( + id="id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + document = await response.parse() + assert_matches_type(DocumentUpdateResponse, document, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_update(self, async_client: AsyncSupermemory) -> None: + async with async_client.documents.with_streaming_response.update( + id="id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + document = await response.parse() + assert_matches_type(DocumentUpdateResponse, document, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @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 ''"): + await async_client.documents.with_raw_response.update( + id="", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list(self, async_client: AsyncSupermemory) -> None: + document = await async_client.documents.list() + assert_matches_type(DocumentListResponse, document, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncSupermemory) -> None: + document = await async_client.documents.list( + container_tags=["user_123", "project_123"], + filters={ + "and_": [ + { + "key": "group", + "value": "jira_users", + "filter_type": "metadata", + "negate": False, + "numeric_operator": ">", + }, + { + "key": "timestamp", + "value": "1742745777", + "filter_type": "numeric", + "negate": False, + "numeric_operator": ">", + }, + ] + }, + include_content=False, + limit=10, + order="desc", + page=1, + sort="createdAt", + ) + assert_matches_type(DocumentListResponse, document, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_list(self, async_client: AsyncSupermemory) -> None: + response = await async_client.documents.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + document = await response.parse() + assert_matches_type(DocumentListResponse, document, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_list(self, async_client: AsyncSupermemory) -> None: + async with async_client.documents.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + document = await response.parse() + assert_matches_type(DocumentListResponse, document, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_delete(self, async_client: AsyncSupermemory) -> None: + document = await async_client.documents.delete( + "id", + ) + assert document is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_delete(self, async_client: AsyncSupermemory) -> None: + response = await async_client.documents.with_raw_response.delete( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + document = await response.parse() + assert document is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncSupermemory) -> None: + async with async_client.documents.with_streaming_response.delete( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + document = await response.parse() + assert document is None + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_delete(self, async_client: AsyncSupermemory) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.documents.with_raw_response.delete( + "", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_add(self, async_client: AsyncSupermemory) -> None: + document = await async_client.documents.add( + content="This is a detailed article about machine learning concepts...", + ) + assert_matches_type(DocumentAddResponse, document, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_add_with_all_params(self, async_client: AsyncSupermemory) -> None: + document = await async_client.documents.add( + content="This is a detailed article about machine learning concepts...", + container_tag="user_123", + container_tags=["user_123", "project_123"], + custom_id="mem_abc123", + file_type="pdf", + metadata={ + "category": "technology", + "isPublic": True, + "readingTime": 5, + "source": "web", + "tag_1": "ai", + "tag_2": "machine-learning", + }, + mime_type="image/png", + ) + assert_matches_type(DocumentAddResponse, document, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_add(self, async_client: AsyncSupermemory) -> None: + response = await async_client.documents.with_raw_response.add( + content="This is a detailed article about machine learning concepts...", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + document = await response.parse() + assert_matches_type(DocumentAddResponse, document, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_add(self, async_client: AsyncSupermemory) -> None: + async with async_client.documents.with_streaming_response.add( + content="This is a detailed article about machine learning concepts...", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + document = await response.parse() + assert_matches_type(DocumentAddResponse, document, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_get(self, async_client: AsyncSupermemory) -> None: + document = await async_client.documents.get( + "id", + ) + assert_matches_type(DocumentGetResponse, document, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_get(self, async_client: AsyncSupermemory) -> None: + response = await async_client.documents.with_raw_response.get( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + document = await response.parse() + assert_matches_type(DocumentGetResponse, document, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_get(self, async_client: AsyncSupermemory) -> None: + async with async_client.documents.with_streaming_response.get( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + document = await response.parse() + assert_matches_type(DocumentGetResponse, document, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_get(self, async_client: AsyncSupermemory) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.documents.with_raw_response.get( + "", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_upload_file(self, async_client: AsyncSupermemory) -> None: + document = await async_client.documents.upload_file( + file=b"raw file contents", + ) + assert_matches_type(DocumentUploadFileResponse, document, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_upload_file_with_all_params(self, async_client: AsyncSupermemory) -> None: + document = await async_client.documents.upload_file( + file=b"raw file contents", + container_tags='["user_123", "project_123"]', + file_type="image", + mime_type="image/png", + ) + assert_matches_type(DocumentUploadFileResponse, document, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_upload_file(self, async_client: AsyncSupermemory) -> None: + response = await async_client.documents.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" + document = await response.parse() + assert_matches_type(DocumentUploadFileResponse, document, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_upload_file(self, async_client: AsyncSupermemory) -> None: + async with async_client.documents.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" + + document = await response.parse() + assert_matches_type(DocumentUploadFileResponse, document, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/test_memories.py b/tests/api_resources/test_memories.py new file mode 100644 index 00000000..2effb9a2 --- /dev/null +++ b/tests/api_resources/test_memories.py @@ -0,0 +1,644 @@ +# 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 ( + MemoryAddResponse, + MemoryGetResponse, + MemoryListResponse, + MemoryUpdateResponse, + MemoryUploadFileResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestMemories: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_update(self, client: Supermemory) -> None: + memory = client.memories.update( + id="id", + ) + assert_matches_type(MemoryUpdateResponse, memory, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_update_with_all_params(self, client: Supermemory) -> None: + memory = client.memories.update( + id="id", + container_tag="user_123", + container_tags=["user_123", "project_123"], + content="This is a detailed article about machine learning concepts...", + custom_id="mem_abc123", + file_type="pdf", + metadata={ + "category": "technology", + "isPublic": True, + "readingTime": 5, + "source": "web", + "tag_1": "ai", + "tag_2": "machine-learning", + }, + mime_type="image/png", + ) + assert_matches_type(MemoryUpdateResponse, memory, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_update(self, client: Supermemory) -> None: + response = client.memories.with_raw_response.update( + id="id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + memory = response.parse() + assert_matches_type(MemoryUpdateResponse, memory, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_update(self, client: Supermemory) -> None: + with client.memories.with_streaming_response.update( + id="id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + memory = response.parse() + assert_matches_type(MemoryUpdateResponse, memory, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @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 ''"): + client.memories.with_raw_response.update( + id="", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list(self, client: Supermemory) -> None: + memory = client.memories.list() + assert_matches_type(MemoryListResponse, memory, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @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", + "filter_type": "metadata", + "negate": False, + "numeric_operator": ">", + }, + { + "key": "timestamp", + "value": "1742745777", + "filter_type": "numeric", + "negate": False, + "numeric_operator": ">", + }, + ] + }, + include_content=False, + limit=10, + order="desc", + page=1, + sort="createdAt", + ) + assert_matches_type(MemoryListResponse, memory, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_list(self, client: Supermemory) -> None: + 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(reason="Prism tests are disabled") + @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" + + memory = response.parse() + assert_matches_type(MemoryListResponse, memory, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_delete(self, client: Supermemory) -> None: + memory = client.memories.delete( + "id", + ) + assert memory is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_delete(self, client: Supermemory) -> None: + response = client.memories.with_raw_response.delete( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + memory = response.parse() + assert memory is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_delete(self, client: Supermemory) -> None: + with client.memories.with_streaming_response.delete( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + memory = response.parse() + assert memory is None + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_delete(self, client: Supermemory) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.memories.with_raw_response.delete( + "", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_add(self, client: Supermemory) -> None: + memory = client.memories.add( + content="This is a detailed article about machine learning concepts...", + ) + assert_matches_type(MemoryAddResponse, memory, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + 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_tag="user_123", + container_tags=["user_123", "project_123"], + custom_id="mem_abc123", + file_type="pdf", + metadata={ + "category": "technology", + "isPublic": True, + "readingTime": 5, + "source": "web", + "tag_1": "ai", + "tag_2": "machine-learning", + }, + mime_type="image/png", + ) + assert_matches_type(MemoryAddResponse, memory, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_add(self, client: Supermemory) -> None: + response = client.memories.with_raw_response.add( + content="This is a detailed article about machine learning concepts...", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + memory = response.parse() + assert_matches_type(MemoryAddResponse, memory, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_add(self, client: Supermemory) -> None: + with client.memories.with_streaming_response.add( + content="This is a detailed article about machine learning concepts...", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + memory = response.parse() + assert_matches_type(MemoryAddResponse, memory, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_get(self, client: Supermemory) -> None: + memory = client.memories.get( + "id", + ) + assert_matches_type(MemoryGetResponse, memory, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_get(self, client: Supermemory) -> None: + response = client.memories.with_raw_response.get( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + memory = response.parse() + assert_matches_type(MemoryGetResponse, memory, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_get(self, client: Supermemory) -> None: + with client.memories.with_streaming_response.get( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + memory = response.parse() + assert_matches_type(MemoryGetResponse, memory, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_get(self, client: Supermemory) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.memories.with_raw_response.get( + "", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @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(reason="Prism tests are disabled") + @parametrize + def test_method_upload_file_with_all_params(self, client: Supermemory) -> None: + memory = client.memories.upload_file( + file=b"raw file contents", + container_tags='["user_123", "project_123"]', + file_type="image", + mime_type="image/png", + ) + assert_matches_type(MemoryUploadFileResponse, memory, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @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(reason="Prism tests are disabled") + @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, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_update(self, async_client: AsyncSupermemory) -> None: + memory = await async_client.memories.update( + id="id", + ) + assert_matches_type(MemoryUpdateResponse, memory, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_update_with_all_params(self, async_client: AsyncSupermemory) -> None: + memory = await async_client.memories.update( + id="id", + container_tag="user_123", + container_tags=["user_123", "project_123"], + content="This is a detailed article about machine learning concepts...", + custom_id="mem_abc123", + file_type="pdf", + metadata={ + "category": "technology", + "isPublic": True, + "readingTime": 5, + "source": "web", + "tag_1": "ai", + "tag_2": "machine-learning", + }, + mime_type="image/png", + ) + assert_matches_type(MemoryUpdateResponse, memory, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_update(self, async_client: AsyncSupermemory) -> None: + response = await async_client.memories.with_raw_response.update( + id="id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + memory = await response.parse() + assert_matches_type(MemoryUpdateResponse, memory, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_update(self, async_client: AsyncSupermemory) -> None: + async with async_client.memories.with_streaming_response.update( + id="id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + memory = await response.parse() + assert_matches_type(MemoryUpdateResponse, memory, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @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 ''"): + await async_client.memories.with_raw_response.update( + id="", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list(self, async_client: AsyncSupermemory) -> None: + memory = await async_client.memories.list() + assert_matches_type(MemoryListResponse, memory, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @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", + "filter_type": "metadata", + "negate": False, + "numeric_operator": ">", + }, + { + "key": "timestamp", + "value": "1742745777", + "filter_type": "numeric", + "negate": False, + "numeric_operator": ">", + }, + ] + }, + include_content=False, + limit=10, + order="desc", + page=1, + sort="createdAt", + ) + assert_matches_type(MemoryListResponse, memory, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_list(self, async_client: AsyncSupermemory) -> None: + 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(reason="Prism tests are disabled") + @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" + + memory = await response.parse() + assert_matches_type(MemoryListResponse, memory, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_delete(self, async_client: AsyncSupermemory) -> None: + memory = await async_client.memories.delete( + "id", + ) + assert memory is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_delete(self, async_client: AsyncSupermemory) -> None: + response = await async_client.memories.with_raw_response.delete( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + memory = await response.parse() + assert memory is None + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncSupermemory) -> None: + async with async_client.memories.with_streaming_response.delete( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + memory = await response.parse() + assert memory is None + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_delete(self, async_client: AsyncSupermemory) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.memories.with_raw_response.delete( + "", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_add(self, async_client: AsyncSupermemory) -> None: + memory = await async_client.memories.add( + content="This is a detailed article about machine learning concepts...", + ) + assert_matches_type(MemoryAddResponse, memory, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + 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_tag="user_123", + container_tags=["user_123", "project_123"], + custom_id="mem_abc123", + file_type="pdf", + metadata={ + "category": "technology", + "isPublic": True, + "readingTime": 5, + "source": "web", + "tag_1": "ai", + "tag_2": "machine-learning", + }, + mime_type="image/png", + ) + assert_matches_type(MemoryAddResponse, memory, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_add(self, async_client: AsyncSupermemory) -> None: + response = await async_client.memories.with_raw_response.add( + content="This is a detailed article about machine learning concepts...", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + memory = await response.parse() + assert_matches_type(MemoryAddResponse, memory, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_add(self, async_client: AsyncSupermemory) -> None: + async with async_client.memories.with_streaming_response.add( + content="This is a detailed article about machine learning concepts...", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + memory = await response.parse() + assert_matches_type(MemoryAddResponse, memory, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_get(self, async_client: AsyncSupermemory) -> None: + memory = await async_client.memories.get( + "id", + ) + assert_matches_type(MemoryGetResponse, memory, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_get(self, async_client: AsyncSupermemory) -> None: + response = await async_client.memories.with_raw_response.get( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + memory = await response.parse() + assert_matches_type(MemoryGetResponse, memory, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_get(self, async_client: AsyncSupermemory) -> None: + async with async_client.memories.with_streaming_response.get( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + memory = await response.parse() + assert_matches_type(MemoryGetResponse, memory, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_get(self, async_client: AsyncSupermemory) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.memories.with_raw_response.get( + "", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @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(reason="Prism tests are disabled") + @parametrize + async def test_method_upload_file_with_all_params(self, async_client: AsyncSupermemory) -> None: + memory = await async_client.memories.upload_file( + file=b"raw file contents", + container_tags='["user_123", "project_123"]', + file_type="image", + mime_type="image/png", + ) + assert_matches_type(MemoryUploadFileResponse, memory, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @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(reason="Prism tests are disabled") + @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 diff --git a/tests/api_resources/test_search.py b/tests/api_resources/test_search.py index fc79884f..810511c6 100644 --- a/tests/api_resources/test_search.py +++ b/tests/api_resources/test_search.py @@ -43,18 +43,19 @@ def test_method_documents_with_all_params(self, client: Supermemory) -> None: "and_": [ { "key": "group", - "negate": False, "value": "jira_users", + "filter_type": "metadata", + "negate": False, + "numeric_operator": ">", }, { - "filterType": "numeric", "key": "timestamp", - "negate": False, - "numericOperator": ">", "value": "1742745777", + "filter_type": "numeric", + "negate": False, + "numeric_operator": ">", }, - ], - "or_": [{}], + ] }, include_full_docs=False, include_summary=False, @@ -113,18 +114,19 @@ def test_method_execute_with_all_params(self, client: Supermemory) -> None: "and_": [ { "key": "group", - "negate": False, "value": "jira_users", + "filter_type": "metadata", + "negate": False, + "numeric_operator": ">", }, { - "filterType": "numeric", "key": "timestamp", - "negate": False, - "numericOperator": ">", "value": "1742745777", + "filter_type": "numeric", + "negate": False, + "numeric_operator": ">", }, - ], - "or_": [{}], + ] }, include_full_docs=False, include_summary=False, @@ -179,21 +181,23 @@ def test_method_memories_with_all_params(self, client: Supermemory) -> None: "and_": [ { "key": "group", - "negate": False, "value": "jira_users", + "filter_type": "metadata", + "negate": False, + "numeric_operator": ">", }, { - "filterType": "numeric", "key": "timestamp", - "negate": False, - "numericOperator": ">", "value": "1742745777", + "filter_type": "numeric", + "negate": False, + "numeric_operator": ">", }, - ], - "or_": [{}], + ] }, include={ "documents": True, + "forgotten_memories": False, "related_memories": True, "summaries": True, }, @@ -258,18 +262,19 @@ async def test_method_documents_with_all_params(self, async_client: AsyncSuperme "and_": [ { "key": "group", - "negate": False, "value": "jira_users", + "filter_type": "metadata", + "negate": False, + "numeric_operator": ">", }, { - "filterType": "numeric", "key": "timestamp", - "negate": False, - "numericOperator": ">", "value": "1742745777", + "filter_type": "numeric", + "negate": False, + "numeric_operator": ">", }, - ], - "or_": [{}], + ] }, include_full_docs=False, include_summary=False, @@ -328,18 +333,19 @@ async def test_method_execute_with_all_params(self, async_client: AsyncSupermemo "and_": [ { "key": "group", - "negate": False, "value": "jira_users", + "filter_type": "metadata", + "negate": False, + "numeric_operator": ">", }, { - "filterType": "numeric", "key": "timestamp", - "negate": False, - "numericOperator": ">", "value": "1742745777", + "filter_type": "numeric", + "negate": False, + "numeric_operator": ">", }, - ], - "or_": [{}], + ] }, include_full_docs=False, include_summary=False, @@ -394,21 +400,23 @@ async def test_method_memories_with_all_params(self, async_client: AsyncSupermem "and_": [ { "key": "group", - "negate": False, "value": "jira_users", + "filter_type": "metadata", + "negate": False, + "numeric_operator": ">", }, { - "filterType": "numeric", "key": "timestamp", - "negate": False, - "numericOperator": ">", "value": "1742745777", + "filter_type": "numeric", + "negate": False, + "numeric_operator": ">", }, - ], - "or_": [{}], + ] }, include={ "documents": True, + "forgotten_memories": False, "related_memories": True, "summaries": True, }, diff --git a/tests/test_client.py b/tests/test_client.py index 0f948871..4e97c388 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -724,20 +724,24 @@ 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, client: Supermemory) -> None: - respx_mock.post("/v3/search").mock(side_effect=httpx.TimeoutException("Test timeout error")) + respx_mock.post("/v3/documents").mock(side_effect=httpx.TimeoutException("Test timeout error")) with pytest.raises(APITimeoutError): - client.search.with_streaming_response.documents(q="machine learning concepts").__enter__() + 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, client: Supermemory) -> None: - respx_mock.post("/v3/search").mock(return_value=httpx.Response(500)) + respx_mock.post("/v3/documents").mock(return_value=httpx.Response(500)) with pytest.raises(APIStatusError): - client.search.with_streaming_response.documents(q="machine learning concepts").__enter__() + 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]) @@ -764,9 +768,11 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: return httpx.Response(500) return httpx.Response(200) - respx_mock.post("/v3/search").mock(side_effect=retry_handler) + respx_mock.post("/v3/documents").mock(side_effect=retry_handler) - response = client.search.with_raw_response.documents(q="machine learning concepts") + response = client.memories.with_raw_response.add( + content="This is a detailed article about machine learning concepts..." + ) assert response.retries_taken == failures_before_success assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success @@ -788,10 +794,11 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: return httpx.Response(500) return httpx.Response(200) - respx_mock.post("/v3/search").mock(side_effect=retry_handler) + respx_mock.post("/v3/documents").mock(side_effect=retry_handler) - response = client.search.with_raw_response.documents( - q="machine learning concepts", extra_headers={"x-stainless-retry-count": Omit()} + response = client.memories.with_raw_response.add( + content="This is a detailed article about machine learning concepts...", + extra_headers={"x-stainless-retry-count": Omit()}, ) assert len(response.http_request.headers.get_list("x-stainless-retry-count")) == 0 @@ -813,10 +820,11 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: return httpx.Response(500) return httpx.Response(200) - respx_mock.post("/v3/search").mock(side_effect=retry_handler) + respx_mock.post("/v3/documents").mock(side_effect=retry_handler) - response = client.search.with_raw_response.documents( - q="machine learning concepts", extra_headers={"x-stainless-retry-count": "42"} + response = client.memories.with_raw_response.add( + content="This is a detailed article about machine learning concepts...", + extra_headers={"x-stainless-retry-count": "42"}, ) assert response.http_request.headers.get("x-stainless-retry-count") == "42" @@ -1545,10 +1553,12 @@ async def test_parse_retry_after_header(self, remaining_retries: int, retry_afte async def test_retrying_timeout_errors_doesnt_leak( self, respx_mock: MockRouter, async_client: AsyncSupermemory ) -> None: - respx_mock.post("/v3/search").mock(side_effect=httpx.TimeoutException("Test timeout error")) + respx_mock.post("/v3/documents").mock(side_effect=httpx.TimeoutException("Test timeout error")) with pytest.raises(APITimeoutError): - await async_client.search.with_streaming_response.documents(q="machine learning concepts").__aenter__() + 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 @@ -1557,10 +1567,12 @@ async def test_retrying_timeout_errors_doesnt_leak( async def test_retrying_status_errors_doesnt_leak( self, respx_mock: MockRouter, async_client: AsyncSupermemory ) -> None: - respx_mock.post("/v3/search").mock(return_value=httpx.Response(500)) + respx_mock.post("/v3/documents").mock(return_value=httpx.Response(500)) with pytest.raises(APIStatusError): - await async_client.search.with_streaming_response.documents(q="machine learning concepts").__aenter__() + 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]) @@ -1588,9 +1600,11 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: return httpx.Response(500) return httpx.Response(200) - respx_mock.post("/v3/search").mock(side_effect=retry_handler) + respx_mock.post("/v3/documents").mock(side_effect=retry_handler) - response = await client.search.with_raw_response.documents(q="machine learning concepts") + response = await client.memories.with_raw_response.add( + content="This is a detailed article about machine learning concepts..." + ) assert response.retries_taken == failures_before_success assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success @@ -1613,10 +1627,11 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: return httpx.Response(500) return httpx.Response(200) - respx_mock.post("/v3/search").mock(side_effect=retry_handler) + respx_mock.post("/v3/documents").mock(side_effect=retry_handler) - response = await client.search.with_raw_response.documents( - q="machine learning concepts", extra_headers={"x-stainless-retry-count": Omit()} + response = await client.memories.with_raw_response.add( + content="This is a detailed article about machine learning concepts...", + extra_headers={"x-stainless-retry-count": Omit()}, ) assert len(response.http_request.headers.get_list("x-stainless-retry-count")) == 0 @@ -1639,10 +1654,11 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: return httpx.Response(500) return httpx.Response(200) - respx_mock.post("/v3/search").mock(side_effect=retry_handler) + respx_mock.post("/v3/documents").mock(side_effect=retry_handler) - response = await client.search.with_raw_response.documents( - q="machine learning concepts", extra_headers={"x-stainless-retry-count": "42"} + response = await client.memories.with_raw_response.add( + content="This is a detailed article about machine learning concepts...", + extra_headers={"x-stainless-retry-count": "42"}, ) assert response.http_request.headers.get("x-stainless-retry-count") == "42" diff --git a/tests/test_transform.py b/tests/test_transform.py index b4c46e41..ab0bf040 100644 --- a/tests/test_transform.py +++ b/tests/test_transform.py @@ -8,7 +8,7 @@ import pytest -from supermemory._types import NOT_GIVEN, Base64FileInput +from supermemory._types import Base64FileInput, omit, not_given from supermemory._utils import ( PropertyInfo, transform as _transform, @@ -450,4 +450,11 @@ async def test_transform_skipping(use_async: bool) -> None: @pytest.mark.asyncio async def test_strips_notgiven(use_async: bool) -> None: assert await transform({"foo_bar": "bar"}, Foo1, use_async) == {"fooBar": "bar"} - assert await transform({"foo_bar": NOT_GIVEN}, Foo1, use_async) == {} + assert await transform({"foo_bar": not_given}, Foo1, use_async) == {} + + +@parametrize +@pytest.mark.asyncio +async def test_strips_omit(use_async: bool) -> None: + assert await transform({"foo_bar": "bar"}, Foo1, use_async) == {"fooBar": "bar"} + assert await transform({"foo_bar": omit}, Foo1, use_async) == {}