From 05dcaaeb6b617cc0e87ac0c984c1c325c2def30f Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Thu, 9 Nov 2023 18:46:43 +0000 Subject: [PATCH] release: 1.2.1 (#754) * refactor(client): deprecate files.retrieve_content in favour of files.content (#753) The latter supports binary response types more elegantly. * docs(readme): fix nested params example (#756) * release: 1.2.1 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 13 +++++ README.md | 11 +++- api.md | 1 + pyproject.toml | 2 +- src/openai/_version.py | 2 +- src/openai/resources/files.py | 85 +++++++++++++++++++++++++++++-- tests/api_resources/test_files.py | 73 ++++++++++++++++++++++---- 8 files changed, 170 insertions(+), 19 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index d0ab6645f5..d43a621a8e 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.2.0" + ".": "1.2.1" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b58f41340..1911aef970 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## 1.2.1 (2023-11-09) + +Full Changelog: [v1.2.0...v1.2.1](https://github.com/openai/openai-python/compare/v1.2.0...v1.2.1) + +### Documentation + +* **readme:** fix nested params example ([#756](https://github.com/openai/openai-python/issues/756)) ([ffbe5ec](https://github.com/openai/openai-python/commit/ffbe5eca0f8790ebcdb27ffe845da178a3ef4c45)) + + +### Refactors + +* **client:** deprecate files.retrieve_content in favour of files.content ([#753](https://github.com/openai/openai-python/issues/753)) ([eea5bc1](https://github.com/openai/openai-python/commit/eea5bc173466f63a6e84bd2d741b4873ca056b4c)) + ## 1.2.0 (2023-11-08) Full Changelog: [v1.1.2...v1.2.0](https://github.com/openai/openai-python/compare/v1.1.2...v1.2.0) diff --git a/README.md b/README.md index cedbc72337..11a1236b5a 100644 --- a/README.md +++ b/README.md @@ -237,7 +237,16 @@ from openai import OpenAI client = OpenAI() -page = client.files.list() +completion = client.chat.completions.create( + messages=[ + { + "role": "user", + "content": "Can you generate an example json object describing a fruit?", + } + ], + model="gpt-3.5-turbo", + response_format={"type": "json_object"}, +) ``` ## File Uploads diff --git a/api.md b/api.md index e0237803de..a7ee177411 100644 --- a/api.md +++ b/api.md @@ -87,6 +87,7 @@ Methods: - client.files.retrieve(file_id) -> FileObject - client.files.list(\*\*params) -> SyncPage[FileObject] - client.files.delete(file_id) -> FileDeleted +- client.files.content(file_id) -> HttpxBinaryResponseContent - client.files.retrieve_content(file_id) -> str - client.files.wait_for_processing(\*args) -> FileObject diff --git a/pyproject.toml b/pyproject.toml index 1900794dfc..844f501c45 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.2.0" +version = "1.2.1" description = "Client library for the openai API" readme = "README.md" license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 9d7e588fcf..46c55958e6 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. __title__ = "openai" -__version__ = "1.2.0" # x-release-please-version +__version__ = "1.2.1" # x-release-please-version diff --git a/src/openai/resources/files.py b/src/openai/resources/files.py index b317845c3a..a6f75e5a4c 100644 --- a/src/openai/resources/files.py +++ b/src/openai/resources/files.py @@ -3,6 +3,7 @@ from __future__ import annotations import time +import typing_extensions from typing import TYPE_CHECKING, Mapping, cast from typing_extensions import Literal @@ -14,7 +15,11 @@ from .._resource import SyncAPIResource, AsyncAPIResource from .._response import to_raw_response_wrapper, async_to_raw_response_wrapper from ..pagination import SyncPage, AsyncPage -from .._base_client import AsyncPaginator, make_request_options +from .._base_client import ( + AsyncPaginator, + HttpxBinaryResponseContent, + make_request_options, +) if TYPE_CHECKING: from .._client import OpenAI, AsyncOpenAI @@ -197,6 +202,38 @@ def delete( cast_to=FileDeleted, ) + def content( + 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, + ) -> HttpxBinaryResponseContent: + """ + Returns the contents of the specified file. + + 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 + """ + return self._get( + f"/files/{file_id}/content", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=HttpxBinaryResponseContent, + ) + + @typing_extensions.deprecated("The `.content()` method should be used instead") def retrieve_content( self, file_id: str, @@ -428,6 +465,38 @@ async def delete( cast_to=FileDeleted, ) + async def content( + 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, + ) -> HttpxBinaryResponseContent: + """ + Returns the contents of the specified file. + + 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 + """ + return await self._get( + f"/files/{file_id}/content", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=HttpxBinaryResponseContent, + ) + + @typing_extensions.deprecated("The `.content()` method should be used instead") async def retrieve_content( self, file_id: str, @@ -498,8 +567,11 @@ def __init__(self, files: Files) -> None: self.delete = to_raw_response_wrapper( files.delete, ) - self.retrieve_content = to_raw_response_wrapper( - files.retrieve_content, + self.content = to_raw_response_wrapper( + files.content, + ) + self.retrieve_content = to_raw_response_wrapper( # pyright: ignore[reportDeprecated] + files.retrieve_content # pyright: ignore[reportDeprecated], ) @@ -517,6 +589,9 @@ def __init__(self, files: AsyncFiles) -> None: self.delete = async_to_raw_response_wrapper( files.delete, ) - self.retrieve_content = async_to_raw_response_wrapper( - files.retrieve_content, + self.content = async_to_raw_response_wrapper( + files.content, + ) + self.retrieve_content = async_to_raw_response_wrapper( # pyright: ignore[reportDeprecated] + files.retrieve_content # pyright: ignore[reportDeprecated], ) diff --git a/tests/api_resources/test_files.py b/tests/api_resources/test_files.py index d668c2d0c7..a2c9d07314 100644 --- a/tests/api_resources/test_files.py +++ b/tests/api_resources/test_files.py @@ -4,14 +4,19 @@ import os +import httpx import pytest +from respx import MockRouter from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type from openai.types import FileObject, FileDeleted +from openai._types import BinaryResponseContent from openai._client import OpenAI, AsyncOpenAI from openai.pagination import SyncPage, AsyncPage +# pyright: reportDeprecated=false + base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") api_key = "My API Key" @@ -91,19 +96,43 @@ def test_raw_response_delete(self, client: OpenAI) -> None: assert_matches_type(FileDeleted, file, path=["response"]) @parametrize - def test_method_retrieve_content(self, client: OpenAI) -> None: - file = client.files.retrieve_content( + @pytest.mark.respx(base_url=base_url) + def test_method_content(self, client: OpenAI, respx_mock: MockRouter) -> None: + respx_mock.get("/files/{file_id}/content").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + file = client.files.content( "string", ) - assert_matches_type(str, file, path=["response"]) + assert isinstance(file, BinaryResponseContent) + assert file.json() == {"foo": "bar"} @parametrize - def test_raw_response_retrieve_content(self, client: OpenAI) -> None: - response = client.files.with_raw_response.retrieve_content( + @pytest.mark.respx(base_url=base_url) + def test_raw_response_content(self, client: OpenAI, respx_mock: MockRouter) -> None: + respx_mock.get("/files/{file_id}/content").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + response = client.files.with_raw_response.content( "string", ) assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = response.parse() + assert isinstance(file, BinaryResponseContent) + assert file.json() == {"foo": "bar"} + + @parametrize + def test_method_retrieve_content(self, client: OpenAI) -> None: + with pytest.warns(DeprecationWarning): + file = client.files.retrieve_content( + "string", + ) + assert_matches_type(str, file, path=["response"]) + + @parametrize + def test_raw_response_retrieve_content(self, client: OpenAI) -> None: + with pytest.warns(DeprecationWarning): + response = client.files.with_raw_response.retrieve_content( + "string", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + file = response.parse() assert_matches_type(str, file, path=["response"]) @@ -182,17 +211,41 @@ async def test_raw_response_delete(self, client: AsyncOpenAI) -> None: assert_matches_type(FileDeleted, file, path=["response"]) @parametrize - async def test_method_retrieve_content(self, client: AsyncOpenAI) -> None: - file = await client.files.retrieve_content( + @pytest.mark.respx(base_url=base_url) + async def test_method_content(self, client: AsyncOpenAI, respx_mock: MockRouter) -> None: + respx_mock.get("/files/{file_id}/content").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + file = await client.files.content( "string", ) - assert_matches_type(str, file, path=["response"]) + assert isinstance(file, BinaryResponseContent) + assert file.json() == {"foo": "bar"} @parametrize - async def test_raw_response_retrieve_content(self, client: AsyncOpenAI) -> None: - response = await client.files.with_raw_response.retrieve_content( + @pytest.mark.respx(base_url=base_url) + async def test_raw_response_content(self, client: AsyncOpenAI, respx_mock: MockRouter) -> None: + respx_mock.get("/files/{file_id}/content").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + response = await client.files.with_raw_response.content( "string", ) assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = response.parse() + assert isinstance(file, BinaryResponseContent) + assert file.json() == {"foo": "bar"} + + @parametrize + async def test_method_retrieve_content(self, client: AsyncOpenAI) -> None: + with pytest.warns(DeprecationWarning): + file = await client.files.retrieve_content( + "string", + ) + assert_matches_type(str, file, path=["response"]) + + @parametrize + async def test_raw_response_retrieve_content(self, client: AsyncOpenAI) -> None: + with pytest.warns(DeprecationWarning): + response = await client.files.with_raw_response.retrieve_content( + "string", + ) + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + file = response.parse() assert_matches_type(str, file, path=["response"])