Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -35,7 +36,7 @@ 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:
Expand All @@ -62,6 +63,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

Expand Down
2 changes: 1 addition & 1 deletion .release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
".": "0.45.0"
".": "0.46.0"
}
6 changes: 3 additions & 3 deletions .stats.yml
Original file line number Diff line number Diff line change
@@ -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: 5a04741d17a98f68dc474a29c516d3f8
19 changes: 19 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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)
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand All @@ -266,15 +266,15 @@ 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
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,
)

Expand Down
2 changes: 1 addition & 1 deletion bin/check-release-environment
Original file line number Diff line number Diff line change
Expand Up @@ -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[@]}
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -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"
Expand Down
8 changes: 4 additions & 4 deletions src/runloop_api_client/_constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
2 changes: 1 addition & 1 deletion src/runloop_api_client/_version.py
Original file line number Diff line number Diff line change
@@ -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
16 changes: 16 additions & 0 deletions src/runloop_api_client/resources/devboxes/devboxes.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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(
Expand Down
7 changes: 6 additions & 1 deletion src/runloop_api_client/resources/devboxes/executions.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand All @@ -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
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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(
Expand Down
3 changes: 3 additions & 0 deletions src/runloop_api_client/types/scoring_function.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
3 changes: 3 additions & 0 deletions src/runloop_api_client/types/scoring_function_param.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
8 changes: 4 additions & 4 deletions tests/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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))
Expand Down
Loading