From 41a600aeceb84b5c32a7d771acc6acd1cca97b8e Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 27 Jun 2025 02:32:10 +0000 Subject: [PATCH 1/6] =?UTF-8?q?fix(ci):=20release-doctor=20=E2=80=94=20rep?= =?UTF-8?q?ort=20correct=20token=20name?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bin/check-release-environment | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/check-release-environment b/bin/check-release-environment index 2ba897b5f..b845b0f4c 100644 --- a/bin/check-release-environment +++ b/bin/check-release-environment @@ -3,7 +3,7 @@ errors=() if [ -z "${PYPI_TOKEN}" ]; then - errors+=("The RUNLOOP_PYPI_TOKEN secret has not been set. Please set it in either this repository's secrets or your organization secrets.") + errors+=("The PYPI_TOKEN secret has not been set. Please set it in either this repository's secrets or your organization secrets.") fi lenErrors=${#errors[@]} From c914e94ea65e9d4180810cc3b816f4ab4a2a8e22 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 28 Jun 2025 08:43:15 +0000 Subject: [PATCH 2/6] chore(ci): only run for pushes and fork pull requests --- .github/workflows/ci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 98974d6cf..d4c5f6399 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,6 +17,7 @@ jobs: timeout-minutes: 10 name: lint runs-on: ${{ github.repository == 'stainless-sdks/runloop-python' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} + if: github.event_name == 'push' || github.event.pull_request.head.repo.fork steps: - uses: actions/checkout@v4 @@ -42,6 +43,7 @@ jobs: contents: read id-token: write runs-on: depot-ubuntu-24.04 + if: github.event_name == 'push' || github.event.pull_request.head.repo.fork steps: - uses: actions/checkout@v4 @@ -62,6 +64,7 @@ jobs: timeout-minutes: 10 name: test runs-on: ${{ github.repository == 'stainless-sdks/runloop-python' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} + if: github.event_name == 'push' || github.event.pull_request.head.repo.fork steps: - uses: actions/checkout@v4 From 2005e4ff9ca9bcb569ef4798bd490e036ad1afd5 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 30 Jun 2025 02:28:42 +0000 Subject: [PATCH 3/6] fix(ci): correct conditional --- .github/workflows/ci.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d4c5f6399..a6a934d47 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,14 +36,13 @@ jobs: run: ./scripts/lint upload: - if: github.repository == 'stainless-sdks/runloop-python' + if: github.repository == 'stainless-sdks/runloop-python' && (github.event_name == 'push' || github.event.pull_request.head.repo.fork) timeout-minutes: 10 name: upload permissions: contents: read id-token: write runs-on: depot-ubuntu-24.04 - if: github.event_name == 'push' || github.event.pull_request.head.repo.fork steps: - uses: actions/checkout@v4 From f8b3493a584b10a91fe1e7d543fe4c0de406cf7d Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 1 Jul 2025 00:38:31 +0000 Subject: [PATCH 4/6] feat(api): api update --- .stats.yml | 6 +++--- README.md | 6 +++--- src/runloop_api_client/_constants.py | 8 ++++---- .../resources/devboxes/devboxes.py | 16 ++++++++++++++++ .../resources/devboxes/executions.py | 7 ++++++- src/runloop_api_client/types/scoring_function.py | 3 +++ .../types/scoring_function_param.py | 3 +++ tests/test_client.py | 8 ++++---- 8 files changed, 42 insertions(+), 15 deletions(-) diff --git a/.stats.yml b/.stats.yml index a615cde74..b752c3f19 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 92 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/runloop-ai%2Frunloop-1d4b018cbfb409baf83725737f00789a9ddbbdbdbe12db1ac7a1fd2cf9c453f0.yml -openapi_spec_hash: 1c533f386f6d3d3802d353cc6f1df547 -config_hash: 33b544375e4a932cbb2890f79a9aa040 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/runloop-ai%2Frunloop-75c0dc94dd629772c98b3e6f763d8b3a1bdb732aa3eb92d49a59fb446fb2410d.yml +openapi_spec_hash: 12286e648ea1156bfa23020ed0c7598b +config_hash: e722b7242ee36143533531c419043a6f diff --git a/README.md b/README.md index eb5672f4d..afc66e029 100644 --- a/README.md +++ b/README.md @@ -245,7 +245,7 @@ Error codes are as follows: ### Retries -Certain errors are automatically retried 0 times by default, with a short exponential backoff. +Certain errors are automatically retried 3 times by default, with a short exponential backoff. Connection errors (for example, due to a network connectivity problem), 408 Request Timeout, 409 Conflict, 429 Rate Limit, and >=500 Internal errors are all retried by default. @@ -266,7 +266,7 @@ client.with_options(max_retries=5).devboxes.create() ### Timeouts -By default requests time out after 1 minute. You can configure this with a `timeout` option, +By default requests time out after 30 seconds. You can configure this with a `timeout` option, which accepts a float or an [`httpx.Timeout`](https://www.python-httpx.org/advanced/timeouts/#fine-tuning-the-configuration) object: ```python @@ -274,7 +274,7 @@ from runloop_api_client import Runloop # Configure the default for all requests: client = Runloop( - # 20 seconds (default is 1 minute) + # 20 seconds (default is 30 seconds) timeout=20.0, ) diff --git a/src/runloop_api_client/_constants.py b/src/runloop_api_client/_constants.py index 6be0f5e04..d1ccc0109 100644 --- a/src/runloop_api_client/_constants.py +++ b/src/runloop_api_client/_constants.py @@ -5,10 +5,10 @@ RAW_RESPONSE_HEADER = "X-Stainless-Raw-Response" OVERRIDE_CAST_TO_HEADER = "____stainless_override_cast_to" -# default timeout is 1 minute -DEFAULT_TIMEOUT = httpx.Timeout(timeout=60, connect=5.0) -DEFAULT_MAX_RETRIES = 0 +# default timeout is 30 seconds +DEFAULT_TIMEOUT = httpx.Timeout(timeout=30, connect=5.0) +DEFAULT_MAX_RETRIES = 3 DEFAULT_CONNECTION_LIMITS = httpx.Limits(max_connections=100, max_keepalive_connections=20) INITIAL_RETRY_DELAY = 1.0 -MAX_RETRY_DELAY = 10.0 +MAX_RETRY_DELAY = 15.0 diff --git a/src/runloop_api_client/resources/devboxes/devboxes.py b/src/runloop_api_client/resources/devboxes/devboxes.py index 4d4c41709..fbff382d6 100644 --- a/src/runloop_api_client/resources/devboxes/devboxes.py +++ b/src/runloop_api_client/resources/devboxes/devboxes.py @@ -691,6 +691,8 @@ def download_file( """ if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + if not is_given(timeout) and self._client.timeout == DEFAULT_TIMEOUT: + timeout = 600 extra_headers = {"Accept": "application/octet-stream", **(extra_headers or {})} return self._post( f"/v1/devboxes/{id}/download_file", @@ -962,6 +964,8 @@ def read_file_contents( """ if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + if not is_given(timeout) and self._client.timeout == DEFAULT_TIMEOUT: + timeout = 600 extra_headers = {"Accept": "text/plain", **(extra_headers or {})} return self._post( f"/v1/devboxes/{id}/read_file_contents", @@ -1297,6 +1301,8 @@ def upload_file( """ if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + if not is_given(timeout) and self._client.timeout == DEFAULT_TIMEOUT: + timeout = 600 body = deepcopy_minimal( { "path": path, @@ -1359,6 +1365,8 @@ def write_file_contents( """ if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + if not is_given(timeout) and self._client.timeout == DEFAULT_TIMEOUT: + timeout = 600 return self._post( f"/v1/devboxes/{id}/write_file_contents", body=maybe_transform( @@ -1954,6 +1962,8 @@ async def download_file( """ if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + if not is_given(timeout) and self._client.timeout == DEFAULT_TIMEOUT: + timeout = 600 extra_headers = {"Accept": "application/octet-stream", **(extra_headers or {})} return await self._post( f"/v1/devboxes/{id}/download_file", @@ -2225,6 +2235,8 @@ async def read_file_contents( """ if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + if not is_given(timeout) and self._client.timeout == DEFAULT_TIMEOUT: + timeout = 600 extra_headers = {"Accept": "text/plain", **(extra_headers or {})} return await self._post( f"/v1/devboxes/{id}/read_file_contents", @@ -2560,6 +2572,8 @@ async def upload_file( """ if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + if not is_given(timeout) and self._client.timeout == DEFAULT_TIMEOUT: + timeout = 600 body = deepcopy_minimal( { "path": path, @@ -2622,6 +2636,8 @@ async def write_file_contents( """ if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + if not is_given(timeout) and self._client.timeout == DEFAULT_TIMEOUT: + timeout = 600 return await self._post( f"/v1/devboxes/{id}/write_file_contents", body=await async_maybe_transform( diff --git a/src/runloop_api_client/resources/devboxes/executions.py b/src/runloop_api_client/resources/devboxes/executions.py index 1515be695..ad7df57e5 100755 --- a/src/runloop_api_client/resources/devboxes/executions.py +++ b/src/runloop_api_client/resources/devboxes/executions.py @@ -7,7 +7,7 @@ import httpx from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from ..._utils import maybe_transform, async_maybe_transform +from ..._utils import is_given, maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import ( @@ -17,6 +17,7 @@ async_to_streamed_response_wrapper, ) from ...lib.polling import PollingConfig, poll_until +from ..._constants import DEFAULT_TIMEOUT from ..._base_client import make_request_options from ...types.devboxes import execution_retrieve_params, execution_execute_sync_params, execution_execute_async_params from ...lib.polling_async import async_poll_until @@ -235,6 +236,8 @@ def execute_sync( """ if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + if not is_given(timeout) and self._client.timeout == DEFAULT_TIMEOUT: + timeout = 600 return self._post( f"/v1/devboxes/{id}/execute_sync", body=maybe_transform( @@ -510,6 +513,8 @@ async def execute_sync( """ if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + if not is_given(timeout) and self._client.timeout == DEFAULT_TIMEOUT: + timeout = 600 return await self._post( f"/v1/devboxes/{id}/execute_sync", body=await async_maybe_transform( diff --git a/src/runloop_api_client/types/scoring_function.py b/src/runloop_api_client/types/scoring_function.py index 9d5cf655a..c49ff61e4 100644 --- a/src/runloop_api_client/types/scoring_function.py +++ b/src/runloop_api_client/types/scoring_function.py @@ -74,6 +74,9 @@ class ScorerPythonScriptScoringFunction(BaseModel): type: Literal["python_script_scorer"] + python_version_constraint: Optional[str] = None + """Python version to run scoring. Default is "==3.12.10" """ + requirements_contents: Optional[str] = None """Package dependencies to be installed. diff --git a/src/runloop_api_client/types/scoring_function_param.py b/src/runloop_api_client/types/scoring_function_param.py index bfbddb21b..1dedcc557 100644 --- a/src/runloop_api_client/types/scoring_function_param.py +++ b/src/runloop_api_client/types/scoring_function_param.py @@ -73,6 +73,9 @@ class ScorerPythonScriptScoringFunction(TypedDict, total=False): type: Required[Literal["python_script_scorer"]] + python_version_constraint: Optional[str] + """Python version to run scoring. Default is "==3.12.10" """ + requirements_contents: str """Package dependencies to be installed. diff --git a/tests/test_client.py b/tests/test_client.py index 8f3f70514..9542e6d4b 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -93,7 +93,7 @@ def test_copy_default_options(self) -> None: # options that have a default are overridden correctly copied = self.client.copy(max_retries=7) assert copied.max_retries == 7 - assert self.client.max_retries == 0 + assert self.client.max_retries == 3 copied2 = copied.copy(max_retries=6) assert copied2.max_retries == 6 @@ -754,7 +754,7 @@ class Model(BaseModel): [3, "", 1], [2, "", 1 * 2.0], [1, "", 1 * 4.0], - [-1100, "", 10], # test large number potentially overflowing + [-1100, "", 15], # test large number potentially overflowing ], ) @mock.patch("time.time", mock.MagicMock(return_value=1696004797)) @@ -949,7 +949,7 @@ def test_copy_default_options(self) -> None: # options that have a default are overridden correctly copied = self.client.copy(max_retries=7) assert copied.max_retries == 7 - assert self.client.max_retries == 0 + assert self.client.max_retries == 3 copied2 = copied.copy(max_retries=6) assert copied2.max_retries == 6 @@ -1613,7 +1613,7 @@ class Model(BaseModel): [3, "", 1], [2, "", 1 * 2.0], [1, "", 1 * 4.0], - [-1100, "", 10], # test large number potentially overflowing + [-1100, "", 15], # test large number potentially overflowing ], ) @mock.patch("time.time", mock.MagicMock(return_value=1696004797)) From 22bcec7d21f9cdb8ef1e6a34ea1831ad4c013ef7 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 1 Jul 2025 01:01:21 +0000 Subject: [PATCH 5/6] codegen metadata --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index b752c3f19..212d59968 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 92 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/runloop-ai%2Frunloop-75c0dc94dd629772c98b3e6f763d8b3a1bdb732aa3eb92d49a59fb446fb2410d.yml openapi_spec_hash: 12286e648ea1156bfa23020ed0c7598b -config_hash: e722b7242ee36143533531c419043a6f +config_hash: 5a04741d17a98f68dc474a29c516d3f8 From b07e239b9c319c39c886cddc5cc1a30d2222fedb Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 1 Jul 2025 01:01:40 +0000 Subject: [PATCH 6/6] release: 0.46.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 19 +++++++++++++++++++ pyproject.toml | 2 +- src/runloop_api_client/_version.py | 2 +- 4 files changed, 22 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index fc0d7ff8b..563004f25 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.45.0" + ".": "0.46.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 5eb9442bc..3db9c93f8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,24 @@ # Changelog +## 0.46.0 (2025-07-01) + +Full Changelog: [v0.45.0...v0.46.0](https://github.com/runloopai/api-client-python/compare/v0.45.0...v0.46.0) + +### Features + +* **api:** api update ([f8b3493](https://github.com/runloopai/api-client-python/commit/f8b3493a584b10a91fe1e7d543fe4c0de406cf7d)) + + +### Bug Fixes + +* **ci:** correct conditional ([2005e4f](https://github.com/runloopai/api-client-python/commit/2005e4ff9ca9bcb569ef4798bd490e036ad1afd5)) +* **ci:** release-doctor — report correct token name ([41a600a](https://github.com/runloopai/api-client-python/commit/41a600aeceb84b5c32a7d771acc6acd1cca97b8e)) + + +### Chores + +* **ci:** only run for pushes and fork pull requests ([c914e94](https://github.com/runloopai/api-client-python/commit/c914e94ea65e9d4180810cc3b816f4ab4a2a8e22)) + ## 0.45.0 (2025-06-24) Full Changelog: [v0.44.0...v0.45.0](https://github.com/runloopai/api-client-python/compare/v0.44.0...v0.45.0) diff --git a/pyproject.toml b/pyproject.toml index 33cf23a70..fcc69280b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "runloop_api_client" -version = "0.45.0" +version = "0.46.0" description = "The official Python library for the runloop API" dynamic = ["readme"] license = "MIT" diff --git a/src/runloop_api_client/_version.py b/src/runloop_api_client/_version.py index 0bac585fa..f66dbcb44 100644 --- a/src/runloop_api_client/_version.py +++ b/src/runloop_api_client/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "runloop_api_client" -__version__ = "0.45.0" # x-release-please-version +__version__ = "0.46.0" # x-release-please-version