diff --git a/.release-please-manifest.json b/.release-please-manifest.json index f391d416..27d2fbbc 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "3.6.0" + ".": "3.7.0" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index 0b389cd6..fa22dd06 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 18 +configured_endpoints: 19 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/supermemory--inc%2Fsupermemory-new-ebd5e757d0e76cb83013e01a1e0bb3dba62beb83b2a2ffa28d148ea032e96fd0.yml openapi_spec_hash: f930474a6ad230545154244045cc602e -config_hash: 5bc39292a7e2f871b35cdebbecd9f022 +config_hash: ec08a36e60458b4d83e71798a8043484 diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e3dbe79..0a6f9c74 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,17 @@ # Changelog +## 3.7.0 (2025-11-26) + +Full Changelog: [v3.6.0...v3.7.0](https://github.com/supermemoryai/python-sdk/compare/v3.6.0...v3.7.0) + +### Features + +* **api:** manual updates ([ee2daac](https://github.com/supermemoryai/python-sdk/commit/ee2daac3a0c1a085971a71ef9605d1d4f6fdb3f7)) +* **api:** manual updates ([89a6e6d](https://github.com/supermemoryai/python-sdk/commit/89a6e6d1220cd8fd2c38d1c54f412650913a9184)) +* **api:** manual updates ([7f0b5aa](https://github.com/supermemoryai/python-sdk/commit/7f0b5aa308004d68b5620f6956068e0c7fbc20da)) +* **api:** manual updates ([3f2f954](https://github.com/supermemoryai/python-sdk/commit/3f2f954a542c4f2e6fc7d6e204c7e44fe640ec7d)) +* **api:** manual updates ([8680e72](https://github.com/supermemoryai/python-sdk/commit/8680e723f9bd9cdc930ed33914557dc60d5242c0)) + ## 3.6.0 (2025-11-26) Full Changelog: [v3.5.0...v3.6.0](https://github.com/supermemoryai/python-sdk/compare/v3.5.0...v3.6.0) diff --git a/README.md b/README.md index 9ed1ed88..7cc684a6 100644 --- a/README.md +++ b/README.md @@ -160,7 +160,7 @@ from supermemory import Supermemory client = Supermemory() try: - client.memories.add( + client.add( content="content", ) except supermemory.APIConnectionError as e: @@ -205,7 +205,7 @@ client = Supermemory( ) # Or, configure per-request: -client.with_options(max_retries=5).memories.add( +client.with_options(max_retries=5).add( content="content", ) ``` @@ -230,7 +230,7 @@ client = Supermemory( ) # Override per-request: -client.with_options(timeout=5.0).memories.add( +client.with_options(timeout=5.0).add( content="content", ) ``` @@ -273,13 +273,13 @@ The "raw" Response object can be accessed by prefixing `.with_raw_response.` to from supermemory import Supermemory client = Supermemory() -response = client.memories.with_raw_response.add( +response = client.with_raw_response.add( content="content", ) print(response.headers.get('X-My-Header')) -memory = response.parse() # get the object that `memories.add()` would have returned -print(memory.id) +client = response.parse() # get the object that `add()` would have returned +print(client.id) ``` These methods return an [`APIResponse`](https://github.com/supermemoryai/python-sdk/tree/main/src/supermemory/_response.py) object. @@ -293,7 +293,7 @@ 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.memories.with_streaming_response.add( +with client.with_streaming_response.add( content="content", ) as response: print(response.headers.get("X-My-Header")) diff --git a/api.md b/api.md index 4451d8b1..43179a60 100644 --- a/api.md +++ b/api.md @@ -1,9 +1,16 @@ -# Shared Types +# Supermemory + +Types: ```python -from supermemory.types import And, Or +from supermemory.types import AddResponse, ProfileResponse ``` +Methods: + +- client.add(\*\*params) -> AddResponse +- client.profile(\*\*params) -> ProfileResponse + # Memories Types: diff --git a/pyproject.toml b/pyproject.toml index ac4ed2c5..e7dcc47a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "supermemory" -version = "3.6.0" +version = "3.7.0" description = "The official Python library for the supermemory API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/supermemory/_client.py b/src/supermemory/_client.py index 0de41593..e9070a3b 100644 --- a/src/supermemory/_client.py +++ b/src/supermemory/_client.py @@ -3,24 +3,41 @@ from __future__ import annotations import os -from typing import Any, Mapping +from typing import Any, Dict, Union, Mapping from typing_extensions import Self, override import httpx from . import _exceptions from ._qs import Querystring +from .types import client_add_params, client_profile_params from ._types import ( + Body, Omit, + Query, + Headers, Timeout, NotGiven, Transport, ProxiesTypes, RequestOptions, + SequenceNotStr, + omit, not_given, ) -from ._utils import is_given, get_async_library +from ._utils import ( + is_given, + maybe_transform, + get_async_library, + async_maybe_transform, +) from ._version import __version__ +from ._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) from .resources import search, memories, settings, documents, connections from ._streaming import Stream as Stream, AsyncStream as AsyncStream from ._exceptions import APIStatusError, SupermemoryError @@ -28,7 +45,10 @@ DEFAULT_MAX_RETRIES, SyncAPIClient, AsyncAPIClient, + make_request_options, ) +from .types.add_response import AddResponse +from .types.profile_response import ProfileResponse __all__ = [ "Timeout", @@ -184,6 +204,106 @@ def copy( # client.with_options(timeout=10).foo.create(...) with_options = copy + def add( + self, + *, + content: str, + container_tag: str | Omit = omit, + container_tags: SequenceNotStr[str] | Omit = omit, + custom_id: str | Omit = omit, + metadata: Dict[str, Union[str, float, bool, SequenceNotStr[str]]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AddResponse: + """ + 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. + + container_tag: Optional tag this document should be containerized by. Max 100 characters, + alphanumeric with hyphens and underscores only. + + custom_id: Optional custom ID of the document. Max 100 characters, alphanumeric with + hyphens and underscores only. + + metadata: Optional metadata for the document. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self.post( + "/v3/documents", + body=maybe_transform( + { + "content": content, + "container_tag": container_tag, + "container_tags": container_tags, + "custom_id": custom_id, + "metadata": metadata, + }, + client_add_params.ClientAddParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=AddResponse, + ) + + def profile( + self, + *, + container_tag: str, + q: 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, + ) -> ProfileResponse: + """ + Get user profile with optional search results + + Args: + container_tag: Tag to filter the profile by. This can be an ID for your user, a project ID, or + any other identifier you wish to use to filter memories. + + q: Optional search query to include search results in the response + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self.post( + "/v4/profile", + body=maybe_transform( + { + "container_tag": container_tag, + "q": q, + }, + client_profile_params.ClientProfileParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ProfileResponse, + ) + @override def _make_status_error( self, @@ -360,6 +480,106 @@ def copy( # client.with_options(timeout=10).foo.create(...) with_options = copy + async def add( + self, + *, + content: str, + container_tag: str | Omit = omit, + container_tags: SequenceNotStr[str] | Omit = omit, + custom_id: str | Omit = omit, + metadata: Dict[str, Union[str, float, bool, SequenceNotStr[str]]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AddResponse: + """ + 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. + + container_tag: Optional tag this document should be containerized by. Max 100 characters, + alphanumeric with hyphens and underscores only. + + custom_id: Optional custom ID of the document. Max 100 characters, alphanumeric with + hyphens and underscores only. + + metadata: Optional metadata for the document. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self.post( + "/v3/documents", + body=await async_maybe_transform( + { + "content": content, + "container_tag": container_tag, + "container_tags": container_tags, + "custom_id": custom_id, + "metadata": metadata, + }, + client_add_params.ClientAddParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=AddResponse, + ) + + async def profile( + self, + *, + container_tag: str, + q: 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, + ) -> ProfileResponse: + """ + Get user profile with optional search results + + Args: + container_tag: Tag to filter the profile by. This can be an ID for your user, a project ID, or + any other identifier you wish to use to filter memories. + + q: Optional search query to include search results in the response + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self.post( + "/v4/profile", + body=await async_maybe_transform( + { + "container_tag": container_tag, + "q": q, + }, + client_profile_params.ClientProfileParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ProfileResponse, + ) + @override def _make_status_error( self, @@ -402,6 +622,13 @@ def __init__(self, client: Supermemory) -> None: self.settings = settings.SettingsResourceWithRawResponse(client.settings) self.connections = connections.ConnectionsResourceWithRawResponse(client.connections) + self.add = to_raw_response_wrapper( + client.add, + ) + self.profile = to_raw_response_wrapper( + client.profile, + ) + class AsyncSupermemoryWithRawResponse: def __init__(self, client: AsyncSupermemory) -> None: @@ -411,6 +638,13 @@ def __init__(self, client: AsyncSupermemory) -> None: self.settings = settings.AsyncSettingsResourceWithRawResponse(client.settings) self.connections = connections.AsyncConnectionsResourceWithRawResponse(client.connections) + self.add = async_to_raw_response_wrapper( + client.add, + ) + self.profile = async_to_raw_response_wrapper( + client.profile, + ) + class SupermemoryWithStreamedResponse: def __init__(self, client: Supermemory) -> None: @@ -420,6 +654,13 @@ def __init__(self, client: Supermemory) -> None: self.settings = settings.SettingsResourceWithStreamingResponse(client.settings) self.connections = connections.ConnectionsResourceWithStreamingResponse(client.connections) + self.add = to_streamed_response_wrapper( + client.add, + ) + self.profile = to_streamed_response_wrapper( + client.profile, + ) + class AsyncSupermemoryWithStreamedResponse: def __init__(self, client: AsyncSupermemory) -> None: @@ -429,6 +670,13 @@ def __init__(self, client: AsyncSupermemory) -> None: self.settings = settings.AsyncSettingsResourceWithStreamingResponse(client.settings) self.connections = connections.AsyncConnectionsResourceWithStreamingResponse(client.connections) + self.add = async_to_streamed_response_wrapper( + client.add, + ) + self.profile = async_to_streamed_response_wrapper( + client.profile, + ) + Client = Supermemory diff --git a/src/supermemory/_version.py b/src/supermemory/_version.py index 859e0c9e..3beee285 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.6.0" # x-release-please-version +__version__ = "3.7.0" # x-release-please-version diff --git a/src/supermemory/types/__init__.py b/src/supermemory/types/__init__.py index 54bc0e85..33058382 100644 --- a/src/supermemory/types/__init__.py +++ b/src/supermemory/types/__init__.py @@ -2,7 +2,9 @@ from __future__ import annotations -from .shared import Or as Or, And as And +from .add_response import AddResponse as AddResponse +from .profile_response import ProfileResponse as ProfileResponse +from .client_add_params import ClientAddParams as ClientAddParams from .memory_add_params import MemoryAddParams as MemoryAddParams from .memory_list_params import MemoryListParams as MemoryListParams from .document_add_params import DocumentAddParams as DocumentAddParams @@ -12,6 +14,7 @@ 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 .client_profile_params import ClientProfileParams as ClientProfileParams from .document_add_response import DocumentAddResponse as DocumentAddResponse from .document_get_response import DocumentGetResponse as DocumentGetResponse from .search_execute_params import SearchExecuteParams as SearchExecuteParams diff --git a/src/supermemory/types/add_response.py b/src/supermemory/types/add_response.py new file mode 100644 index 00000000..cfa9c5fb --- /dev/null +++ b/src/supermemory/types/add_response.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .._models import BaseModel + +__all__ = ["AddResponse"] + + +class AddResponse(BaseModel): + id: str + """Unique identifier of the document""" + + status: str + """Status of the document""" diff --git a/src/supermemory/types/client_add_params.py b/src/supermemory/types/client_add_params.py new file mode 100644 index 00000000..2d9e5c02 --- /dev/null +++ b/src/supermemory/types/client_add_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 import Dict, Union +from typing_extensions import Required, Annotated, TypedDict + +from .._types import SequenceNotStr +from .._utils import PropertyInfo + +__all__ = ["ClientAddParams"] + + +class ClientAddParams(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. + """ + + container_tag: Annotated[str, PropertyInfo(alias="containerTag")] + """Optional tag this document should be containerized by. + + Max 100 characters, alphanumeric with hyphens and underscores only. + """ + + container_tags: Annotated[SequenceNotStr[str], PropertyInfo(alias="containerTags")] + + custom_id: Annotated[str, PropertyInfo(alias="customId")] + """Optional custom ID of the document. + + Max 100 characters, alphanumeric with hyphens and underscores only. + """ + + metadata: Dict[str, Union[str, float, bool, SequenceNotStr[str]]] + """Optional metadata for the document.""" diff --git a/src/supermemory/types/client_profile_params.py b/src/supermemory/types/client_profile_params.py new file mode 100644 index 00000000..428b62aa --- /dev/null +++ b/src/supermemory/types/client_profile_params.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, Annotated, TypedDict + +from .._utils import PropertyInfo + +__all__ = ["ClientProfileParams"] + + +class ClientProfileParams(TypedDict, total=False): + container_tag: Required[Annotated[str, PropertyInfo(alias="containerTag")]] + """Tag to filter the profile by. + + This can be an ID for your user, a project ID, or any other identifier you wish + to use to filter memories. + """ + + q: str + """Optional search query to include search results in the response""" diff --git a/src/supermemory/types/document_list_params.py b/src/supermemory/types/document_list_params.py index f48c76f1..3b62b1a6 100644 --- a/src/supermemory/types/document_list_params.py +++ b/src/supermemory/types/document_list_params.py @@ -2,15 +2,13 @@ from __future__ import annotations -from typing import Union -from typing_extensions import Literal, Annotated, TypeAlias, TypedDict +from typing import Union, Iterable +from typing_extensions import Literal, Required, Annotated, TypeAlias, TypedDict from .._types import SequenceNotStr from .._utils import PropertyInfo -from .shared_params.or_ import Or -from .shared_params.and_ import And -__all__ = ["DocumentListParams", "Filters"] +__all__ = ["DocumentListParams", "Filters", "FiltersOr", "FiltersAnd"] class DocumentListParams(TypedDict, total=False): @@ -43,4 +41,12 @@ class DocumentListParams(TypedDict, total=False): """Field to sort by""" -Filters: TypeAlias = Union[Or, And] +class FiltersOr(TypedDict, total=False): + or_: Required[Annotated[Iterable[object], PropertyInfo(alias="OR")]] + + +class FiltersAnd(TypedDict, total=False): + and_: Required[Annotated[Iterable[object], PropertyInfo(alias="AND")]] + + +Filters: TypeAlias = Union[FiltersOr, FiltersAnd] diff --git a/src/supermemory/types/memory_list_params.py b/src/supermemory/types/memory_list_params.py index 07eb8d9a..48eb7f6d 100644 --- a/src/supermemory/types/memory_list_params.py +++ b/src/supermemory/types/memory_list_params.py @@ -2,15 +2,13 @@ from __future__ import annotations -from typing import Union -from typing_extensions import Literal, Annotated, TypeAlias, TypedDict +from typing import Union, Iterable +from typing_extensions import Literal, Required, Annotated, TypeAlias, TypedDict from .._types import SequenceNotStr from .._utils import PropertyInfo -from .shared_params.or_ import Or -from .shared_params.and_ import And -__all__ = ["MemoryListParams", "Filters"] +__all__ = ["MemoryListParams", "Filters", "FiltersOr", "FiltersAnd"] class MemoryListParams(TypedDict, total=False): @@ -43,4 +41,12 @@ class MemoryListParams(TypedDict, total=False): """Field to sort by""" -Filters: TypeAlias = Union[Or, And] +class FiltersOr(TypedDict, total=False): + or_: Required[Annotated[Iterable[object], PropertyInfo(alias="OR")]] + + +class FiltersAnd(TypedDict, total=False): + and_: Required[Annotated[Iterable[object], PropertyInfo(alias="AND")]] + + +Filters: TypeAlias = Union[FiltersOr, FiltersAnd] diff --git a/src/supermemory/types/profile_response.py b/src/supermemory/types/profile_response.py new file mode 100644 index 00000000..3eb5e2b0 --- /dev/null +++ b/src/supermemory/types/profile_response.py @@ -0,0 +1,35 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = ["ProfileResponse", "Profile", "SearchResults"] + + +class Profile(BaseModel): + dynamic: List[str] + """Dynamic profile information (recent memories)""" + + static: List[str] + """Static profile information that remains relevant long-term""" + + +class SearchResults(BaseModel): + results: List[object] + """Search results for the provided query""" + + timing: float + """Search timing in milliseconds""" + + total: float + """Total number of search results""" + + +class ProfileResponse(BaseModel): + profile: Profile + + search_results: Optional[SearchResults] = FieldInfo(alias="searchResults", default=None) + """Search results if a search query was provided""" diff --git a/src/supermemory/types/search_documents_params.py b/src/supermemory/types/search_documents_params.py index 20f62b3c..2b88d453 100644 --- a/src/supermemory/types/search_documents_params.py +++ b/src/supermemory/types/search_documents_params.py @@ -2,15 +2,13 @@ from __future__ import annotations -from typing import Union +from typing import Union, Iterable from typing_extensions import Required, Annotated, TypeAlias, TypedDict from .._types import SequenceNotStr from .._utils import PropertyInfo -from .shared_params.or_ import Or -from .shared_params.and_ import And -__all__ = ["SearchDocumentsParams", "Filters"] +__all__ = ["SearchDocumentsParams", "Filters", "FiltersOr", "FiltersAnd"] class SearchDocumentsParams(TypedDict, total=False): @@ -84,4 +82,12 @@ class SearchDocumentsParams(TypedDict, total=False): """ -Filters: TypeAlias = Union[Or, And] +class FiltersOr(TypedDict, total=False): + or_: Required[Annotated[Iterable[object], PropertyInfo(alias="OR")]] + + +class FiltersAnd(TypedDict, total=False): + and_: Required[Annotated[Iterable[object], 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 da786065..9a49c448 100644 --- a/src/supermemory/types/search_execute_params.py +++ b/src/supermemory/types/search_execute_params.py @@ -2,15 +2,13 @@ from __future__ import annotations -from typing import Union +from typing import Union, Iterable from typing_extensions import Required, Annotated, TypeAlias, TypedDict from .._types import SequenceNotStr from .._utils import PropertyInfo -from .shared_params.or_ import Or -from .shared_params.and_ import And -__all__ = ["SearchExecuteParams", "Filters"] +__all__ = ["SearchExecuteParams", "Filters", "FiltersOr", "FiltersAnd"] class SearchExecuteParams(TypedDict, total=False): @@ -84,4 +82,12 @@ class SearchExecuteParams(TypedDict, total=False): """ -Filters: TypeAlias = Union[Or, And] +class FiltersOr(TypedDict, total=False): + or_: Required[Annotated[Iterable[object], PropertyInfo(alias="OR")]] + + +class FiltersAnd(TypedDict, total=False): + and_: Required[Annotated[Iterable[object], 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 cca09bc1..20328dc7 100644 --- a/src/supermemory/types/search_memories_params.py +++ b/src/supermemory/types/search_memories_params.py @@ -2,14 +2,12 @@ from __future__ import annotations -from typing import Union +from typing import Union, Iterable from typing_extensions import Required, Annotated, TypeAlias, TypedDict from .._utils import PropertyInfo -from .shared_params.or_ import Or -from .shared_params.and_ import And -__all__ = ["SearchMemoriesParams", "Filters", "Include"] +__all__ = ["SearchMemoriesParams", "Filters", "FiltersOr", "FiltersAnd", "Include"] class SearchMemoriesParams(TypedDict, total=False): @@ -51,7 +49,15 @@ class SearchMemoriesParams(TypedDict, total=False): """ -Filters: TypeAlias = Union[Or, And] +class FiltersOr(TypedDict, total=False): + or_: Required[Annotated[Iterable[object], PropertyInfo(alias="OR")]] + + +class FiltersAnd(TypedDict, total=False): + and_: Required[Annotated[Iterable[object], PropertyInfo(alias="AND")]] + + +Filters: TypeAlias = Union[FiltersOr, FiltersAnd] class Include(TypedDict, total=False): diff --git a/src/supermemory/types/shared/__init__.py b/src/supermemory/types/shared/__init__.py deleted file mode 100644 index 96ecf13e..00000000 --- a/src/supermemory/types/shared/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from .or_ import Or as Or -from .and_ import And as And diff --git a/src/supermemory/types/shared/and_.py b/src/supermemory/types/shared/and_.py deleted file mode 100644 index 980dbb24..00000000 --- a/src/supermemory/types/shared/and_.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import List - -from pydantic import Field as FieldInfo - -from ..._models import BaseModel - -__all__ = ["And"] - - -class And(BaseModel): - and_: List[object] = FieldInfo(alias="AND") diff --git a/src/supermemory/types/shared/or_.py b/src/supermemory/types/shared/or_.py deleted file mode 100644 index fe2c5a95..00000000 --- a/src/supermemory/types/shared/or_.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import List - -from pydantic import Field as FieldInfo - -from ..._models import BaseModel - -__all__ = ["Or"] - - -class Or(BaseModel): - or_: List[object] = FieldInfo(alias="OR") diff --git a/src/supermemory/types/shared_params/__init__.py b/src/supermemory/types/shared_params/__init__.py deleted file mode 100644 index 96ecf13e..00000000 --- a/src/supermemory/types/shared_params/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from .or_ import Or as Or -from .and_ import And as And diff --git a/src/supermemory/types/shared_params/and_.py b/src/supermemory/types/shared_params/and_.py deleted file mode 100644 index 0e0dad21..00000000 --- a/src/supermemory/types/shared_params/and_.py +++ /dev/null @@ -1,14 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Iterable -from typing_extensions import Required, Annotated, TypedDict - -from ..._utils import PropertyInfo - -__all__ = ["And"] - - -class And(TypedDict, total=False): - and_: Required[Annotated[Iterable[object], PropertyInfo(alias="AND")]] diff --git a/src/supermemory/types/shared_params/or_.py b/src/supermemory/types/shared_params/or_.py deleted file mode 100644 index fdb83e1f..00000000 --- a/src/supermemory/types/shared_params/or_.py +++ /dev/null @@ -1,14 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Iterable -from typing_extensions import Required, Annotated, TypedDict - -from ..._utils import PropertyInfo - -__all__ = ["Or"] - - -class Or(TypedDict, total=False): - or_: Required[Annotated[Iterable[object], PropertyInfo(alias="OR")]] diff --git a/tests/api_resources/test_client.py b/tests/api_resources/test_client.py new file mode 100644 index 00000000..37b1c63e --- /dev/null +++ b/tests/api_resources/test_client.py @@ -0,0 +1,202 @@ +# 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 AddResponse, ProfileResponse + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestClient: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_add(self, client: Supermemory) -> None: + client_ = client.add( + content="content", + ) + assert_matches_type(AddResponse, client_, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_add_with_all_params(self, client: Supermemory) -> None: + client_ = client.add( + content="content", + container_tag="containerTag", + container_tags=["string"], + custom_id="customId", + metadata={"foo": "string"}, + ) + assert_matches_type(AddResponse, client_, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_add(self, client: Supermemory) -> None: + response = client.with_raw_response.add( + content="content", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + client_ = response.parse() + assert_matches_type(AddResponse, client_, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_add(self, client: Supermemory) -> None: + with client.with_streaming_response.add( + content="content", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + client_ = response.parse() + assert_matches_type(AddResponse, client_, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_profile(self, client: Supermemory) -> None: + client_ = client.profile( + container_tag="containerTag", + ) + assert_matches_type(ProfileResponse, client_, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_profile_with_all_params(self, client: Supermemory) -> None: + client_ = client.profile( + container_tag="containerTag", + q="q", + ) + assert_matches_type(ProfileResponse, client_, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_profile(self, client: Supermemory) -> None: + response = client.with_raw_response.profile( + container_tag="containerTag", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + client_ = response.parse() + assert_matches_type(ProfileResponse, client_, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_profile(self, client: Supermemory) -> None: + with client.with_streaming_response.profile( + container_tag="containerTag", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + client_ = response.parse() + assert_matches_type(ProfileResponse, client_, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncClient: + 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_add(self, async_client: AsyncSupermemory) -> None: + client = await async_client.add( + content="content", + ) + assert_matches_type(AddResponse, client, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_add_with_all_params(self, async_client: AsyncSupermemory) -> None: + client = await async_client.add( + content="content", + container_tag="containerTag", + container_tags=["string"], + custom_id="customId", + metadata={"foo": "string"}, + ) + assert_matches_type(AddResponse, client, 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.with_raw_response.add( + content="content", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + client = await response.parse() + assert_matches_type(AddResponse, client, 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.with_streaming_response.add( + content="content", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + client = await response.parse() + assert_matches_type(AddResponse, client, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_profile(self, async_client: AsyncSupermemory) -> None: + client = await async_client.profile( + container_tag="containerTag", + ) + assert_matches_type(ProfileResponse, client, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_profile_with_all_params(self, async_client: AsyncSupermemory) -> None: + client = await async_client.profile( + container_tag="containerTag", + q="q", + ) + assert_matches_type(ProfileResponse, client, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_profile(self, async_client: AsyncSupermemory) -> None: + response = await async_client.with_raw_response.profile( + container_tag="containerTag", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + client = await response.parse() + assert_matches_type(ProfileResponse, client, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_profile(self, async_client: AsyncSupermemory) -> None: + async with async_client.with_streaming_response.profile( + container_tag="containerTag", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + client = await response.parse() + assert_matches_type(ProfileResponse, client, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/test_client.py b/tests/test_client.py index aa1ea71c..3d65c9a7 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -747,7 +747,7 @@ def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter, clien respx_mock.post("/v3/documents").mock(side_effect=httpx.TimeoutException("Test timeout error")) with pytest.raises(APITimeoutError): - client.memories.with_streaming_response.add(content="content").__enter__() + client.with_streaming_response.add(content="content").__enter__() assert _get_open_connections(client) == 0 @@ -757,7 +757,7 @@ def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter, client respx_mock.post("/v3/documents").mock(return_value=httpx.Response(500)) with pytest.raises(APIStatusError): - client.memories.with_streaming_response.add(content="content").__enter__() + client.with_streaming_response.add(content="content").__enter__() assert _get_open_connections(client) == 0 @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) @@ -786,7 +786,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: respx_mock.post("/v3/documents").mock(side_effect=retry_handler) - response = client.memories.with_raw_response.add(content="content") + response = client.with_raw_response.add(content="content") assert response.retries_taken == failures_before_success assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success @@ -810,9 +810,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: respx_mock.post("/v3/documents").mock(side_effect=retry_handler) - response = client.memories.with_raw_response.add( - content="content", extra_headers={"x-stainless-retry-count": Omit()} - ) + response = client.with_raw_response.add(content="content", extra_headers={"x-stainless-retry-count": Omit()}) assert len(response.http_request.headers.get_list("x-stainless-retry-count")) == 0 @@ -835,9 +833,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: respx_mock.post("/v3/documents").mock(side_effect=retry_handler) - response = client.memories.with_raw_response.add( - content="content", extra_headers={"x-stainless-retry-count": "42"} - ) + response = client.with_raw_response.add(content="content", extra_headers={"x-stainless-retry-count": "42"}) assert response.http_request.headers.get("x-stainless-retry-count") == "42" @@ -1587,7 +1583,7 @@ async def test_retrying_timeout_errors_doesnt_leak( respx_mock.post("/v3/documents").mock(side_effect=httpx.TimeoutException("Test timeout error")) with pytest.raises(APITimeoutError): - await async_client.memories.with_streaming_response.add(content="content").__aenter__() + await async_client.with_streaming_response.add(content="content").__aenter__() assert _get_open_connections(async_client) == 0 @@ -1599,7 +1595,7 @@ async def test_retrying_status_errors_doesnt_leak( respx_mock.post("/v3/documents").mock(return_value=httpx.Response(500)) with pytest.raises(APIStatusError): - await async_client.memories.with_streaming_response.add(content="content").__aenter__() + await async_client.with_streaming_response.add(content="content").__aenter__() assert _get_open_connections(async_client) == 0 @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) @@ -1628,7 +1624,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: respx_mock.post("/v3/documents").mock(side_effect=retry_handler) - response = await client.memories.with_raw_response.add(content="content") + response = await client.with_raw_response.add(content="content") assert response.retries_taken == failures_before_success assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success @@ -1652,7 +1648,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: respx_mock.post("/v3/documents").mock(side_effect=retry_handler) - response = await client.memories.with_raw_response.add( + response = await client.with_raw_response.add( content="content", extra_headers={"x-stainless-retry-count": Omit()} ) @@ -1677,7 +1673,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: respx_mock.post("/v3/documents").mock(side_effect=retry_handler) - response = await client.memories.with_raw_response.add( + response = await client.with_raw_response.add( content="content", extra_headers={"x-stainless-retry-count": "42"} )