diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 3d2ac0b..10f3091 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.1.0" + ".": "0.2.0" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index 0ecc23f..7349579 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 30 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/replicate%2Freplicate-client-2088d647e7b0fd37dcf58ef48b8f01ed1a82ff797b9697ad10a7b6a5105e9e0f.yml -openapi_spec_hash: 718f540e7c44501e1a8c7156ee45d595 -config_hash: 927b6ebc00ee115763ad69483bbf5566 +configured_endpoints: 35 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/replicate%2Freplicate-client-efbc8cc2d74644b213e161d3e11e0589d1cef181fb318ea02c8eb6b00f245713.yml +openapi_spec_hash: 13da0c06c900b61cd98ab678e024987a +config_hash: 8ef6787524fd12bfeb27f8c6acef3dca diff --git a/CHANGELOG.md b/CHANGELOG.md index 654c222..94d8543 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ # Changelog +## 0.2.0 (2025-05-07) + +Full Changelog: [v0.1.0...v0.2.0](https://github.com/replicate/replicate-python-stainless/compare/v0.1.0...v0.2.0) + +### Features + +* **api:** add Files API methods ([3173e5f](https://github.com/replicate/replicate-python-stainless/commit/3173e5f61edd89ffe0b64b53fc8e8e9905e145e4)) +* **api:** fix bearer token which also regressed when guessing with AI ([13162be](https://github.com/replicate/replicate-python-stainless/commit/13162be9d367de29d222b86506fa921a10800665)) + + +### Bug Fixes + +* **api:** fix client_settings.opts.api_key.read_env ([5a9b95c](https://github.com/replicate/replicate-python-stainless/commit/5a9b95ce89e536b539eefe0864a47784fdb0ec08)) + ## 0.1.0 (2025-05-07) Full Changelog: [v0.1.0-alpha.10...v0.1.0](https://github.com/replicate/replicate-python-stainless/compare/v0.1.0-alpha.10...v0.1.0) diff --git a/README.md b/README.md index 8cfc0e7..23b8b71 100644 --- a/README.md +++ b/README.md @@ -136,6 +136,24 @@ for prediction in first_page.results: # Remove `await` for non-async usage. ``` +## 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 replicate import Replicate + +client = Replicate() + +client.files.create( + content=Path("/path/to/file"), + filename="filename", +) +``` + +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 `replicate.APIConnectionError` is raised. diff --git a/api.md b/api.md index 0afde25..408f49a 100644 --- a/api.md +++ b/api.md @@ -154,3 +154,11 @@ from replicate.types.webhooks.default import SecretGetResponse Methods: - client.webhooks.default.secret.get() -> SecretGetResponse + +# Files + +Types: + +```python +from replicate.types import FileCreateResponse, FileListResponse, FileGetResponse +``` diff --git a/pyproject.toml b/pyproject.toml index 46f2b41..aee2e9a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "replicate-stainless" -version = "0.1.0" +version = "0.2.0" description = "The official Python library for the replicate API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/replicate/__init__.py b/src/replicate/__init__.py index ad8bd3f..7a38cc0 100644 --- a/src/replicate/__init__.py +++ b/src/replicate/__init__.py @@ -230,6 +230,7 @@ def _reset_client() -> None: # type: ignore[reportUnusedFunction] from ._module_client import ( + files as files, models as models, account as account, hardware as hardware, diff --git a/src/replicate/_client.py b/src/replicate/_client.py index 88fc0d9..0fe93c6 100644 --- a/src/replicate/_client.py +++ b/src/replicate/_client.py @@ -31,7 +31,8 @@ ) if TYPE_CHECKING: - from .resources import models, account, hardware, webhooks, trainings, collections, deployments, predictions + from .resources import files, models, account, hardware, webhooks, trainings, collections, deployments, predictions + from .resources.files import FilesResource, AsyncFilesResource from .resources.account import AccountResource, AsyncAccountResource from .resources.hardware import HardwareResource, AsyncHardwareResource from .resources.trainings import TrainingsResource, AsyncTrainingsResource @@ -156,6 +157,12 @@ def webhooks(self) -> WebhooksResource: return WebhooksResource(self) + @cached_property + def files(self) -> FilesResource: + from .resources.files import FilesResource + + return FilesResource(self) + @cached_property def with_raw_response(self) -> ReplicateWithRawResponse: return ReplicateWithRawResponse(self) @@ -372,6 +379,12 @@ def webhooks(self) -> AsyncWebhooksResource: return AsyncWebhooksResource(self) + @cached_property + def files(self) -> AsyncFilesResource: + from .resources.files import AsyncFilesResource + + return AsyncFilesResource(self) + @cached_property def with_raw_response(self) -> AsyncReplicateWithRawResponse: return AsyncReplicateWithRawResponse(self) @@ -539,6 +552,12 @@ def webhooks(self) -> webhooks.WebhooksResourceWithRawResponse: return WebhooksResourceWithRawResponse(self._client.webhooks) + @cached_property + def files(self) -> files.FilesResourceWithRawResponse: + from .resources.files import FilesResourceWithRawResponse + + return FilesResourceWithRawResponse(self._client.files) + class AsyncReplicateWithRawResponse: _client: AsyncReplicate @@ -594,6 +613,12 @@ def webhooks(self) -> webhooks.AsyncWebhooksResourceWithRawResponse: return AsyncWebhooksResourceWithRawResponse(self._client.webhooks) + @cached_property + def files(self) -> files.AsyncFilesResourceWithRawResponse: + from .resources.files import AsyncFilesResourceWithRawResponse + + return AsyncFilesResourceWithRawResponse(self._client.files) + class ReplicateWithStreamedResponse: _client: Replicate @@ -649,6 +674,12 @@ def webhooks(self) -> webhooks.WebhooksResourceWithStreamingResponse: return WebhooksResourceWithStreamingResponse(self._client.webhooks) + @cached_property + def files(self) -> files.FilesResourceWithStreamingResponse: + from .resources.files import FilesResourceWithStreamingResponse + + return FilesResourceWithStreamingResponse(self._client.files) + class AsyncReplicateWithStreamedResponse: _client: AsyncReplicate @@ -704,6 +735,12 @@ def webhooks(self) -> webhooks.AsyncWebhooksResourceWithStreamingResponse: return AsyncWebhooksResourceWithStreamingResponse(self._client.webhooks) + @cached_property + def files(self) -> files.AsyncFilesResourceWithStreamingResponse: + from .resources.files import AsyncFilesResourceWithStreamingResponse + + return AsyncFilesResourceWithStreamingResponse(self._client.files) + Client = Replicate diff --git a/src/replicate/_files.py b/src/replicate/_files.py index 715cc20..d563518 100644 --- a/src/replicate/_files.py +++ b/src/replicate/_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/replicate/replicate-python-stainless/tree/main#file-uploads" ) from None diff --git a/src/replicate/_module_client.py b/src/replicate/_module_client.py index c80ea25..1d90cf0 100644 --- a/src/replicate/_module_client.py +++ b/src/replicate/_module_client.py @@ -6,6 +6,7 @@ from typing_extensions import override if TYPE_CHECKING: + from .resources.files import FilesResource from .resources.account import AccountResource from .resources.hardware import HardwareResource from .resources.trainings import TrainingsResource @@ -19,6 +20,12 @@ from ._utils import LazyProxy +class FilesResourceProxy(LazyProxy["FilesResource"]): + @override + def __load__(self) -> FilesResource: + return _load_client().files + + class ModelsResourceProxy(LazyProxy["ModelsResource"]): @override def __load__(self) -> ModelsResource: @@ -67,6 +74,7 @@ def __load__(self) -> PredictionsResource: return _load_client().predictions +files: FilesResource = FilesResourceProxy().__as_proxied__() models: ModelsResource = ModelsResourceProxy().__as_proxied__() account: AccountResource = AccountResourceProxy().__as_proxied__() hardware: HardwareResource = HardwareResourceProxy().__as_proxied__() diff --git a/src/replicate/_version.py b/src/replicate/_version.py index ba344a8..1f94a83 100644 --- a/src/replicate/_version.py +++ b/src/replicate/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "replicate" -__version__ = "0.1.0" # x-release-please-version +__version__ = "0.2.0" # x-release-please-version diff --git a/src/replicate/resources/__init__.py b/src/replicate/resources/__init__.py index 7982afa..4786942 100644 --- a/src/replicate/resources/__init__.py +++ b/src/replicate/resources/__init__.py @@ -1,5 +1,13 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +from .files import ( + FilesResource, + AsyncFilesResource, + FilesResourceWithRawResponse, + AsyncFilesResourceWithRawResponse, + FilesResourceWithStreamingResponse, + AsyncFilesResourceWithStreamingResponse, +) from .models import ( ModelsResource, AsyncModelsResource, @@ -114,4 +122,10 @@ "AsyncWebhooksResourceWithRawResponse", "WebhooksResourceWithStreamingResponse", "AsyncWebhooksResourceWithStreamingResponse", + "FilesResource", + "AsyncFilesResource", + "FilesResourceWithRawResponse", + "AsyncFilesResourceWithRawResponse", + "FilesResourceWithStreamingResponse", + "AsyncFilesResourceWithStreamingResponse", ] diff --git a/src/replicate/resources/files.py b/src/replicate/resources/files.py new file mode 100644 index 0000000..fdb90c0 --- /dev/null +++ b/src/replicate/resources/files.py @@ -0,0 +1,678 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Mapping, cast + +import httpx + +from ..types import file_create_params, file_download_params +from .._types import NOT_GIVEN, Body, Query, Headers, NoneType, NotGiven, FileTypes +from .._utils import extract_files, maybe_transform, deepcopy_minimal, async_maybe_transform +from .._compat import cached_property +from .._resource import SyncAPIResource, AsyncAPIResource +from .._response import ( + BinaryAPIResponse, + AsyncBinaryAPIResponse, + StreamedBinaryAPIResponse, + AsyncStreamedBinaryAPIResponse, + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + to_custom_raw_response_wrapper, + async_to_streamed_response_wrapper, + to_custom_streamed_response_wrapper, + async_to_custom_raw_response_wrapper, + async_to_custom_streamed_response_wrapper, +) +from ..pagination import SyncCursorURLPage, AsyncCursorURLPage +from .._base_client import AsyncPaginator, make_request_options +from ..types.file_get_response import FileGetResponse +from ..types.file_list_response import FileListResponse +from ..types.file_create_response import FileCreateResponse + +__all__ = ["FilesResource", "AsyncFilesResource"] + + +class FilesResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> FilesResourceWithRawResponse: + """ + 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/replicate/replicate-python-stainless#accessing-raw-response-data-eg-headers + """ + return FilesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> FilesResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/replicate/replicate-python-stainless#with_streaming_response + """ + return FilesResourceWithStreamingResponse(self) + + def create( + self, + *, + content: FileTypes, + filename: str, + metadata: object | NotGiven = NOT_GIVEN, + type: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> FileCreateResponse: + """ + Create a file by uploading its content and optional metadata. + + Example cURL request: + + ```console + curl -X POST https://api.replicate.com/v1/files \\ + -H "Authorization: Token $REPLICATE_API_TOKEN" \\ + -H 'Content-Type: multipart/form-data' \\ + -F 'content=@/path/to/archive.zip;type=application/zip;filename=example.zip' \\ + -F 'metadata={"customer_reference_id": 123};type=application/json' + ``` + + The request must include: + + - `content`: The file content (required) + - `type`: The content / MIME type for the file (defaults to + `application/octet-stream`) + - `filename`: The filename (required, ≤ 255 bytes, valid UTF-8) + - `metadata`: User-provided metadata associated with the file (defaults to `{}`, + must be valid JSON) + + Args: + content: The file content + + filename: The filename + + metadata: User-provided metadata associated with the file + + type: The content / MIME type for the file + + 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( + { + "content": content, + "filename": filename, + "metadata": metadata, + "type": type, + } + ) + files = extract_files(cast(Mapping[str, object], body), paths=[["content"]]) + # 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( + "/files", + body=maybe_transform(body, file_create_params.FileCreateParams), + files=files, + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=FileCreateResponse, + ) + + def list( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> SyncCursorURLPage[FileListResponse]: + """ + Get a paginated list of all files created by the user or organization associated + with the provided API token. + + Example cURL request: + + ```console + curl -s \\ + -H "Authorization: Token $REPLICATE_API_TOKEN" \\ + https://api.replicate.com/v1/files + ``` + + The response will be a paginated JSON array of file objects, sorted with the + most recent file first. + """ + return self._get_api_list( + "/files", + page=SyncCursorURLPage[FileListResponse], + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + model=FileListResponse, + ) + + def delete( + self, + *, + file_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 file. + + Once a file has been deleted, subsequent requests to the file + resource return 404 Not found. + + Example cURL request: + + ```console + curl -X DELETE \\ + -H "Authorization: Token $REPLICATE_API_TOKEN" \\ + https://api.replicate.com/v1/files/cneqzikepnug6xezperrr4z55o + ``` + + 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 file_id: + raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return self._delete( + f"/files/{file_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + def download( + self, + *, + file_id: str, + expiry: int, + owner: str, + signature: 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, + ) -> BinaryAPIResponse: + """ + Download a file by providing the file owner, access expiry, and a valid + signature. + + Example cURL request: + + ```console + curl -X GET "https://api.replicate.com/v1/files/cneqzikepnug6xezperrr4z55o/download?expiry=1708515345&owner=mattt&signature=zuoghqlrcnw8YHywkpaXQlHsVhWen%2FDZ4aal76dLiOo%3D" + ``` + + Args: + expiry: A Unix timestamp with expiration date of this download URL + + owner: The username of the user or organization that uploaded the file + + signature: A base64-encoded HMAC-SHA256 checksum of the string '{owner} {id} {expiry}' + generated with the Files API signing secret + + 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 file_id: + raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") + extra_headers = {"Accept": "application/octet-stream", **(extra_headers or {})} + return self._get( + f"/files/{file_id}/download", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "expiry": expiry, + "owner": owner, + "signature": signature, + }, + file_download_params.FileDownloadParams, + ), + ), + cast_to=BinaryAPIResponse, + ) + + def get( + self, + *, + file_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, + ) -> FileGetResponse: + """ + Get the details of a file. + + Example cURL request: + + ```console + curl -s \\ + -H "Authorization: Token $REPLICATE_API_TOKEN" \\ + https://api.replicate.com/v1/files/cneqzikepnug6xezperrr4z55o + ``` + + 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 file_id: + raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") + return self._get( + f"/files/{file_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=FileGetResponse, + ) + + +class AsyncFilesResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncFilesResourceWithRawResponse: + """ + 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/replicate/replicate-python-stainless#accessing-raw-response-data-eg-headers + """ + return AsyncFilesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncFilesResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/replicate/replicate-python-stainless#with_streaming_response + """ + return AsyncFilesResourceWithStreamingResponse(self) + + async def create( + self, + *, + content: FileTypes, + filename: str, + metadata: object | NotGiven = NOT_GIVEN, + type: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> FileCreateResponse: + """ + Create a file by uploading its content and optional metadata. + + Example cURL request: + + ```console + curl -X POST https://api.replicate.com/v1/files \\ + -H "Authorization: Token $REPLICATE_API_TOKEN" \\ + -H 'Content-Type: multipart/form-data' \\ + -F 'content=@/path/to/archive.zip;type=application/zip;filename=example.zip' \\ + -F 'metadata={"customer_reference_id": 123};type=application/json' + ``` + + The request must include: + + - `content`: The file content (required) + - `type`: The content / MIME type for the file (defaults to + `application/octet-stream`) + - `filename`: The filename (required, ≤ 255 bytes, valid UTF-8) + - `metadata`: User-provided metadata associated with the file (defaults to `{}`, + must be valid JSON) + + Args: + content: The file content + + filename: The filename + + metadata: User-provided metadata associated with the file + + type: The content / MIME type for the file + + 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( + { + "content": content, + "filename": filename, + "metadata": metadata, + "type": type, + } + ) + files = extract_files(cast(Mapping[str, object], body), paths=[["content"]]) + # 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( + "/files", + body=await async_maybe_transform(body, file_create_params.FileCreateParams), + files=files, + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=FileCreateResponse, + ) + + def list( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AsyncPaginator[FileListResponse, AsyncCursorURLPage[FileListResponse]]: + """ + Get a paginated list of all files created by the user or organization associated + with the provided API token. + + Example cURL request: + + ```console + curl -s \\ + -H "Authorization: Token $REPLICATE_API_TOKEN" \\ + https://api.replicate.com/v1/files + ``` + + The response will be a paginated JSON array of file objects, sorted with the + most recent file first. + """ + return self._get_api_list( + "/files", + page=AsyncCursorURLPage[FileListResponse], + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + model=FileListResponse, + ) + + async def delete( + self, + *, + file_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 file. + + Once a file has been deleted, subsequent requests to the file + resource return 404 Not found. + + Example cURL request: + + ```console + curl -X DELETE \\ + -H "Authorization: Token $REPLICATE_API_TOKEN" \\ + https://api.replicate.com/v1/files/cneqzikepnug6xezperrr4z55o + ``` + + 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 file_id: + raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return await self._delete( + f"/files/{file_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + async def download( + self, + *, + file_id: str, + expiry: int, + owner: str, + signature: 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, + ) -> AsyncBinaryAPIResponse: + """ + Download a file by providing the file owner, access expiry, and a valid + signature. + + Example cURL request: + + ```console + curl -X GET "https://api.replicate.com/v1/files/cneqzikepnug6xezperrr4z55o/download?expiry=1708515345&owner=mattt&signature=zuoghqlrcnw8YHywkpaXQlHsVhWen%2FDZ4aal76dLiOo%3D" + ``` + + Args: + expiry: A Unix timestamp with expiration date of this download URL + + owner: The username of the user or organization that uploaded the file + + signature: A base64-encoded HMAC-SHA256 checksum of the string '{owner} {id} {expiry}' + generated with the Files API signing secret + + 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 file_id: + raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") + extra_headers = {"Accept": "application/octet-stream", **(extra_headers or {})} + return await self._get( + f"/files/{file_id}/download", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "expiry": expiry, + "owner": owner, + "signature": signature, + }, + file_download_params.FileDownloadParams, + ), + ), + cast_to=AsyncBinaryAPIResponse, + ) + + async def get( + self, + *, + file_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, + ) -> FileGetResponse: + """ + Get the details of a file. + + Example cURL request: + + ```console + curl -s \\ + -H "Authorization: Token $REPLICATE_API_TOKEN" \\ + https://api.replicate.com/v1/files/cneqzikepnug6xezperrr4z55o + ``` + + 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 file_id: + raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") + return await self._get( + f"/files/{file_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=FileGetResponse, + ) + + +class FilesResourceWithRawResponse: + def __init__(self, files: FilesResource) -> None: + self._files = files + + self.create = to_raw_response_wrapper( + files.create, + ) + self.list = to_raw_response_wrapper( + files.list, + ) + self.delete = to_raw_response_wrapper( + files.delete, + ) + self.download = to_custom_raw_response_wrapper( + files.download, + BinaryAPIResponse, + ) + self.get = to_raw_response_wrapper( + files.get, + ) + + +class AsyncFilesResourceWithRawResponse: + def __init__(self, files: AsyncFilesResource) -> None: + self._files = files + + self.create = async_to_raw_response_wrapper( + files.create, + ) + self.list = async_to_raw_response_wrapper( + files.list, + ) + self.delete = async_to_raw_response_wrapper( + files.delete, + ) + self.download = async_to_custom_raw_response_wrapper( + files.download, + AsyncBinaryAPIResponse, + ) + self.get = async_to_raw_response_wrapper( + files.get, + ) + + +class FilesResourceWithStreamingResponse: + def __init__(self, files: FilesResource) -> None: + self._files = files + + self.create = to_streamed_response_wrapper( + files.create, + ) + self.list = to_streamed_response_wrapper( + files.list, + ) + self.delete = to_streamed_response_wrapper( + files.delete, + ) + self.download = to_custom_streamed_response_wrapper( + files.download, + StreamedBinaryAPIResponse, + ) + self.get = to_streamed_response_wrapper( + files.get, + ) + + +class AsyncFilesResourceWithStreamingResponse: + def __init__(self, files: AsyncFilesResource) -> None: + self._files = files + + self.create = async_to_streamed_response_wrapper( + files.create, + ) + self.list = async_to_streamed_response_wrapper( + files.list, + ) + self.delete = async_to_streamed_response_wrapper( + files.delete, + ) + self.download = async_to_custom_streamed_response_wrapper( + files.download, + AsyncStreamedBinaryAPIResponse, + ) + self.get = async_to_streamed_response_wrapper( + files.get, + ) diff --git a/src/replicate/types/__init__.py b/src/replicate/types/__init__.py index 5edacdb..aa49593 100644 --- a/src/replicate/types/__init__.py +++ b/src/replicate/types/__init__.py @@ -3,10 +3,15 @@ from __future__ import annotations from .prediction import Prediction as Prediction +from .file_get_response import FileGetResponse as FileGetResponse +from .file_create_params import FileCreateParams as FileCreateParams +from .file_list_response import FileListResponse as FileListResponse from .model_create_params import ModelCreateParams as ModelCreateParams from .model_list_response import ModelListResponse as ModelListResponse from .model_search_params import ModelSearchParams as ModelSearchParams from .account_get_response import AccountGetResponse as AccountGetResponse +from .file_create_response import FileCreateResponse as FileCreateResponse +from .file_download_params import FileDownloadParams as FileDownloadParams from .training_get_response import TrainingGetResponse as TrainingGetResponse from .hardware_list_response import HardwareListResponse as HardwareListResponse from .prediction_list_params import PredictionListParams as PredictionListParams diff --git a/src/replicate/types/file_create_params.py b/src/replicate/types/file_create_params.py new file mode 100644 index 0000000..e7c2186 --- /dev/null +++ b/src/replicate/types/file_create_params.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +from .._types import FileTypes + +__all__ = ["FileCreateParams"] + + +class FileCreateParams(TypedDict, total=False): + content: Required[FileTypes] + """The file content""" + + filename: Required[str] + """The filename""" + + metadata: object + """User-provided metadata associated with the file""" + + type: str + """The content / MIME type for the file""" diff --git a/src/replicate/types/file_create_response.py b/src/replicate/types/file_create_response.py new file mode 100644 index 0000000..118c73c --- /dev/null +++ b/src/replicate/types/file_create_response.py @@ -0,0 +1,44 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from datetime import datetime + +from .._models import BaseModel + +__all__ = ["FileCreateResponse", "Checksums", "URLs"] + + +class Checksums(BaseModel): + sha256: Optional[str] = None + """SHA256 checksum of the file""" + + +class URLs(BaseModel): + get: Optional[str] = None + """A URL to the file resource""" + + +class FileCreateResponse(BaseModel): + id: str + """A unique, randomly-generated identifier for the file resource""" + + checksums: Checksums + """A dictionary of checksums for the file keyed by the algorithm name""" + + content_type: str + """The content / MIME type of the file""" + + created_at: datetime + """When the file was created""" + + expires_at: datetime + """When the file expires""" + + metadata: object + """Metadata provided by user when the file was created""" + + size: int + """The length of the file in bytes""" + + urls: URLs + """A dictionary of URLs associated with the file resource""" diff --git a/src/replicate/types/file_download_params.py b/src/replicate/types/file_download_params.py new file mode 100644 index 0000000..c13cd5f --- /dev/null +++ b/src/replicate/types/file_download_params.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["FileDownloadParams"] + + +class FileDownloadParams(TypedDict, total=False): + file_id: Required[str] + + expiry: Required[int] + """A Unix timestamp with expiration date of this download URL""" + + owner: Required[str] + """The username of the user or organization that uploaded the file""" + + signature: Required[str] + """ + A base64-encoded HMAC-SHA256 checksum of the string '{owner} {id} {expiry}' + generated with the Files API signing secret + """ diff --git a/src/replicate/types/file_get_response.py b/src/replicate/types/file_get_response.py new file mode 100644 index 0000000..e8abbf9 --- /dev/null +++ b/src/replicate/types/file_get_response.py @@ -0,0 +1,44 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from datetime import datetime + +from .._models import BaseModel + +__all__ = ["FileGetResponse", "Checksums", "URLs"] + + +class Checksums(BaseModel): + sha256: Optional[str] = None + """SHA256 checksum of the file""" + + +class URLs(BaseModel): + get: Optional[str] = None + """A URL to the file resource""" + + +class FileGetResponse(BaseModel): + id: str + """A unique, randomly-generated identifier for the file resource""" + + checksums: Checksums + """A dictionary of checksums for the file keyed by the algorithm name""" + + content_type: str + """The content / MIME type of the file""" + + created_at: datetime + """When the file was created""" + + expires_at: datetime + """When the file expires""" + + metadata: object + """Metadata provided by user when the file was created""" + + size: int + """The length of the file in bytes""" + + urls: URLs + """A dictionary of URLs associated with the file resource""" diff --git a/src/replicate/types/file_list_response.py b/src/replicate/types/file_list_response.py new file mode 100644 index 0000000..76ac052 --- /dev/null +++ b/src/replicate/types/file_list_response.py @@ -0,0 +1,44 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from datetime import datetime + +from .._models import BaseModel + +__all__ = ["FileListResponse", "Checksums", "URLs"] + + +class Checksums(BaseModel): + sha256: Optional[str] = None + """SHA256 checksum of the file""" + + +class URLs(BaseModel): + get: Optional[str] = None + """A URL to the file resource""" + + +class FileListResponse(BaseModel): + id: str + """A unique, randomly-generated identifier for the file resource""" + + checksums: Checksums + """A dictionary of checksums for the file keyed by the algorithm name""" + + content_type: str + """The content / MIME type of the file""" + + created_at: datetime + """When the file was created""" + + expires_at: datetime + """When the file expires""" + + metadata: object + """Metadata provided by user when the file was created""" + + size: int + """The length of the file in bytes""" + + urls: URLs + """A dictionary of URLs associated with the file resource""" diff --git a/tests/api_resources/test_files.py b/tests/api_resources/test_files.py new file mode 100644 index 0000000..2a773fe --- /dev/null +++ b/tests/api_resources/test_files.py @@ -0,0 +1,487 @@ +# 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 httpx +import pytest +from respx import MockRouter + +from replicate import Replicate, AsyncReplicate +from tests.utils import assert_matches_type +from replicate.types import ( + FileGetResponse, + FileListResponse, + FileCreateResponse, +) +from replicate._response import ( + BinaryAPIResponse, + AsyncBinaryAPIResponse, + StreamedBinaryAPIResponse, + AsyncStreamedBinaryAPIResponse, +) +from replicate.pagination import SyncCursorURLPage, AsyncCursorURLPage + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestFiles: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip() + @parametrize + def test_method_create(self, client: Replicate) -> None: + file = client.files.create( + content=b"raw file contents", + filename="filename", + ) + assert_matches_type(FileCreateResponse, file, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_method_create_with_all_params(self, client: Replicate) -> None: + file = client.files.create( + content=b"raw file contents", + filename="filename", + metadata={}, + type="type", + ) + assert_matches_type(FileCreateResponse, file, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_create(self, client: Replicate) -> None: + response = client.files.with_raw_response.create( + content=b"raw file contents", + filename="filename", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + file = response.parse() + assert_matches_type(FileCreateResponse, file, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_create(self, client: Replicate) -> None: + with client.files.with_streaming_response.create( + content=b"raw file contents", + filename="filename", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + file = response.parse() + assert_matches_type(FileCreateResponse, file, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_method_list(self, client: Replicate) -> None: + file = client.files.list() + assert_matches_type(SyncCursorURLPage[FileListResponse], file, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_list(self, client: Replicate) -> None: + response = client.files.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + file = response.parse() + assert_matches_type(SyncCursorURLPage[FileListResponse], file, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_list(self, client: Replicate) -> None: + with client.files.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + file = response.parse() + assert_matches_type(SyncCursorURLPage[FileListResponse], file, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_method_delete(self, client: Replicate) -> None: + file = client.files.delete( + file_id="file_id", + ) + assert file is None + + @pytest.mark.skip() + @parametrize + def test_raw_response_delete(self, client: Replicate) -> None: + response = client.files.with_raw_response.delete( + file_id="file_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + file = response.parse() + assert file is None + + @pytest.mark.skip() + @parametrize + def test_streaming_response_delete(self, client: Replicate) -> None: + with client.files.with_streaming_response.delete( + file_id="file_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + file = response.parse() + assert file is None + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_path_params_delete(self, client: Replicate) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_id` but received ''"): + client.files.with_raw_response.delete( + file_id="", + ) + + @pytest.mark.skip() + @parametrize + @pytest.mark.respx(base_url=base_url) + def test_method_download(self, client: Replicate, respx_mock: MockRouter) -> None: + respx_mock.get("/files/file_id/download").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + file = client.files.download( + file_id="file_id", + expiry=0, + owner="owner", + signature="signature", + ) + assert file.is_closed + assert file.json() == {"foo": "bar"} + assert cast(Any, file.is_closed) is True + assert isinstance(file, BinaryAPIResponse) + + @pytest.mark.skip() + @parametrize + @pytest.mark.respx(base_url=base_url) + def test_raw_response_download(self, client: Replicate, respx_mock: MockRouter) -> None: + respx_mock.get("/files/file_id/download").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + + file = client.files.with_raw_response.download( + file_id="file_id", + expiry=0, + owner="owner", + signature="signature", + ) + + assert file.is_closed is True + assert file.http_request.headers.get("X-Stainless-Lang") == "python" + assert file.json() == {"foo": "bar"} + assert isinstance(file, BinaryAPIResponse) + + @pytest.mark.skip() + @parametrize + @pytest.mark.respx(base_url=base_url) + def test_streaming_response_download(self, client: Replicate, respx_mock: MockRouter) -> None: + respx_mock.get("/files/file_id/download").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + with client.files.with_streaming_response.download( + file_id="file_id", + expiry=0, + owner="owner", + signature="signature", + ) as file: + assert not file.is_closed + assert file.http_request.headers.get("X-Stainless-Lang") == "python" + + assert file.json() == {"foo": "bar"} + assert cast(Any, file.is_closed) is True + assert isinstance(file, StreamedBinaryAPIResponse) + + assert cast(Any, file.is_closed) is True + + @pytest.mark.skip() + @parametrize + @pytest.mark.respx(base_url=base_url) + def test_path_params_download(self, client: Replicate) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_id` but received ''"): + client.files.with_raw_response.download( + file_id="", + expiry=0, + owner="owner", + signature="signature", + ) + + @pytest.mark.skip() + @parametrize + def test_method_get(self, client: Replicate) -> None: + file = client.files.get( + file_id="file_id", + ) + assert_matches_type(FileGetResponse, file, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_get(self, client: Replicate) -> None: + response = client.files.with_raw_response.get( + file_id="file_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + file = response.parse() + assert_matches_type(FileGetResponse, file, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_get(self, client: Replicate) -> None: + with client.files.with_streaming_response.get( + file_id="file_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + file = response.parse() + assert_matches_type(FileGetResponse, file, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_path_params_get(self, client: Replicate) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_id` but received ''"): + client.files.with_raw_response.get( + file_id="", + ) + + +class TestAsyncFiles: + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip() + @parametrize + async def test_method_create(self, async_client: AsyncReplicate) -> None: + file = await async_client.files.create( + content=b"raw file contents", + filename="filename", + ) + assert_matches_type(FileCreateResponse, file, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncReplicate) -> None: + file = await async_client.files.create( + content=b"raw file contents", + filename="filename", + metadata={}, + type="type", + ) + assert_matches_type(FileCreateResponse, file, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_create(self, async_client: AsyncReplicate) -> None: + response = await async_client.files.with_raw_response.create( + content=b"raw file contents", + filename="filename", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + file = await response.parse() + assert_matches_type(FileCreateResponse, file, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_create(self, async_client: AsyncReplicate) -> None: + async with async_client.files.with_streaming_response.create( + content=b"raw file contents", + filename="filename", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + file = await response.parse() + assert_matches_type(FileCreateResponse, file, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_method_list(self, async_client: AsyncReplicate) -> None: + file = await async_client.files.list() + assert_matches_type(AsyncCursorURLPage[FileListResponse], file, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_list(self, async_client: AsyncReplicate) -> None: + response = await async_client.files.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + file = await response.parse() + assert_matches_type(AsyncCursorURLPage[FileListResponse], file, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_list(self, async_client: AsyncReplicate) -> None: + async with async_client.files.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + file = await response.parse() + assert_matches_type(AsyncCursorURLPage[FileListResponse], file, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_method_delete(self, async_client: AsyncReplicate) -> None: + file = await async_client.files.delete( + file_id="file_id", + ) + assert file is None + + @pytest.mark.skip() + @parametrize + async def test_raw_response_delete(self, async_client: AsyncReplicate) -> None: + response = await async_client.files.with_raw_response.delete( + file_id="file_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + file = await response.parse() + assert file is None + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncReplicate) -> None: + async with async_client.files.with_streaming_response.delete( + file_id="file_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + file = await response.parse() + assert file is None + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_path_params_delete(self, async_client: AsyncReplicate) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_id` but received ''"): + await async_client.files.with_raw_response.delete( + file_id="", + ) + + @pytest.mark.skip() + @parametrize + @pytest.mark.respx(base_url=base_url) + async def test_method_download(self, async_client: AsyncReplicate, respx_mock: MockRouter) -> None: + respx_mock.get("/files/file_id/download").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + file = await async_client.files.download( + file_id="file_id", + expiry=0, + owner="owner", + signature="signature", + ) + assert file.is_closed + assert await file.json() == {"foo": "bar"} + assert cast(Any, file.is_closed) is True + assert isinstance(file, AsyncBinaryAPIResponse) + + @pytest.mark.skip() + @parametrize + @pytest.mark.respx(base_url=base_url) + async def test_raw_response_download(self, async_client: AsyncReplicate, respx_mock: MockRouter) -> None: + respx_mock.get("/files/file_id/download").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + + file = await async_client.files.with_raw_response.download( + file_id="file_id", + expiry=0, + owner="owner", + signature="signature", + ) + + assert file.is_closed is True + assert file.http_request.headers.get("X-Stainless-Lang") == "python" + assert await file.json() == {"foo": "bar"} + assert isinstance(file, AsyncBinaryAPIResponse) + + @pytest.mark.skip() + @parametrize + @pytest.mark.respx(base_url=base_url) + async def test_streaming_response_download(self, async_client: AsyncReplicate, respx_mock: MockRouter) -> None: + respx_mock.get("/files/file_id/download").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + async with async_client.files.with_streaming_response.download( + file_id="file_id", + expiry=0, + owner="owner", + signature="signature", + ) as file: + assert not file.is_closed + assert file.http_request.headers.get("X-Stainless-Lang") == "python" + + assert await file.json() == {"foo": "bar"} + assert cast(Any, file.is_closed) is True + assert isinstance(file, AsyncStreamedBinaryAPIResponse) + + assert cast(Any, file.is_closed) is True + + @pytest.mark.skip() + @parametrize + @pytest.mark.respx(base_url=base_url) + async def test_path_params_download(self, async_client: AsyncReplicate) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_id` but received ''"): + await async_client.files.with_raw_response.download( + file_id="", + expiry=0, + owner="owner", + signature="signature", + ) + + @pytest.mark.skip() + @parametrize + async def test_method_get(self, async_client: AsyncReplicate) -> None: + file = await async_client.files.get( + file_id="file_id", + ) + assert_matches_type(FileGetResponse, file, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_get(self, async_client: AsyncReplicate) -> None: + response = await async_client.files.with_raw_response.get( + file_id="file_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + file = await response.parse() + assert_matches_type(FileGetResponse, file, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_get(self, async_client: AsyncReplicate) -> None: + async with async_client.files.with_streaming_response.get( + file_id="file_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + file = await response.parse() + assert_matches_type(FileGetResponse, file, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_path_params_get(self, async_client: AsyncReplicate) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_id` but received ''"): + await async_client.files.with_raw_response.get( + file_id="", + )