From fcfb1263841cac7a6defadf485fc7c7b128401d4 Mon Sep 17 00:00:00 2001 From: stainless-bot Date: Fri, 23 Aug 2024 21:28:23 +0000 Subject: [PATCH] feat(api): manual updates --- src/runloop_api_client/_client.py | 4 ++ src/runloop_api_client/_streaming.py | 48 ++++++++++++++++++- .../api_resources/devboxes/test_executions.py | 8 ++++ tests/api_resources/devboxes/test_logs.py | 8 ++++ tests/api_resources/test_deployments.py | 8 ++++ tests/test_client.py | 24 ++++++++++ 6 files changed, 98 insertions(+), 2 deletions(-) diff --git a/src/runloop_api_client/_client.py b/src/runloop_api_client/_client.py index f173ecd62..720cd7196 100755 --- a/src/runloop_api_client/_client.py +++ b/src/runloop_api_client/_client.py @@ -108,6 +108,8 @@ def __init__( _strict_response_validation=_strict_response_validation, ) + self._default_stream_cls = Stream + self.blueprints = resources.BlueprintsResource(self) self.deployments = resources.DeploymentsResource(self) self.devboxes = resources.DevboxesResource(self) @@ -284,6 +286,8 @@ def __init__( _strict_response_validation=_strict_response_validation, ) + self._default_stream_cls = AsyncStream + self.blueprints = resources.AsyncBlueprintsResource(self) self.deployments = resources.AsyncDeploymentsResource(self) self.devboxes = resources.AsyncDevboxesResource(self) diff --git a/src/runloop_api_client/_streaming.py b/src/runloop_api_client/_streaming.py index ded05ee4b..7a008d1c2 100755 --- a/src/runloop_api_client/_streaming.py +++ b/src/runloop_api_client/_streaming.py @@ -55,7 +55,29 @@ def __stream__(self) -> Iterator[_T]: iterator = self._iter_events() for sse in iterator: - yield process_data(data=sse.json(), cast_to=cast_to, response=response) + if sse.event == "completion": + yield process_data(data=sse.json(), cast_to=cast_to, response=response) + + if sse.event == "message_start" or sse.event == "content_block_stop": + yield process_data(data=sse.json(), cast_to=cast_to, response=response) + + if sse.event == "ping": + continue + + if sse.event == "error": + body = sse.data + + try: + body = sse.json() + err_msg = f"{body}" + except Exception: + err_msg = sse.data or f"Error code: {response.status_code}" + + raise self._client._make_status_error( + err_msg, + body=body, + response=self.response, + ) # Ensure the entire stream is consumed for _sse in iterator: @@ -119,7 +141,29 @@ async def __stream__(self) -> AsyncIterator[_T]: iterator = self._iter_events() async for sse in iterator: - yield process_data(data=sse.json(), cast_to=cast_to, response=response) + if sse.event == "completion": + yield process_data(data=sse.json(), cast_to=cast_to, response=response) + + if sse.event == "message_start" or sse.event == "content_block_stop": + yield process_data(data=sse.json(), cast_to=cast_to, response=response) + + if sse.event == "ping": + continue + + if sse.event == "error": + body = sse.data + + try: + body = sse.json() + err_msg = f"{body}" + except Exception: + err_msg = sse.data or f"Error code: {response.status_code}" + + raise self._client._make_status_error( + err_msg, + body=body, + response=self.response, + ) # Ensure the entire stream is consumed async for _sse in iterator: diff --git a/tests/api_resources/devboxes/test_executions.py b/tests/api_resources/devboxes/test_executions.py index 665142572..a841221f0 100755 --- a/tests/api_resources/devboxes/test_executions.py +++ b/tests/api_resources/devboxes/test_executions.py @@ -266,6 +266,7 @@ def test_path_params_logs(self, client: Runloop) -> None: id="id", ) + @pytest.mark.skip(reason="cannot test text/event-stream") @parametrize def test_method_tail(self, client: Runloop) -> None: execution = client.devboxes.executions.tail( @@ -274,6 +275,7 @@ def test_method_tail(self, client: Runloop) -> None: ) assert execution is None + @pytest.mark.skip(reason="cannot test text/event-stream") @parametrize def test_raw_response_tail(self, client: Runloop) -> None: response = client.devboxes.executions.with_raw_response.tail( @@ -286,6 +288,7 @@ def test_raw_response_tail(self, client: Runloop) -> None: execution = response.parse() assert execution is None + @pytest.mark.skip(reason="cannot test text/event-stream") @parametrize def test_streaming_response_tail(self, client: Runloop) -> None: with client.devboxes.executions.with_streaming_response.tail( @@ -300,6 +303,7 @@ def test_streaming_response_tail(self, client: Runloop) -> None: assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="cannot test text/event-stream") @parametrize def test_path_params_tail(self, client: Runloop) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -563,6 +567,7 @@ async def test_path_params_logs(self, async_client: AsyncRunloop) -> None: id="id", ) + @pytest.mark.skip(reason="cannot test text/event-stream") @parametrize async def test_method_tail(self, async_client: AsyncRunloop) -> None: execution = await async_client.devboxes.executions.tail( @@ -571,6 +576,7 @@ async def test_method_tail(self, async_client: AsyncRunloop) -> None: ) assert execution is None + @pytest.mark.skip(reason="cannot test text/event-stream") @parametrize async def test_raw_response_tail(self, async_client: AsyncRunloop) -> None: response = await async_client.devboxes.executions.with_raw_response.tail( @@ -583,6 +589,7 @@ async def test_raw_response_tail(self, async_client: AsyncRunloop) -> None: execution = await response.parse() assert execution is None + @pytest.mark.skip(reason="cannot test text/event-stream") @parametrize async def test_streaming_response_tail(self, async_client: AsyncRunloop) -> None: async with async_client.devboxes.executions.with_streaming_response.tail( @@ -597,6 +604,7 @@ async def test_streaming_response_tail(self, async_client: AsyncRunloop) -> None assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="cannot test text/event-stream") @parametrize async def test_path_params_tail(self, async_client: AsyncRunloop) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): diff --git a/tests/api_resources/devboxes/test_logs.py b/tests/api_resources/devboxes/test_logs.py index c0d8bdc24..370066940 100755 --- a/tests/api_resources/devboxes/test_logs.py +++ b/tests/api_resources/devboxes/test_logs.py @@ -55,6 +55,7 @@ def test_path_params_list(self, client: Runloop) -> None: "", ) + @pytest.mark.skip(reason="cannot test text/event-stream") @parametrize def test_method_tail(self, client: Runloop) -> None: log = client.devboxes.logs.tail( @@ -62,6 +63,7 @@ def test_method_tail(self, client: Runloop) -> None: ) assert log is None + @pytest.mark.skip(reason="cannot test text/event-stream") @parametrize def test_raw_response_tail(self, client: Runloop) -> None: response = client.devboxes.logs.with_raw_response.tail( @@ -73,6 +75,7 @@ def test_raw_response_tail(self, client: Runloop) -> None: log = response.parse() assert log is None + @pytest.mark.skip(reason="cannot test text/event-stream") @parametrize def test_streaming_response_tail(self, client: Runloop) -> None: with client.devboxes.logs.with_streaming_response.tail( @@ -86,6 +89,7 @@ def test_streaming_response_tail(self, client: Runloop) -> None: assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="cannot test text/event-stream") @parametrize def test_path_params_tail(self, client: Runloop) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -135,6 +139,7 @@ async def test_path_params_list(self, async_client: AsyncRunloop) -> None: "", ) + @pytest.mark.skip(reason="cannot test text/event-stream") @parametrize async def test_method_tail(self, async_client: AsyncRunloop) -> None: log = await async_client.devboxes.logs.tail( @@ -142,6 +147,7 @@ async def test_method_tail(self, async_client: AsyncRunloop) -> None: ) assert log is None + @pytest.mark.skip(reason="cannot test text/event-stream") @parametrize async def test_raw_response_tail(self, async_client: AsyncRunloop) -> None: response = await async_client.devboxes.logs.with_raw_response.tail( @@ -153,6 +159,7 @@ async def test_raw_response_tail(self, async_client: AsyncRunloop) -> None: log = await response.parse() assert log is None + @pytest.mark.skip(reason="cannot test text/event-stream") @parametrize async def test_streaming_response_tail(self, async_client: AsyncRunloop) -> None: async with async_client.devboxes.logs.with_streaming_response.tail( @@ -166,6 +173,7 @@ async def test_streaming_response_tail(self, async_client: AsyncRunloop) -> None assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="cannot test text/event-stream") @parametrize async def test_path_params_tail(self, async_client: AsyncRunloop) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): diff --git a/tests/api_resources/test_deployments.py b/tests/api_resources/test_deployments.py index 5a83bef98..1c1c11f74 100755 --- a/tests/api_resources/test_deployments.py +++ b/tests/api_resources/test_deployments.py @@ -170,6 +170,7 @@ def test_path_params_redeploy(self, client: Runloop) -> None: "", ) + @pytest.mark.skip(reason="cannot test text/event-stream") @parametrize def test_method_tail(self, client: Runloop) -> None: deployment = client.deployments.tail( @@ -177,6 +178,7 @@ def test_method_tail(self, client: Runloop) -> None: ) assert_matches_type(DeploymentTailResponse, deployment, path=["response"]) + @pytest.mark.skip(reason="cannot test text/event-stream") @parametrize def test_raw_response_tail(self, client: Runloop) -> None: response = client.deployments.with_raw_response.tail( @@ -188,6 +190,7 @@ def test_raw_response_tail(self, client: Runloop) -> None: deployment = response.parse() assert_matches_type(DeploymentTailResponse, deployment, path=["response"]) + @pytest.mark.skip(reason="cannot test text/event-stream") @parametrize def test_streaming_response_tail(self, client: Runloop) -> None: with client.deployments.with_streaming_response.tail( @@ -201,6 +204,7 @@ def test_streaming_response_tail(self, client: Runloop) -> None: assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="cannot test text/event-stream") @parametrize def test_path_params_tail(self, client: Runloop) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `deployment_id` but received ''"): @@ -359,6 +363,7 @@ async def test_path_params_redeploy(self, async_client: AsyncRunloop) -> None: "", ) + @pytest.mark.skip(reason="cannot test text/event-stream") @parametrize async def test_method_tail(self, async_client: AsyncRunloop) -> None: deployment = await async_client.deployments.tail( @@ -366,6 +371,7 @@ async def test_method_tail(self, async_client: AsyncRunloop) -> None: ) assert_matches_type(DeploymentTailResponse, deployment, path=["response"]) + @pytest.mark.skip(reason="cannot test text/event-stream") @parametrize async def test_raw_response_tail(self, async_client: AsyncRunloop) -> None: response = await async_client.deployments.with_raw_response.tail( @@ -377,6 +383,7 @@ async def test_raw_response_tail(self, async_client: AsyncRunloop) -> None: deployment = await response.parse() assert_matches_type(DeploymentTailResponse, deployment, path=["response"]) + @pytest.mark.skip(reason="cannot test text/event-stream") @parametrize async def test_streaming_response_tail(self, async_client: AsyncRunloop) -> None: async with async_client.deployments.with_streaming_response.tail( @@ -390,6 +397,7 @@ async def test_streaming_response_tail(self, async_client: AsyncRunloop) -> None assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="cannot test text/event-stream") @parametrize async def test_path_params_tail(self, async_client: AsyncRunloop) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `deployment_id` but received ''"): diff --git a/tests/test_client.py b/tests/test_client.py index ab10be034..58485333a 100755 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -20,6 +20,7 @@ from runloop_api_client._types import Omit from runloop_api_client._models import BaseModel, FinalRequestOptions from runloop_api_client._constants import RAW_RESPONSE_HEADER +from runloop_api_client._streaming import Stream, AsyncStream from runloop_api_client._exceptions import RunloopError, APIStatusError, APITimeoutError, APIResponseValidationError from runloop_api_client._base_client import ( DEFAULT_TIMEOUT, @@ -685,6 +686,17 @@ def test_client_max_retries_validation(self) -> None: max_retries=cast(Any, None), ) + @pytest.mark.respx(base_url=base_url) + def test_default_stream_cls(self, respx_mock: MockRouter) -> None: + class Model(BaseModel): + name: str + + respx_mock.post("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + + stream = self.client.post("/foo", cast_to=Model, stream=True, stream_cls=Stream[Model]) + assert isinstance(stream, Stream) + stream.response.close() + @pytest.mark.respx(base_url=base_url) def test_received_text_for_expected_json(self, respx_mock: MockRouter) -> None: class Model(BaseModel): @@ -1420,6 +1432,18 @@ async def test_client_max_retries_validation(self) -> None: max_retries=cast(Any, None), ) + @pytest.mark.respx(base_url=base_url) + @pytest.mark.asyncio + async def test_default_stream_cls(self, respx_mock: MockRouter) -> None: + class Model(BaseModel): + name: str + + respx_mock.post("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + + stream = await self.client.post("/foo", cast_to=Model, stream=True, stream_cls=AsyncStream[Model]) + assert isinstance(stream, AsyncStream) + await stream.response.aclose() + @pytest.mark.respx(base_url=base_url) @pytest.mark.asyncio async def test_received_text_for_expected_json(self, respx_mock: MockRouter) -> None: