diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 51acdaa..8ea07c9 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.37.0" + ".": "0.38.0" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index 485fba7..abe6bc3 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 11 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/perplexity-ai/perplexity-bf28ac7f1a24d27aa2ba57b65aaf7fc99f492d28ab969f570d86abc339c842e9.yml -openapi_spec_hash: 2f086349fe584965e24e58bddbb91672 -config_hash: 059988c88f0dc18e9e393daed94b5eb6 +configured_endpoints: 13 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/perplexity-ai/perplexity-6a9e741f72f36fa6c1f8ece842ba1616376107badbfdeaf6e88c5db7b9412437.yml +openapi_spec_hash: 76d7c6e6e7f84a1de30b869a7579e6b6 +config_hash: 3f1487a29a16f85810ba4d77134da232 diff --git a/CHANGELOG.md b/CHANGELOG.md index 57fab1d..85a8354 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 0.38.0 (2026-06-08) + +Full Changelog: [v0.37.0...v0.38.0](https://github.com/perplexityai/perplexity-py/compare/v0.37.0...v0.38.0) + +### Features + +* **responses:** add files subresource for sandbox file retrieval ([0dbfd9c](https://github.com/perplexityai/perplexity-py/commit/0dbfd9ce064505723a5dce4b40be6a766a08a733)) + ## 0.37.0 (2026-06-02) Full Changelog: [v0.36.0...v0.37.0](https://github.com/perplexityai/perplexity-py/compare/v0.36.0...v0.37.0) diff --git a/api.md b/api.md index 4ab7679..dd1c80f 100644 --- a/api.md +++ b/api.md @@ -58,6 +58,8 @@ from perplexity.types import ( FunctionToolParam, InputItemParam, OutputItem, + ResponseFile, + ResponseFileList, ResponseStreamChunk, ResponseCreateParams, ResponsesUsage, @@ -68,8 +70,15 @@ from perplexity.types import ( Methods: -- client.responses.create(\*\*params) -> ResponseCreateResponse -- client.responses.retrieve(response_id) -> ResponseRetrieveResponse +- client.responses.create(\*\*params) -> ResponseCreateResponse +- client.responses.retrieve(response_id) -> ResponseRetrieveResponse + +## Files + +Methods: + +- client.responses.files.list(response_id) -> ResponseFileList +- client.responses.files.content(file_id, \*, response_id) -> BinaryAPIResponse # Embeddings diff --git a/pyproject.toml b/pyproject.toml index e7a28d6..f0a9b38 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "perplexityai" -version = "0.37.0" +version = "0.38.0" description = "The official Python library for the perplexity API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/perplexity/_client.py b/src/perplexity/_client.py index 0cb2bfe..1699f8c 100644 --- a/src/perplexity/_client.py +++ b/src/perplexity/_client.py @@ -38,10 +38,10 @@ from .resources import chat, async_, search, browser, responses, embeddings, contextualized_embeddings from .resources.search import SearchResource, AsyncSearchResource from .resources.chat.chat import ChatResource, AsyncChatResource - from .resources.responses import ResponsesResource, AsyncResponsesResource from .resources.embeddings import EmbeddingsResource, AsyncEmbeddingsResource from .resources.async_.async_ import AsyncResource, AsyncAsyncResource from .resources.browser.browser import BrowserResource, AsyncBrowserResource + from .resources.responses.responses import ResponsesResource, AsyncResponsesResource from .resources.contextualized_embeddings import ( ContextualizedEmbeddingsResource, AsyncContextualizedEmbeddingsResource, diff --git a/src/perplexity/_version.py b/src/perplexity/_version.py index 862f58f..d00b853 100644 --- a/src/perplexity/_version.py +++ b/src/perplexity/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "perplexity" -__version__ = "0.37.0" # x-release-please-version +__version__ = "0.38.0" # x-release-please-version diff --git a/src/perplexity/resources/responses/__init__.py b/src/perplexity/resources/responses/__init__.py new file mode 100644 index 0000000..41233da --- /dev/null +++ b/src/perplexity/resources/responses/__init__.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .files import ( + FilesResource, + AsyncFilesResource, + FilesResourceWithRawResponse, + AsyncFilesResourceWithRawResponse, + FilesResourceWithStreamingResponse, + AsyncFilesResourceWithStreamingResponse, +) +from .responses import ( + ResponsesResource, + AsyncResponsesResource, + ResponsesResourceWithRawResponse, + AsyncResponsesResourceWithRawResponse, + ResponsesResourceWithStreamingResponse, + AsyncResponsesResourceWithStreamingResponse, +) + +__all__ = [ + "FilesResource", + "AsyncFilesResource", + "FilesResourceWithRawResponse", + "AsyncFilesResourceWithRawResponse", + "FilesResourceWithStreamingResponse", + "AsyncFilesResourceWithStreamingResponse", + "ResponsesResource", + "AsyncResponsesResource", + "ResponsesResourceWithRawResponse", + "AsyncResponsesResourceWithRawResponse", + "ResponsesResourceWithStreamingResponse", + "AsyncResponsesResourceWithStreamingResponse", +] diff --git a/src/perplexity/resources/responses/files.py b/src/perplexity/resources/responses/files.py new file mode 100644 index 0000000..6ec62ca --- /dev/null +++ b/src/perplexity/resources/responses/files.py @@ -0,0 +1,272 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from ..._types import Body, Query, Headers, NotGiven, not_given +from ..._utils import path_template +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 ..._base_client import make_request_options +from ...types.response_file_list import ResponseFileList + +__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/perplexityai/perplexity-py#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/perplexityai/perplexity-py#with_streaming_response + """ + return FilesResourceWithStreamingResponse(self) + + def list( + self, + response_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, + ) -> ResponseFileList: + """ + Lists the files the model delivered via the share_file tool during this + response. + + 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 response_id: + raise ValueError(f"Expected a non-empty value for `response_id` but received {response_id!r}") + return self._get( + path_template("/v1/responses/{response_id}/files", response_id=response_id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ResponseFileList, + ) + + def content( + self, + file_id: str, + *, + response_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, + ) -> BinaryAPIResponse: + """Streams the raw bytes of one shared file. + + Content-Disposition carries the + original filename. + + 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 response_id: + raise ValueError(f"Expected a non-empty value for `response_id` but received {response_id!r}") + 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( + path_template( + "/v1/responses/{response_id}/files/{file_id}/content", response_id=response_id, file_id=file_id + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=BinaryAPIResponse, + ) + + +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/perplexityai/perplexity-py#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/perplexityai/perplexity-py#with_streaming_response + """ + return AsyncFilesResourceWithStreamingResponse(self) + + async def list( + self, + response_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, + ) -> ResponseFileList: + """ + Lists the files the model delivered via the share_file tool during this + response. + + 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 response_id: + raise ValueError(f"Expected a non-empty value for `response_id` but received {response_id!r}") + return await self._get( + path_template("/v1/responses/{response_id}/files", response_id=response_id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ResponseFileList, + ) + + async def content( + self, + file_id: str, + *, + response_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, + ) -> AsyncBinaryAPIResponse: + """Streams the raw bytes of one shared file. + + Content-Disposition carries the + original filename. + + 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 response_id: + raise ValueError(f"Expected a non-empty value for `response_id` but received {response_id!r}") + 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( + path_template( + "/v1/responses/{response_id}/files/{file_id}/content", response_id=response_id, file_id=file_id + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=AsyncBinaryAPIResponse, + ) + + +class FilesResourceWithRawResponse: + def __init__(self, files: FilesResource) -> None: + self._files = files + + self.list = to_raw_response_wrapper( + files.list, + ) + self.content = to_custom_raw_response_wrapper( + files.content, + BinaryAPIResponse, + ) + + +class AsyncFilesResourceWithRawResponse: + def __init__(self, files: AsyncFilesResource) -> None: + self._files = files + + self.list = async_to_raw_response_wrapper( + files.list, + ) + self.content = async_to_custom_raw_response_wrapper( + files.content, + AsyncBinaryAPIResponse, + ) + + +class FilesResourceWithStreamingResponse: + def __init__(self, files: FilesResource) -> None: + self._files = files + + self.list = to_streamed_response_wrapper( + files.list, + ) + self.content = to_custom_streamed_response_wrapper( + files.content, + StreamedBinaryAPIResponse, + ) + + +class AsyncFilesResourceWithStreamingResponse: + def __init__(self, files: AsyncFilesResource) -> None: + self._files = files + + self.list = async_to_streamed_response_wrapper( + files.list, + ) + self.content = async_to_custom_streamed_response_wrapper( + files.content, + AsyncStreamedBinaryAPIResponse, + ) diff --git a/src/perplexity/resources/responses.py b/src/perplexity/resources/responses/responses.py similarity index 94% rename from src/perplexity/resources/responses.py rename to src/perplexity/resources/responses/responses.py index 66f7912..cec1a10 100644 --- a/src/perplexity/resources/responses.py +++ b/src/perplexity/resources/responses/responses.py @@ -7,29 +7,41 @@ import httpx -from ..types import response_create_params -from .._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given -from .._utils import path_template, required_args, maybe_transform, async_maybe_transform -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import ( +from .files import ( + FilesResource, + AsyncFilesResource, + FilesResourceWithRawResponse, + AsyncFilesResourceWithRawResponse, + FilesResourceWithStreamingResponse, + AsyncFilesResourceWithStreamingResponse, +) +from ...types import response_create_params +from ..._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given +from ..._utils import path_template, required_args, maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( to_raw_response_wrapper, to_streamed_response_wrapper, async_to_raw_response_wrapper, async_to_streamed_response_wrapper, ) -from .._streaming import Stream, AsyncStream -from .._base_client import make_request_options -from ..types.input_item_param import InputItemParam -from ..types.response_stream_chunk import ResponseStreamChunk -from ..types.response_create_response import ResponseCreateResponse -from ..types.response_retrieve_response import ResponseRetrieveResponse -from ..types.shared_params.response_format import ResponseFormat +from ..._streaming import Stream, AsyncStream +from ..._base_client import make_request_options +from ...types.input_item_param import InputItemParam +from ...types.response_stream_chunk import ResponseStreamChunk +from ...types.response_create_response import ResponseCreateResponse +from ...types.response_retrieve_response import ResponseRetrieveResponse +from ...types.shared_params.response_format import ResponseFormat __all__ = ["ResponsesResource", "AsyncResponsesResource"] class ResponsesResource(SyncAPIResource): + @cached_property + def files(self) -> FilesResource: + return FilesResource(self._client) + @cached_property def with_raw_response(self) -> ResponsesResourceWithRawResponse: """ @@ -358,6 +370,10 @@ def retrieve( class AsyncResponsesResource(AsyncAPIResource): + @cached_property + def files(self) -> AsyncFilesResource: + return AsyncFilesResource(self._client) + @cached_property def with_raw_response(self) -> AsyncResponsesResourceWithRawResponse: """ @@ -696,6 +712,10 @@ def __init__(self, responses: ResponsesResource) -> None: responses.retrieve, ) + @cached_property + def files(self) -> FilesResourceWithRawResponse: + return FilesResourceWithRawResponse(self._responses.files) + class AsyncResponsesResourceWithRawResponse: def __init__(self, responses: AsyncResponsesResource) -> None: @@ -708,6 +728,10 @@ def __init__(self, responses: AsyncResponsesResource) -> None: responses.retrieve, ) + @cached_property + def files(self) -> AsyncFilesResourceWithRawResponse: + return AsyncFilesResourceWithRawResponse(self._responses.files) + class ResponsesResourceWithStreamingResponse: def __init__(self, responses: ResponsesResource) -> None: @@ -720,6 +744,10 @@ def __init__(self, responses: ResponsesResource) -> None: responses.retrieve, ) + @cached_property + def files(self) -> FilesResourceWithStreamingResponse: + return FilesResourceWithStreamingResponse(self._responses.files) + class AsyncResponsesResourceWithStreamingResponse: def __init__(self, responses: AsyncResponsesResource) -> None: @@ -731,3 +759,7 @@ def __init__(self, responses: AsyncResponsesResource) -> None: self.retrieve = async_to_streamed_response_wrapper( responses.retrieve, ) + + @cached_property + def files(self) -> AsyncFilesResourceWithStreamingResponse: + return AsyncFilesResourceWithStreamingResponse(self._responses.files) diff --git a/src/perplexity/types/__init__.py b/src/perplexity/types/__init__.py index bb213f7..eb8c539 100644 --- a/src/perplexity/types/__init__.py +++ b/src/perplexity/types/__init__.py @@ -23,8 +23,10 @@ from .output_item import OutputItem as OutputItem from .content_part import ContentPart as ContentPart from .stream_chunk import StreamChunk as StreamChunk +from .response_file import ResponseFile as ResponseFile from .responses_usage import ResponsesUsage as ResponsesUsage from .input_item_param import InputItemParam as InputItemParam +from .response_file_list import ResponseFileList as ResponseFileList from .function_tool_param import FunctionToolParam as FunctionToolParam from .search_create_params import SearchCreateParams as SearchCreateParams from .response_stream_chunk import ResponseStreamChunk as ResponseStreamChunk diff --git a/src/perplexity/types/response_file.py b/src/perplexity/types/response_file.py new file mode 100644 index 0000000..65af3ba --- /dev/null +++ b/src/perplexity/types/response_file.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from .._models import BaseModel + +__all__ = ["ResponseFile"] + + +class ResponseFile(BaseModel): + """Metadata for a file shared from a response sandbox via the share_file tool.""" + + id: str + """File identifier, scoped to the parent response.""" + + bytes: int + """Size of the file in bytes.""" + + created_at: int + """Unix timestamp (seconds) when the file was produced.""" + + filename: str + """Original filename set by the share_file tool call.""" + + object: Literal["file"] + """Object type. Always `file`.""" diff --git a/src/perplexity/types/response_file_list.py b/src/perplexity/types/response_file_list.py new file mode 100644 index 0000000..fe5bd79 --- /dev/null +++ b/src/perplexity/types/response_file_list.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List +from typing_extensions import Literal + +from .._models import BaseModel +from .response_file import ResponseFile + +__all__ = ["ResponseFileList"] + + +class ResponseFileList(BaseModel): + data: List[ResponseFile] + + object: Literal["list"] + """Object type. Always `list`.""" diff --git a/src/perplexity/types/responses/__init__.py b/src/perplexity/types/responses/__init__.py new file mode 100644 index 0000000..f8ee8b1 --- /dev/null +++ b/src/perplexity/types/responses/__init__.py @@ -0,0 +1,3 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations diff --git a/tests/api_resources/responses/__init__.py b/tests/api_resources/responses/__init__.py new file mode 100644 index 0000000..fd8019a --- /dev/null +++ b/tests/api_resources/responses/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/responses/test_files.py b/tests/api_resources/responses/test_files.py new file mode 100644 index 0000000..f30e8c2 --- /dev/null +++ b/tests/api_resources/responses/test_files.py @@ -0,0 +1,248 @@ +# 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 perplexity import Perplexity, AsyncPerplexity +from tests.utils import assert_matches_type +from perplexity.types import ResponseFileList +from perplexity._response import ( + BinaryAPIResponse, + AsyncBinaryAPIResponse, + StreamedBinaryAPIResponse, + AsyncStreamedBinaryAPIResponse, +) + +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(reason="Mock server tests are disabled") + @parametrize + def test_method_list(self, client: Perplexity) -> None: + file = client.responses.files.list( + "response_id", + ) + assert_matches_type(ResponseFileList, file, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_raw_response_list(self, client: Perplexity) -> None: + response = client.responses.files.with_raw_response.list( + "response_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + file = response.parse() + assert_matches_type(ResponseFileList, file, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_streaming_response_list(self, client: Perplexity) -> None: + with client.responses.files.with_streaming_response.list( + "response_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + file = response.parse() + assert_matches_type(ResponseFileList, file, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_path_params_list(self, client: Perplexity) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `response_id` but received ''"): + client.responses.files.with_raw_response.list( + "", + ) + + @parametrize + @pytest.mark.respx(base_url=base_url) + def test_method_content(self, client: Perplexity, respx_mock: MockRouter) -> None: + respx_mock.get("/v1/responses/response_id/files/file_id/content").mock( + return_value=httpx.Response(200, json={"foo": "bar"}) + ) + file = client.responses.files.content( + file_id="file_id", + response_id="response_id", + ) + assert file.is_closed + assert file.json() == {"foo": "bar"} + assert cast(Any, file.is_closed) is True + assert isinstance(file, BinaryAPIResponse) + + @parametrize + @pytest.mark.respx(base_url=base_url) + def test_raw_response_content(self, client: Perplexity, respx_mock: MockRouter) -> None: + respx_mock.get("/v1/responses/response_id/files/file_id/content").mock( + return_value=httpx.Response(200, json={"foo": "bar"}) + ) + + file = client.responses.files.with_raw_response.content( + file_id="file_id", + response_id="response_id", + ) + + 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) + + @parametrize + @pytest.mark.respx(base_url=base_url) + def test_streaming_response_content(self, client: Perplexity, respx_mock: MockRouter) -> None: + respx_mock.get("/v1/responses/response_id/files/file_id/content").mock( + return_value=httpx.Response(200, json={"foo": "bar"}) + ) + with client.responses.files.with_streaming_response.content( + file_id="file_id", + response_id="response_id", + ) 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 + + @parametrize + @pytest.mark.respx(base_url=base_url) + def test_path_params_content(self, client: Perplexity) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `response_id` but received ''"): + client.responses.files.with_raw_response.content( + file_id="file_id", + response_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_id` but received ''"): + client.responses.files.with_raw_response.content( + file_id="", + response_id="response_id", + ) + + +class TestAsyncFiles: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_list(self, async_client: AsyncPerplexity) -> None: + file = await async_client.responses.files.list( + "response_id", + ) + assert_matches_type(ResponseFileList, file, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_raw_response_list(self, async_client: AsyncPerplexity) -> None: + response = await async_client.responses.files.with_raw_response.list( + "response_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + file = await response.parse() + assert_matches_type(ResponseFileList, file, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_streaming_response_list(self, async_client: AsyncPerplexity) -> None: + async with async_client.responses.files.with_streaming_response.list( + "response_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(ResponseFileList, file, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_path_params_list(self, async_client: AsyncPerplexity) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `response_id` but received ''"): + await async_client.responses.files.with_raw_response.list( + "", + ) + + @parametrize + @pytest.mark.respx(base_url=base_url) + async def test_method_content(self, async_client: AsyncPerplexity, respx_mock: MockRouter) -> None: + respx_mock.get("/v1/responses/response_id/files/file_id/content").mock( + return_value=httpx.Response(200, json={"foo": "bar"}) + ) + file = await async_client.responses.files.content( + file_id="file_id", + response_id="response_id", + ) + assert file.is_closed + assert await file.json() == {"foo": "bar"} + assert cast(Any, file.is_closed) is True + assert isinstance(file, AsyncBinaryAPIResponse) + + @parametrize + @pytest.mark.respx(base_url=base_url) + async def test_raw_response_content(self, async_client: AsyncPerplexity, respx_mock: MockRouter) -> None: + respx_mock.get("/v1/responses/response_id/files/file_id/content").mock( + return_value=httpx.Response(200, json={"foo": "bar"}) + ) + + file = await async_client.responses.files.with_raw_response.content( + file_id="file_id", + response_id="response_id", + ) + + 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) + + @parametrize + @pytest.mark.respx(base_url=base_url) + async def test_streaming_response_content(self, async_client: AsyncPerplexity, respx_mock: MockRouter) -> None: + respx_mock.get("/v1/responses/response_id/files/file_id/content").mock( + return_value=httpx.Response(200, json={"foo": "bar"}) + ) + async with async_client.responses.files.with_streaming_response.content( + file_id="file_id", + response_id="response_id", + ) 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 + + @parametrize + @pytest.mark.respx(base_url=base_url) + async def test_path_params_content(self, async_client: AsyncPerplexity) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `response_id` but received ''"): + await async_client.responses.files.with_raw_response.content( + file_id="file_id", + response_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_id` but received ''"): + await async_client.responses.files.with_raw_response.content( + file_id="", + response_id="response_id", + )