diff --git a/.gitignore b/.gitignore index 2f716b579..90668e988 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .prism.log +.stdy.log _dev __pycache__ diff --git a/.release-please-manifest.json b/.release-please-manifest.json index ffb929a02..f94eeca26 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.12.1" + ".": "1.13.0" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index ab8cc33c0..f43fecc35 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 117 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/runloop-ai%2Frunloop-0568973e19e8af9fa953b2ded109ab2b69e76e90e2b74f33617dbf7092e26274.yml -openapi_spec_hash: 10ba804ce69510d7985e05c77d0ffcf6 -config_hash: de99cfce88e2d1f02246dc6c2f43bc6c +configured_endpoints: 122 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/runloop-ai%2Frunloop-32e4b2dfb75745be076697d252bd80aff21c08464750928ffe2b7dd997d0b443.yml +openapi_spec_hash: eb0ccabfcda0fb8c56b53939b56f6d80 +config_hash: c422b761c745873bce8fa5ccf03b7b98 diff --git a/CHANGELOG.md b/CHANGELOG.md index 7fa80f3dd..1f7dfb282 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,37 @@ # Changelog +## 1.13.0 (2026-03-24) + +Full Changelog: [v1.12.1...v1.13.0](https://github.com/runloopai/api-client-python/compare/v1.12.1...v1.13.0) + +### Features + +* example for using tunnels ([#754](https://github.com/runloopai/api-client-python/issues/754)) ([97bcd70](https://github.com/runloopai/api-client-python/commit/97bcd701c6aac8077b8dea9cd4d0693d524de9b7)) + + +### Bug Fixes + +* add AxonEventView schema to OpenAPI spec for SSE subscribe endpoint ([#8274](https://github.com/runloopai/api-client-python/issues/8274)) ([618f1bb](https://github.com/runloopai/api-client-python/commit/618f1bb48ff7757e5285cddd083f49ccbbeeed3c)) +* add name to Axon ([#8277](https://github.com/runloopai/api-client-python/issues/8277)) ([7bdb527](https://github.com/runloopai/api-client-python/commit/7bdb527e9011a68f456c927f138fac22d73b3035)) +* sanitize endpoint path params ([5182d36](https://github.com/runloopai/api-client-python/commit/5182d3666b20f4fe5b245201e0b992705443d024)) +* sanitize endpoint path params ([0993026](https://github.com/runloopai/api-client-python/commit/0993026b7dd67b1f7a9a81036c7fc93ab638112d)) +* **tunnels:** allow tunnel removal ([#8257](https://github.com/runloopai/api-client-python/issues/8257)) ([6cff97e](https://github.com/runloopai/api-client-python/commit/6cff97ee43a2f2c906833e9900841e34d478d176)) + + +### Chores + +* **internal:** update gitignore ([96bd6fd](https://github.com/runloopai/api-client-python/commit/96bd6fdc7b767371b21f5753729f01020f9e78ec)) +* remove dead port configuration code, mark deprecated / ignored in the API ([#8195](https://github.com/runloopai/api-client-python/issues/8195)) ([e565324](https://github.com/runloopai/api-client-python/commit/e5653245b65f7afd6210abe1b507ae64151da9b7)) +* **tests:** bump steady to v0.19.4 ([66cb637](https://github.com/runloopai/api-client-python/commit/66cb637779eaea2cacc4b1a06ac3a33f2358b0ee)) +* **tests:** bump steady to v0.19.5 ([601c93a](https://github.com/runloopai/api-client-python/commit/601c93afffb8379770ac895b89235903c8348b00)) +* **tests:** bump steady to v0.19.6 ([95b2ffc](https://github.com/runloopai/api-client-python/commit/95b2ffc5d487f6595ec1dc73b6a34696c309f67d)) + + +### Refactors + +* **tests:** switch from prism to steady ([dd8fe60](https://github.com/runloopai/api-client-python/commit/dd8fe60922808bb0f001b49a99988004195d806f)) +* undeprecate total_count from pagination API, remove remaining_count ([#8084](https://github.com/runloopai/api-client-python/issues/8084)) ([4bd524f](https://github.com/runloopai/api-client-python/commit/4bd524fa32e6aad6efeeabd8a5bbef5e3c7ea742)) + ## 1.12.1 (2026-03-19) Full Changelog: [v1.12.0...v1.12.1](https://github.com/runloopai/api-client-python/compare/v1.12.0...v1.12.1) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index cdf385047..e1abe960c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -85,7 +85,7 @@ $ pip install ./path-to-wheel-file.whl ## Running tests -Most tests require you to [set up a mock server](https://github.com/stoplightio/prism) against the OpenAPI spec to run the tests. +Most tests require you to [set up a mock server](https://github.com/dgellow/steady) against the OpenAPI spec to run the tests. ```sh $ ./scripts/mock diff --git a/api.md b/api.md index 4a2dd3da3..81abd6751 100644 --- a/api.md +++ b/api.md @@ -87,6 +87,29 @@ Methods: - client.agents.retrieve(id) -> AgentView - client.agents.list(\*\*params) -> SyncAgentsCursorIDPage[AgentView] +# Axons + +Types: + +```python +from runloop_api_client.types import ( + AxonCreateParams, + AxonEventView, + AxonListView, + AxonView, + PublishParams, + PublishResultView, +) +``` + +Methods: + +- client.axons.create(\*\*params) -> AxonView +- client.axons.retrieve(id) -> AxonView +- client.axons.list() -> AxonListView +- client.axons.publish(id, \*\*params) -> PublishResultView +- client.axons.subscribe_sse(id) -> AxonEventView + # Blueprints Types: @@ -155,7 +178,7 @@ Methods: - client.devboxes.keep_alive(id) -> object - client.devboxes.list_disk_snapshots(\*\*params) -> SyncDiskSnapshotsCursorIDPage[DevboxSnapshotView] - client.devboxes.read_file_contents(id, \*\*params) -> str -- client.devboxes.remove_tunnel(id, \*\*params) -> object +- client.devboxes.remove_tunnel(id) -> object - client.devboxes.resume(id) -> DevboxView - client.devboxes.retrieve_resource_usage(id) -> DevboxResourceUsageView - client.devboxes.shutdown(id, \*\*params) -> DevboxView diff --git a/pyproject.toml b/pyproject.toml index 1fccbf83b..931df6d19 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "runloop_api_client" -version = "1.12.1" +version = "1.13.0" description = "The official Python library for the runloop API" dynamic = ["readme"] license = "MIT" diff --git a/scripts/mock b/scripts/mock index bcf3b392b..b319bdfbb 100755 --- a/scripts/mock +++ b/scripts/mock @@ -19,34 +19,34 @@ fi echo "==> Starting mock server with URL ${URL}" -# Run prism mock on the given spec +# Run steady mock on the given spec if [ "$1" == "--daemon" ]; then # Pre-install the package so the download doesn't eat into the startup timeout - npm exec --package=@stainless-api/prism-cli@5.15.0 -- prism --version + npm exec --package=@stdy/cli@0.19.6 -- steady --version - npm exec --package=@stainless-api/prism-cli@5.15.0 -- prism mock "$URL" &> .prism.log & + npm exec --package=@stdy/cli@0.19.6 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" &> .stdy.log & - # Wait for server to come online (max 30s) + # Wait for server to come online via health endpoint (max 30s) echo -n "Waiting for server" attempts=0 - while ! grep -q "✖ fatal\|Prism is listening" ".prism.log" ; do + while ! curl --silent --fail "http://127.0.0.1:4010/_x-steady/health" >/dev/null 2>&1; do + if ! kill -0 $! 2>/dev/null; then + echo + cat .stdy.log + exit 1 + fi attempts=$((attempts + 1)) if [ "$attempts" -ge 300 ]; then echo - echo "Timed out waiting for Prism server to start" - cat .prism.log + echo "Timed out waiting for Steady server to start" + cat .stdy.log exit 1 fi echo -n "." sleep 0.1 done - if grep -q "✖ fatal" ".prism.log"; then - cat .prism.log - exit 1 - fi - echo else - npm exec --package=@stainless-api/prism-cli@5.15.0 -- prism mock "$URL" + npm exec --package=@stdy/cli@0.19.6 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" fi diff --git a/scripts/test b/scripts/test index df5c29f74..bc3f79742 100755 --- a/scripts/test +++ b/scripts/test @@ -9,8 +9,8 @@ GREEN='\033[0;32m' YELLOW='\033[0;33m' NC='\033[0m' # No Color -function prism_is_running() { - curl --silent "http://localhost:4010" >/dev/null 2>&1 +function steady_is_running() { + curl --silent "http://127.0.0.1:4010/_x-steady/health" >/dev/null 2>&1 } kill_server_on_port() { @@ -25,7 +25,7 @@ function is_overriding_api_base_url() { [ -n "$TEST_API_BASE_URL" ] } -if ! is_overriding_api_base_url && ! prism_is_running ; then +if ! is_overriding_api_base_url && ! steady_is_running ; then # When we exit this script, make sure to kill the background mock server process trap 'kill_server_on_port 4010' EXIT @@ -36,19 +36,19 @@ fi if is_overriding_api_base_url ; then echo -e "${GREEN}✔ Running tests against ${TEST_API_BASE_URL}${NC}" echo -elif ! prism_is_running ; then - echo -e "${RED}ERROR:${NC} The test suite will not run without a mock Prism server" +elif ! steady_is_running ; then + echo -e "${RED}ERROR:${NC} The test suite will not run without a mock Steady server" echo -e "running against your OpenAPI spec." echo echo -e "To run the server, pass in the path or url of your OpenAPI" - echo -e "spec to the prism command:" + echo -e "spec to the steady command:" echo - echo -e " \$ ${YELLOW}npm exec --package=@stainless-api/prism-cli@5.15.0 -- prism mock path/to/your.openapi.yml${NC}" + echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.19.6 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets${NC}" echo exit 1 else - echo -e "${GREEN}✔ Mock prism server is running with your OpenAPI spec${NC}" + echo -e "${GREEN}✔ Mock steady server is running with your OpenAPI spec${NC}" echo fi diff --git a/src/runloop_api_client/_client.py b/src/runloop_api_client/_client.py index 0f626950b..3b5ab20db 100644 --- a/src/runloop_api_client/_client.py +++ b/src/runloop_api_client/_client.py @@ -32,6 +32,7 @@ if TYPE_CHECKING: from .resources import ( + axons, agents, objects, secrets, @@ -46,6 +47,7 @@ gateway_configs, network_policies, ) + from .resources.axons import AxonsResource, AsyncAxonsResource from .resources.agents import AgentsResource, AsyncAgentsResource from .resources.objects import ObjectsResource, AsyncObjectsResource from .resources.secrets import SecretsResource, AsyncSecretsResource @@ -144,6 +146,12 @@ def agents(self) -> AgentsResource: return AgentsResource(self) + @cached_property + def axons(self) -> AxonsResource: + from .resources.axons import AxonsResource + + return AxonsResource(self) + @cached_property def blueprints(self) -> BlueprintsResource: from .resources.blueprints import BlueprintsResource @@ -392,6 +400,12 @@ def agents(self) -> AsyncAgentsResource: return AsyncAgentsResource(self) + @cached_property + def axons(self) -> AsyncAxonsResource: + from .resources.axons import AsyncAxonsResource + + return AsyncAxonsResource(self) + @cached_property def blueprints(self) -> AsyncBlueprintsResource: from .resources.blueprints import AsyncBlueprintsResource @@ -589,6 +603,12 @@ def agents(self) -> agents.AgentsResourceWithRawResponse: return AgentsResourceWithRawResponse(self._client.agents) + @cached_property + def axons(self) -> axons.AxonsResourceWithRawResponse: + from .resources.axons import AxonsResourceWithRawResponse + + return AxonsResourceWithRawResponse(self._client.axons) + @cached_property def blueprints(self) -> blueprints.BlueprintsResourceWithRawResponse: from .resources.blueprints import BlueprintsResourceWithRawResponse @@ -674,6 +694,12 @@ def agents(self) -> agents.AsyncAgentsResourceWithRawResponse: return AsyncAgentsResourceWithRawResponse(self._client.agents) + @cached_property + def axons(self) -> axons.AsyncAxonsResourceWithRawResponse: + from .resources.axons import AsyncAxonsResourceWithRawResponse + + return AsyncAxonsResourceWithRawResponse(self._client.axons) + @cached_property def blueprints(self) -> blueprints.AsyncBlueprintsResourceWithRawResponse: from .resources.blueprints import AsyncBlueprintsResourceWithRawResponse @@ -759,6 +785,12 @@ def agents(self) -> agents.AgentsResourceWithStreamingResponse: return AgentsResourceWithStreamingResponse(self._client.agents) + @cached_property + def axons(self) -> axons.AxonsResourceWithStreamingResponse: + from .resources.axons import AxonsResourceWithStreamingResponse + + return AxonsResourceWithStreamingResponse(self._client.axons) + @cached_property def blueprints(self) -> blueprints.BlueprintsResourceWithStreamingResponse: from .resources.blueprints import BlueprintsResourceWithStreamingResponse @@ -844,6 +876,12 @@ def agents(self) -> agents.AsyncAgentsResourceWithStreamingResponse: return AsyncAgentsResourceWithStreamingResponse(self._client.agents) + @cached_property + def axons(self) -> axons.AsyncAxonsResourceWithStreamingResponse: + from .resources.axons import AsyncAxonsResourceWithStreamingResponse + + return AsyncAxonsResourceWithStreamingResponse(self._client.axons) + @cached_property def blueprints(self) -> blueprints.AsyncBlueprintsResourceWithStreamingResponse: from .resources.blueprints import AsyncBlueprintsResourceWithStreamingResponse diff --git a/src/runloop_api_client/_utils/__init__.py b/src/runloop_api_client/_utils/__init__.py index 80df24dba..64f8a6d9c 100644 --- a/src/runloop_api_client/_utils/__init__.py +++ b/src/runloop_api_client/_utils/__init__.py @@ -1,4 +1,5 @@ # isort: skip_file +from ._path import path_template as path_template from ._sync import asyncify as asyncify from ._proxy import LazyProxy as LazyProxy from ._utils import ( diff --git a/src/runloop_api_client/_utils/_path.py b/src/runloop_api_client/_utils/_path.py new file mode 100644 index 000000000..4d6e1e4cb --- /dev/null +++ b/src/runloop_api_client/_utils/_path.py @@ -0,0 +1,127 @@ +from __future__ import annotations + +import re +from typing import ( + Any, + Mapping, + Callable, +) +from urllib.parse import quote + +# Matches '.' or '..' where each dot is either literal or percent-encoded (%2e / %2E). +_DOT_SEGMENT_RE = re.compile(r"^(?:\.|%2[eE]){1,2}$") + +_PLACEHOLDER_RE = re.compile(r"\{(\w+)\}") + + +def _quote_path_segment_part(value: str) -> str: + """Percent-encode `value` for use in a URI path segment. + + Considers characters not in `pchar` set from RFC 3986 §3.3 to be unsafe. + https://datatracker.ietf.org/doc/html/rfc3986#section-3.3 + """ + # quote() already treats unreserved characters (letters, digits, and -._~) + # as safe, so we only need to add sub-delims, ':', and '@'. + # Notably, unlike the default `safe` for quote(), / is unsafe and must be quoted. + return quote(value, safe="!$&'()*+,;=:@") + + +def _quote_query_part(value: str) -> str: + """Percent-encode `value` for use in a URI query string. + + Considers &, = and characters not in `query` set from RFC 3986 §3.4 to be unsafe. + https://datatracker.ietf.org/doc/html/rfc3986#section-3.4 + """ + return quote(value, safe="!$'()*+,;:@/?") + + +def _quote_fragment_part(value: str) -> str: + """Percent-encode `value` for use in a URI fragment. + + Considers characters not in `fragment` set from RFC 3986 §3.5 to be unsafe. + https://datatracker.ietf.org/doc/html/rfc3986#section-3.5 + """ + return quote(value, safe="!$&'()*+,;=:@/?") + + +def _interpolate( + template: str, + values: Mapping[str, Any], + quoter: Callable[[str], str], +) -> str: + """Replace {name} placeholders in `template`, quoting each value with `quoter`. + + Placeholder names are looked up in `values`. + + Raises: + KeyError: If a placeholder is not found in `values`. + """ + # re.split with a capturing group returns alternating + # [text, name, text, name, ..., text] elements. + parts = _PLACEHOLDER_RE.split(template) + + for i in range(1, len(parts), 2): + name = parts[i] + if name not in values: + raise KeyError(f"a value for placeholder {{{name}}} was not provided") + val = values[name] + if val is None: + parts[i] = "null" + elif isinstance(val, bool): + parts[i] = "true" if val else "false" + else: + parts[i] = quoter(str(values[name])) + + return "".join(parts) + + +def path_template(template: str, /, **kwargs: Any) -> str: + """Interpolate {name} placeholders in `template` from keyword arguments. + + Args: + template: The template string containing {name} placeholders. + **kwargs: Keyword arguments to interpolate into the template. + + Returns: + The template with placeholders interpolated and percent-encoded. + + Safe characters for percent-encoding are dependent on the URI component. + Placeholders in path and fragment portions are percent-encoded where the `segment` + and `fragment` sets from RFC 3986 respectively are considered safe. + Placeholders in the query portion are percent-encoded where the `query` set from + RFC 3986 §3.3 is considered safe except for = and & characters. + + Raises: + KeyError: If a placeholder is not found in `kwargs`. + ValueError: If resulting path contains /./ or /../ segments (including percent-encoded dot-segments). + """ + # Split the template into path, query, and fragment portions. + fragment_template: str | None = None + query_template: str | None = None + + rest = template + if "#" in rest: + rest, fragment_template = rest.split("#", 1) + if "?" in rest: + rest, query_template = rest.split("?", 1) + path_template = rest + + # Interpolate each portion with the appropriate quoting rules. + path_result = _interpolate(path_template, kwargs, _quote_path_segment_part) + + # Reject dot-segments (. and ..) in the final assembled path. The check + # runs after interpolation so that adjacent placeholders or a mix of static + # text and placeholders that together form a dot-segment are caught. + # Also reject percent-encoded dot-segments to protect against incorrectly + # implemented normalization in servers/proxies. + for segment in path_result.split("/"): + if _DOT_SEGMENT_RE.match(segment): + raise ValueError(f"Constructed path {path_result!r} contains dot-segment {segment!r} which is not allowed") + + result = path_result + if query_template is not None: + result += "?" + _interpolate(query_template, kwargs, _quote_query_part) + if fragment_template is not None: + result += "#" + _interpolate(fragment_template, kwargs, _quote_fragment_part) + + return result diff --git a/src/runloop_api_client/_version.py b/src/runloop_api_client/_version.py index f93dfde0e..5dafd5d62 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__ = "1.12.1" # x-release-please-version +__version__ = "1.13.0" # x-release-please-version diff --git a/src/runloop_api_client/resources/__init__.py b/src/runloop_api_client/resources/__init__.py index 2e0584f25..58d7488f6 100644 --- a/src/runloop_api_client/resources/__init__.py +++ b/src/runloop_api_client/resources/__init__.py @@ -1,5 +1,13 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +from .axons import ( + AxonsResource, + AsyncAxonsResource, + AxonsResourceWithRawResponse, + AsyncAxonsResourceWithRawResponse, + AxonsResourceWithStreamingResponse, + AsyncAxonsResourceWithStreamingResponse, +) from .agents import ( AgentsResource, AsyncAgentsResource, @@ -130,6 +138,12 @@ "AsyncAgentsResourceWithRawResponse", "AgentsResourceWithStreamingResponse", "AsyncAgentsResourceWithStreamingResponse", + "AxonsResource", + "AsyncAxonsResource", + "AxonsResourceWithRawResponse", + "AsyncAxonsResourceWithRawResponse", + "AxonsResourceWithStreamingResponse", + "AsyncAxonsResourceWithStreamingResponse", "BlueprintsResource", "AsyncBlueprintsResource", "BlueprintsResourceWithRawResponse", diff --git a/src/runloop_api_client/resources/agents.py b/src/runloop_api_client/resources/agents.py index 9ac9f8c02..082d5725c 100644 --- a/src/runloop_api_client/resources/agents.py +++ b/src/runloop_api_client/resources/agents.py @@ -8,7 +8,7 @@ from ..types import agent_list_params, agent_create_params from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from .._utils import maybe_transform, async_maybe_transform +from .._utils import path_template, maybe_transform, async_maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import ( @@ -127,7 +127,7 @@ def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get( - f"/v1/agents/{id}", + path_template("/v1/agents/{id}", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -137,6 +137,7 @@ def retrieve( def list( self, *, + include_total_count: bool | Omit = omit, is_public: bool | Omit = omit, limit: int | Omit = omit, name: str | Omit = omit, @@ -154,6 +155,9 @@ def list( List all Agents for the authenticated account with pagination support. Args: + include_total_count: If true (default), includes total_count in the response. Set to false to skip + the count query for better performance on large datasets. + is_public: Filter agents by public visibility. limit: The limit of items to return. Default is 20. Max is 5000. @@ -184,6 +188,7 @@ def list( timeout=timeout, query=maybe_transform( { + "include_total_count": include_total_count, "is_public": is_public, "limit": limit, "name": name, @@ -300,7 +305,7 @@ async def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._get( - f"/v1/agents/{id}", + path_template("/v1/agents/{id}", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -310,6 +315,7 @@ async def retrieve( def list( self, *, + include_total_count: bool | Omit = omit, is_public: bool | Omit = omit, limit: int | Omit = omit, name: str | Omit = omit, @@ -327,6 +333,9 @@ def list( List all Agents for the authenticated account with pagination support. Args: + include_total_count: If true (default), includes total_count in the response. Set to false to skip + the count query for better performance on large datasets. + is_public: Filter agents by public visibility. limit: The limit of items to return. Default is 20. Max is 5000. @@ -357,6 +366,7 @@ def list( timeout=timeout, query=maybe_transform( { + "include_total_count": include_total_count, "is_public": is_public, "limit": limit, "name": name, diff --git a/src/runloop_api_client/resources/axons.py b/src/runloop_api_client/resources/axons.py new file mode 100644 index 000000000..9758057de --- /dev/null +++ b/src/runloop_api_client/resources/axons.py @@ -0,0 +1,532 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal + +import httpx + +from ..types import axon_create_params, axon_publish_params +from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from .._utils import path_template, 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.axon_view import AxonView +from ..types.axon_list_view import AxonListView +from ..types.axon_event_view import AxonEventView +from ..types.publish_result_view import PublishResultView + +__all__ = ["AxonsResource", "AsyncAxonsResource"] + + +class AxonsResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> AxonsResourceWithRawResponse: + """ + 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/runloopai/api-client-python#accessing-raw-response-data-eg-headers + """ + return AxonsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AxonsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/runloopai/api-client-python#with_streaming_response + """ + return AxonsResourceWithStreamingResponse(self) + + def create( + self, + *, + name: Optional[str] | Omit = omit, + # 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, + idempotency_key: str | None = None, + ) -> AxonView: + """ + [Beta] Create a new axon. + + Args: + name: (Optional) Name for the axon. + + 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 + + idempotency_key: Specify a custom idempotency key for this request + """ + return self._post( + "/v1/axons", + body=maybe_transform({"name": name}, axon_create_params.AxonCreateParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + idempotency_key=idempotency_key, + ), + cast_to=AxonView, + ) + + def retrieve( + self, + 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, + ) -> AxonView: + """ + [Beta] Get an axon given ID. + + 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 id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._get( + path_template("/v1/axons/{id}", id=id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=AxonView, + ) + + def list( + self, + *, + # 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, + ) -> AxonListView: + """[Beta] List all active axons.""" + return self._get( + "/v1/axons", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=AxonListView, + ) + + def publish( + self, + id: str, + *, + event_type: str, + origin: Literal["EXTERNAL_EVENT", "AGENT_EVENT", "USER_EVENT"], + payload: str, + source: 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, + idempotency_key: str | None = None, + ) -> PublishResultView: + """ + [Beta] Publish an event to a specified axon. + + Args: + event_type: The event type (e.g. push, pull_request). + + origin: Event origin. + + payload: Event payload. + + source: The source of the event (e.g. github, slack). + + 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 + + idempotency_key: Specify a custom idempotency key for this request + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._post( + path_template("/v1/axons/{id}/publish", id=id), + body=maybe_transform( + { + "event_type": event_type, + "origin": origin, + "payload": payload, + "source": source, + }, + axon_publish_params.AxonPublishParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + idempotency_key=idempotency_key, + ), + cast_to=PublishResultView, + ) + + def subscribe_sse( + self, + 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, + ) -> Stream[AxonEventView]: + """ + [Beta] Subscribe to an axon event stream via server-sent events. + + 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 id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._get( + path_template("/v1/axons/{id}/subscribe/sse", id=id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=AxonEventView, + stream=True, + stream_cls=Stream[AxonEventView], + ) + + +class AsyncAxonsResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncAxonsResourceWithRawResponse: + """ + 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/runloopai/api-client-python#accessing-raw-response-data-eg-headers + """ + return AsyncAxonsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncAxonsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/runloopai/api-client-python#with_streaming_response + """ + return AsyncAxonsResourceWithStreamingResponse(self) + + async def create( + self, + *, + name: Optional[str] | Omit = omit, + # 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, + idempotency_key: str | None = None, + ) -> AxonView: + """ + [Beta] Create a new axon. + + Args: + name: (Optional) Name for the axon. + + 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 + + idempotency_key: Specify a custom idempotency key for this request + """ + return await self._post( + "/v1/axons", + body=await async_maybe_transform({"name": name}, axon_create_params.AxonCreateParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + idempotency_key=idempotency_key, + ), + cast_to=AxonView, + ) + + async def retrieve( + self, + 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, + ) -> AxonView: + """ + [Beta] Get an axon given ID. + + 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 id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._get( + path_template("/v1/axons/{id}", id=id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=AxonView, + ) + + async def list( + self, + *, + # 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, + ) -> AxonListView: + """[Beta] List all active axons.""" + return await self._get( + "/v1/axons", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=AxonListView, + ) + + async def publish( + self, + id: str, + *, + event_type: str, + origin: Literal["EXTERNAL_EVENT", "AGENT_EVENT", "USER_EVENT"], + payload: str, + source: 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, + idempotency_key: str | None = None, + ) -> PublishResultView: + """ + [Beta] Publish an event to a specified axon. + + Args: + event_type: The event type (e.g. push, pull_request). + + origin: Event origin. + + payload: Event payload. + + source: The source of the event (e.g. github, slack). + + 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 + + idempotency_key: Specify a custom idempotency key for this request + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._post( + path_template("/v1/axons/{id}/publish", id=id), + body=await async_maybe_transform( + { + "event_type": event_type, + "origin": origin, + "payload": payload, + "source": source, + }, + axon_publish_params.AxonPublishParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + idempotency_key=idempotency_key, + ), + cast_to=PublishResultView, + ) + + async def subscribe_sse( + self, + 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, + ) -> AsyncStream[AxonEventView]: + """ + [Beta] Subscribe to an axon event stream via server-sent events. + + 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 id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._get( + path_template("/v1/axons/{id}/subscribe/sse", id=id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=AxonEventView, + stream=True, + stream_cls=AsyncStream[AxonEventView], + ) + + +class AxonsResourceWithRawResponse: + def __init__(self, axons: AxonsResource) -> None: + self._axons = axons + + self.create = to_raw_response_wrapper( + axons.create, + ) + self.retrieve = to_raw_response_wrapper( + axons.retrieve, + ) + self.list = to_raw_response_wrapper( + axons.list, + ) + self.publish = to_raw_response_wrapper( + axons.publish, + ) + self.subscribe_sse = to_raw_response_wrapper( + axons.subscribe_sse, + ) + + +class AsyncAxonsResourceWithRawResponse: + def __init__(self, axons: AsyncAxonsResource) -> None: + self._axons = axons + + self.create = async_to_raw_response_wrapper( + axons.create, + ) + self.retrieve = async_to_raw_response_wrapper( + axons.retrieve, + ) + self.list = async_to_raw_response_wrapper( + axons.list, + ) + self.publish = async_to_raw_response_wrapper( + axons.publish, + ) + self.subscribe_sse = async_to_raw_response_wrapper( + axons.subscribe_sse, + ) + + +class AxonsResourceWithStreamingResponse: + def __init__(self, axons: AxonsResource) -> None: + self._axons = axons + + self.create = to_streamed_response_wrapper( + axons.create, + ) + self.retrieve = to_streamed_response_wrapper( + axons.retrieve, + ) + self.list = to_streamed_response_wrapper( + axons.list, + ) + self.publish = to_streamed_response_wrapper( + axons.publish, + ) + self.subscribe_sse = to_streamed_response_wrapper( + axons.subscribe_sse, + ) + + +class AsyncAxonsResourceWithStreamingResponse: + def __init__(self, axons: AsyncAxonsResource) -> None: + self._axons = axons + + self.create = async_to_streamed_response_wrapper( + axons.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + axons.retrieve, + ) + self.list = async_to_streamed_response_wrapper( + axons.list, + ) + self.publish = async_to_streamed_response_wrapper( + axons.publish, + ) + self.subscribe_sse = async_to_streamed_response_wrapper( + axons.subscribe_sse, + ) diff --git a/src/runloop_api_client/resources/benchmark_jobs.py b/src/runloop_api_client/resources/benchmark_jobs.py index f6172d118..e57e58c33 100644 --- a/src/runloop_api_client/resources/benchmark_jobs.py +++ b/src/runloop_api_client/resources/benchmark_jobs.py @@ -8,7 +8,7 @@ from ..types import benchmark_job_list_params, benchmark_job_create_params from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from .._utils import maybe_transform, async_maybe_transform +from .._utils import path_template, maybe_transform, async_maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import ( @@ -121,7 +121,7 @@ def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get( - f"/v1/benchmark_jobs/{id}", + path_template("/v1/benchmark_jobs/{id}", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -131,6 +131,7 @@ def retrieve( def list( self, *, + include_total_count: bool | Omit = omit, limit: int | Omit = omit, name: str | Omit = omit, starting_after: str | Omit = omit, @@ -145,6 +146,9 @@ def list( [Beta] List all BenchmarkJobs matching filter. Args: + include_total_count: If true (default), includes total_count in the response. Set to false to skip + the count query for better performance on large datasets. + limit: The limit of items to return. Default is 20. Max is 5000. name: Filter by name @@ -168,6 +172,7 @@ def list( timeout=timeout, query=maybe_transform( { + "include_total_count": include_total_count, "limit": limit, "name": name, "starting_after": starting_after, @@ -276,7 +281,7 @@ async def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._get( - f"/v1/benchmark_jobs/{id}", + path_template("/v1/benchmark_jobs/{id}", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -286,6 +291,7 @@ async def retrieve( async def list( self, *, + include_total_count: bool | Omit = omit, limit: int | Omit = omit, name: str | Omit = omit, starting_after: str | Omit = omit, @@ -300,6 +306,9 @@ async def list( [Beta] List all BenchmarkJobs matching filter. Args: + include_total_count: If true (default), includes total_count in the response. Set to false to skip + the count query for better performance on large datasets. + limit: The limit of items to return. Default is 20. Max is 5000. name: Filter by name @@ -323,6 +332,7 @@ async def list( timeout=timeout, query=await async_maybe_transform( { + "include_total_count": include_total_count, "limit": limit, "name": name, "starting_after": starting_after, diff --git a/src/runloop_api_client/resources/benchmark_runs.py b/src/runloop_api_client/resources/benchmark_runs.py index 964497cde..27e327d3d 100644 --- a/src/runloop_api_client/resources/benchmark_runs.py +++ b/src/runloop_api_client/resources/benchmark_runs.py @@ -8,7 +8,7 @@ from ..types import benchmark_run_list_params, benchmark_run_list_scenario_runs_params from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from .._utils import maybe_transform +from .._utils import path_template, maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import ( @@ -71,7 +71,7 @@ def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get( - f"/v1/benchmark_runs/{id}", + path_template("/v1/benchmark_runs/{id}", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -82,6 +82,7 @@ def list( self, *, benchmark_id: str | Omit = omit, + include_total_count: bool | Omit = omit, limit: int | Omit = omit, name: str | Omit = omit, starting_after: str | Omit = omit, @@ -98,6 +99,9 @@ def list( Args: benchmark_id: The Benchmark ID to filter by. + include_total_count: If true (default), includes total_count in the response. Set to false to skip + the count query for better performance on large datasets. + limit: The limit of items to return. Default is 20. Max is 5000. name: Filter by name @@ -123,6 +127,7 @@ def list( query=maybe_transform( { "benchmark_id": benchmark_id, + "include_total_count": include_total_count, "limit": limit, "name": name, "starting_after": starting_after, @@ -165,7 +170,7 @@ def cancel( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._post( - f"/v1/benchmark_runs/{id}/cancel", + path_template("/v1/benchmark_runs/{id}/cancel", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -205,7 +210,7 @@ def complete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._post( - f"/v1/benchmark_runs/{id}/complete", + path_template("/v1/benchmark_runs/{id}/complete", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -220,6 +225,7 @@ def list_scenario_runs( self, id: str, *, + include_total_count: bool | Omit = omit, limit: int | Omit = omit, starting_after: str | Omit = omit, state: Literal["running", "scoring", "scored", "completed", "canceled", "timeout", "failed"] | Omit = omit, @@ -234,6 +240,9 @@ def list_scenario_runs( List started scenario runs for a benchmark run. Args: + include_total_count: If true (default), includes total_count in the response. Set to false to skip + the count query for better performance on large datasets. + limit: The limit of items to return. Default is 20. Max is 5000. starting_after: Load the next page of data starting after the item with the given ID. @@ -251,7 +260,7 @@ def list_scenario_runs( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get_api_list( - f"/v1/benchmark_runs/{id}/scenario_runs", + path_template("/v1/benchmark_runs/{id}/scenario_runs", id=id), page=SyncBenchmarkRunsCursorIDPage[ScenarioRunView], options=make_request_options( extra_headers=extra_headers, @@ -260,6 +269,7 @@ def list_scenario_runs( timeout=timeout, query=maybe_transform( { + "include_total_count": include_total_count, "limit": limit, "starting_after": starting_after, "state": state, @@ -317,7 +327,7 @@ async def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._get( - f"/v1/benchmark_runs/{id}", + path_template("/v1/benchmark_runs/{id}", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -328,6 +338,7 @@ def list( self, *, benchmark_id: str | Omit = omit, + include_total_count: bool | Omit = omit, limit: int | Omit = omit, name: str | Omit = omit, starting_after: str | Omit = omit, @@ -344,6 +355,9 @@ def list( Args: benchmark_id: The Benchmark ID to filter by. + include_total_count: If true (default), includes total_count in the response. Set to false to skip + the count query for better performance on large datasets. + limit: The limit of items to return. Default is 20. Max is 5000. name: Filter by name @@ -369,6 +383,7 @@ def list( query=maybe_transform( { "benchmark_id": benchmark_id, + "include_total_count": include_total_count, "limit": limit, "name": name, "starting_after": starting_after, @@ -411,7 +426,7 @@ async def cancel( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._post( - f"/v1/benchmark_runs/{id}/cancel", + path_template("/v1/benchmark_runs/{id}/cancel", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -451,7 +466,7 @@ async def complete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._post( - f"/v1/benchmark_runs/{id}/complete", + path_template("/v1/benchmark_runs/{id}/complete", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -466,6 +481,7 @@ def list_scenario_runs( self, id: str, *, + include_total_count: bool | Omit = omit, limit: int | Omit = omit, starting_after: str | Omit = omit, state: Literal["running", "scoring", "scored", "completed", "canceled", "timeout", "failed"] | Omit = omit, @@ -480,6 +496,9 @@ def list_scenario_runs( List started scenario runs for a benchmark run. Args: + include_total_count: If true (default), includes total_count in the response. Set to false to skip + the count query for better performance on large datasets. + limit: The limit of items to return. Default is 20. Max is 5000. starting_after: Load the next page of data starting after the item with the given ID. @@ -497,7 +516,7 @@ def list_scenario_runs( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get_api_list( - f"/v1/benchmark_runs/{id}/scenario_runs", + path_template("/v1/benchmark_runs/{id}/scenario_runs", id=id), page=AsyncBenchmarkRunsCursorIDPage[ScenarioRunView], options=make_request_options( extra_headers=extra_headers, @@ -506,6 +525,7 @@ def list_scenario_runs( timeout=timeout, query=maybe_transform( { + "include_total_count": include_total_count, "limit": limit, "starting_after": starting_after, "state": state, diff --git a/src/runloop_api_client/resources/benchmarks.py b/src/runloop_api_client/resources/benchmarks.py index d23992bd2..ca5442f52 100644 --- a/src/runloop_api_client/resources/benchmarks.py +++ b/src/runloop_api_client/resources/benchmarks.py @@ -16,7 +16,7 @@ benchmark_update_scenarios_params, ) from .._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given -from .._utils import maybe_transform, async_maybe_transform +from .._utils import path_template, maybe_transform, async_maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import ( @@ -154,7 +154,7 @@ def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get( - f"/v1/benchmarks/{id}", + path_template("/v1/benchmarks/{id}", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -218,7 +218,7 @@ def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._post( - f"/v1/benchmarks/{id}", + path_template("/v1/benchmarks/{id}", id=id), body=maybe_transform( { "attribution": attribution, @@ -244,6 +244,7 @@ def update( def list( self, *, + include_total_count: bool | Omit = omit, limit: int | Omit = omit, name: str | Omit = omit, starting_after: str | Omit = omit, @@ -258,6 +259,9 @@ def list( List all Benchmarks matching filter. Args: + include_total_count: If true (default), includes total_count in the response. Set to false to skip + the count query for better performance on large datasets. + limit: The limit of items to return. Default is 20. Max is 5000. name: Filter by name @@ -282,6 +286,7 @@ def list( timeout=timeout, query=maybe_transform( { + "include_total_count": include_total_count, "limit": limit, "name": name, "starting_after": starting_after, @@ -324,7 +329,7 @@ def definitions( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get( - f"/v1/benchmarks/{id}/definitions", + path_template("/v1/benchmarks/{id}/definitions", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -344,6 +349,7 @@ def definitions( def list_public( self, *, + include_total_count: bool | Omit = omit, limit: int | Omit = omit, starting_after: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -357,6 +363,9 @@ def list_public( List all public benchmarks matching filter. Args: + include_total_count: If true (default), includes total_count in the response. Set to false to skip + the count query for better performance on large datasets. + limit: The limit of items to return. Default is 20. Max is 5000. starting_after: Load the next page of data starting after the item with the given ID. @@ -379,6 +388,7 @@ def list_public( timeout=timeout, query=maybe_transform( { + "include_total_count": include_total_count, "limit": limit, "starting_after": starting_after, }, @@ -481,7 +491,7 @@ def update_scenarios( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._post( - f"/v1/benchmarks/{id}/scenarios", + path_template("/v1/benchmarks/{id}/scenarios", id=id), body=maybe_transform( { "scenarios_to_add": scenarios_to_add, @@ -619,7 +629,7 @@ async def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._get( - f"/v1/benchmarks/{id}", + path_template("/v1/benchmarks/{id}", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -683,7 +693,7 @@ async def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._post( - f"/v1/benchmarks/{id}", + path_template("/v1/benchmarks/{id}", id=id), body=await async_maybe_transform( { "attribution": attribution, @@ -709,6 +719,7 @@ async def update( def list( self, *, + include_total_count: bool | Omit = omit, limit: int | Omit = omit, name: str | Omit = omit, starting_after: str | Omit = omit, @@ -723,6 +734,9 @@ def list( List all Benchmarks matching filter. Args: + include_total_count: If true (default), includes total_count in the response. Set to false to skip + the count query for better performance on large datasets. + limit: The limit of items to return. Default is 20. Max is 5000. name: Filter by name @@ -747,6 +761,7 @@ def list( timeout=timeout, query=maybe_transform( { + "include_total_count": include_total_count, "limit": limit, "name": name, "starting_after": starting_after, @@ -789,7 +804,7 @@ async def definitions( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._get( - f"/v1/benchmarks/{id}/definitions", + path_template("/v1/benchmarks/{id}/definitions", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -809,6 +824,7 @@ async def definitions( def list_public( self, *, + include_total_count: bool | Omit = omit, limit: int | Omit = omit, starting_after: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -822,6 +838,9 @@ def list_public( List all public benchmarks matching filter. Args: + include_total_count: If true (default), includes total_count in the response. Set to false to skip + the count query for better performance on large datasets. + limit: The limit of items to return. Default is 20. Max is 5000. starting_after: Load the next page of data starting after the item with the given ID. @@ -844,6 +863,7 @@ def list_public( timeout=timeout, query=maybe_transform( { + "include_total_count": include_total_count, "limit": limit, "starting_after": starting_after, }, @@ -946,7 +966,7 @@ async def update_scenarios( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._post( - f"/v1/benchmarks/{id}/scenarios", + path_template("/v1/benchmarks/{id}/scenarios", id=id), body=await async_maybe_transform( { "scenarios_to_add": scenarios_to_add, diff --git a/src/runloop_api_client/resources/blueprints.py b/src/runloop_api_client/resources/blueprints.py index 39eae98aa..7e5b09939 100644 --- a/src/runloop_api_client/resources/blueprints.py +++ b/src/runloop_api_client/resources/blueprints.py @@ -14,7 +14,7 @@ blueprint_create_from_inspection_params, ) from .._types import NOT_GIVEN, Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given -from .._utils import is_given, maybe_transform, async_maybe_transform +from .._utils import is_given, path_template, maybe_transform, async_maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import ( @@ -268,7 +268,7 @@ def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get( - f"/v1/blueprints/{id}", + path_template("/v1/blueprints/{id}", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -396,6 +396,7 @@ def create_and_await_build_complete( def list( self, *, + include_total_count: bool | Omit = omit, limit: int | Omit = omit, name: str | Omit = omit, starting_after: str | Omit = omit, @@ -411,6 +412,9 @@ def list( List all Blueprints or filter by name. Args: + include_total_count: If true (default), includes total_count in the response. Set to false to skip + the count query for better performance on large datasets. + limit: The limit of items to return. Default is 20. Max is 5000. name: Filter by name @@ -437,6 +441,7 @@ def list( timeout=timeout, query=maybe_transform( { + "include_total_count": include_total_count, "limit": limit, "name": name, "starting_after": starting_after, @@ -480,7 +485,7 @@ def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._post( - f"/v1/blueprints/{id}/delete", + path_template("/v1/blueprints/{id}/delete", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -574,6 +579,7 @@ def create_from_inspection( def list_public( self, *, + include_total_count: bool | Omit = omit, limit: int | Omit = omit, name: str | Omit = omit, starting_after: str | Omit = omit, @@ -589,6 +595,9 @@ def list_public( List all public Blueprints that are available to all users. Args: + include_total_count: If true (default), includes total_count in the response. Set to false to skip + the count query for better performance on large datasets. + limit: The limit of items to return. Default is 20. Max is 5000. name: Filter by name @@ -615,6 +624,7 @@ def list_public( timeout=timeout, query=maybe_transform( { + "include_total_count": include_total_count, "limit": limit, "name": name, "starting_after": starting_after, @@ -652,7 +662,7 @@ def logs( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get( - f"/v1/blueprints/{id}/logs", + path_template("/v1/blueprints/{id}/logs", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -938,7 +948,7 @@ async def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._get( - f"/v1/blueprints/{id}", + path_template("/v1/blueprints/{id}", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -1066,6 +1076,7 @@ async def create_and_await_build_complete( def list( self, *, + include_total_count: bool | Omit = omit, limit: int | Omit = omit, name: str | Omit = omit, starting_after: str | Omit = omit, @@ -1081,6 +1092,9 @@ def list( List all Blueprints or filter by name. Args: + include_total_count: If true (default), includes total_count in the response. Set to false to skip + the count query for better performance on large datasets. + limit: The limit of items to return. Default is 20. Max is 5000. name: Filter by name @@ -1107,6 +1121,7 @@ def list( timeout=timeout, query=maybe_transform( { + "include_total_count": include_total_count, "limit": limit, "name": name, "starting_after": starting_after, @@ -1150,7 +1165,7 @@ async def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._post( - f"/v1/blueprints/{id}/delete", + path_template("/v1/blueprints/{id}/delete", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -1244,6 +1259,7 @@ async def create_from_inspection( def list_public( self, *, + include_total_count: bool | Omit = omit, limit: int | Omit = omit, name: str | Omit = omit, starting_after: str | Omit = omit, @@ -1259,6 +1275,9 @@ def list_public( List all public Blueprints that are available to all users. Args: + include_total_count: If true (default), includes total_count in the response. Set to false to skip + the count query for better performance on large datasets. + limit: The limit of items to return. Default is 20. Max is 5000. name: Filter by name @@ -1285,6 +1304,7 @@ def list_public( timeout=timeout, query=maybe_transform( { + "include_total_count": include_total_count, "limit": limit, "name": name, "starting_after": starting_after, @@ -1322,7 +1342,7 @@ async def logs( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._get( - f"/v1/blueprints/{id}/logs", + path_template("/v1/blueprints/{id}/logs", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/runloop_api_client/resources/devboxes/browsers.py b/src/runloop_api_client/resources/devboxes/browsers.py index 517857f56..d2977464b 100644 --- a/src/runloop_api_client/resources/devboxes/browsers.py +++ b/src/runloop_api_client/resources/devboxes/browsers.py @@ -7,7 +7,7 @@ import httpx from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ..._utils import maybe_transform, async_maybe_transform +from ..._utils import path_template, maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import ( @@ -113,7 +113,7 @@ def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get( - f"/v1/devboxes/browsers/{id}", + path_template("/v1/devboxes/browsers/{id}", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -211,7 +211,7 @@ async def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._get( - f"/v1/devboxes/browsers/{id}", + path_template("/v1/devboxes/browsers/{id}", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/runloop_api_client/resources/devboxes/computers.py b/src/runloop_api_client/resources/devboxes/computers.py index be051db2e..4eddfccc8 100644 --- a/src/runloop_api_client/resources/devboxes/computers.py +++ b/src/runloop_api_client/resources/devboxes/computers.py @@ -8,7 +8,7 @@ import httpx from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ..._utils import maybe_transform, async_maybe_transform +from ..._utils import path_template, maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import ( @@ -133,7 +133,7 @@ def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get( - f"/v1/devboxes/computers/{id}", + path_template("/v1/devboxes/computers/{id}", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -176,7 +176,7 @@ def keyboard_interaction( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._post( - f"/v1/devboxes/computers/{id}/keyboard_interaction", + path_template("/v1/devboxes/computers/{id}/keyboard_interaction", id=id), body=maybe_transform( { "action": action, @@ -232,7 +232,7 @@ def mouse_interaction( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._post( - f"/v1/devboxes/computers/{id}/mouse_interaction", + path_template("/v1/devboxes/computers/{id}/mouse_interaction", id=id), body=maybe_transform( { "action": action, @@ -283,7 +283,7 @@ def screen_interaction( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._post( - f"/v1/devboxes/computers/{id}/screen_interaction", + path_template("/v1/devboxes/computers/{id}/screen_interaction", id=id), body=maybe_transform( {"action": action}, computer_screen_interaction_params.ComputerScreenInteractionParams ), @@ -399,7 +399,7 @@ async def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._get( - f"/v1/devboxes/computers/{id}", + path_template("/v1/devboxes/computers/{id}", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -442,7 +442,7 @@ async def keyboard_interaction( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._post( - f"/v1/devboxes/computers/{id}/keyboard_interaction", + path_template("/v1/devboxes/computers/{id}/keyboard_interaction", id=id), body=await async_maybe_transform( { "action": action, @@ -498,7 +498,7 @@ async def mouse_interaction( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._post( - f"/v1/devboxes/computers/{id}/mouse_interaction", + path_template("/v1/devboxes/computers/{id}/mouse_interaction", id=id), body=await async_maybe_transform( { "action": action, @@ -549,7 +549,7 @@ async def screen_interaction( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._post( - f"/v1/devboxes/computers/{id}/screen_interaction", + path_template("/v1/devboxes/computers/{id}/screen_interaction", id=id), body=await async_maybe_transform( {"action": action}, computer_screen_interaction_params.ComputerScreenInteractionParams ), diff --git a/src/runloop_api_client/resources/devboxes/devboxes.py b/src/runloop_api_client/resources/devboxes/devboxes.py index 406af8626..1f016f2eb 100644 --- a/src/runloop_api_client/resources/devboxes/devboxes.py +++ b/src/runloop_api_client/resources/devboxes/devboxes.py @@ -30,7 +30,6 @@ devbox_download_file_params, devbox_enable_tunnel_params, devbox_execute_async_params, - devbox_remove_tunnel_params, devbox_snapshot_disk_params, devbox_wait_for_command_params, devbox_read_file_contents_params, @@ -39,7 +38,7 @@ devbox_write_file_contents_params, ) from ..._types import Body, Omit, Query, Headers, NotGiven, FileTypes, omit, not_given -from ..._utils import is_given, extract_files, maybe_transform, deepcopy_minimal, async_maybe_transform +from ..._utils import is_given, extract_files, path_template, maybe_transform, deepcopy_minimal, async_maybe_transform from .browsers import ( BrowsersResource, AsyncBrowsersResource, @@ -335,7 +334,7 @@ def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get( - f"/v1/devboxes/{id}", + path_template("/v1/devboxes/{id}", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -378,7 +377,7 @@ def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._post( - f"/v1/devboxes/{id}", + path_template("/v1/devboxes/{id}", id=id), body=maybe_transform( { "metadata": metadata, @@ -571,6 +570,7 @@ def create_and_await_running( def list( self, *, + include_total_count: bool | Omit = omit, limit: int | Omit = omit, starting_after: str | Omit = omit, status: Literal[ @@ -588,6 +588,9 @@ def list( List all Devboxes while optionally filtering by status. Args: + include_total_count: If true (default), includes total_count in the response. Set to false to skip + the count query for better performance on large datasets. + limit: The limit of items to return. Default is 20. Max is 5000. starting_after: Load the next page of data starting after the item with the given ID. @@ -612,6 +615,7 @@ def list( timeout=timeout, query=maybe_transform( { + "include_total_count": include_total_count, "limit": limit, "starting_after": starting_after, "status": status, @@ -651,7 +655,7 @@ def create_ssh_key( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._post( - f"/v1/devboxes/{id}/create_ssh_key", + path_template("/v1/devboxes/{id}/create_ssh_key", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -691,7 +695,7 @@ def delete_disk_snapshot( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._post( - f"/v1/devboxes/disk_snapshots/{id}/delete", + path_template("/v1/devboxes/disk_snapshots/{id}/delete", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -739,7 +743,7 @@ def download_file( timeout = 600 extra_headers = {"Accept": "application/octet-stream", **(extra_headers or {})} return self._post( - f"/v1/devboxes/{id}/download_file", + path_template("/v1/devboxes/{id}/download_file", id=id), body=maybe_transform({"path": path}, devbox_download_file_params.DevboxDownloadFileParams), options=make_request_options( extra_headers=extra_headers, @@ -792,7 +796,7 @@ def enable_tunnel( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._post( - f"/v1/devboxes/{id}/enable_tunnel", + path_template("/v1/devboxes/{id}/enable_tunnel", id=id), body=maybe_transform( { "auth_mode": auth_mode, @@ -869,7 +873,7 @@ def execute( if not is_given(timeout) and self._client.timeout == DEFAULT_TIMEOUT: timeout = 600 return self._post( - f"/v1/devboxes/{id}/execute", + path_template("/v1/devboxes/{id}/execute", id=id), body=maybe_transform( { "command": command, @@ -997,7 +1001,7 @@ def execute_async( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._post( - f"/v1/devboxes/{id}/execute_async", + path_template("/v1/devboxes/{id}/execute_async", id=id), body=maybe_transform( { "command": command, @@ -1069,7 +1073,7 @@ def execute_sync( if not is_given(timeout) and self._client.timeout == DEFAULT_TIMEOUT: timeout = 600 return self._post( - f"/v1/devboxes/{id}/execute_sync", + path_template("/v1/devboxes/{id}/execute_sync", id=id), body=maybe_transform( { "command": command, @@ -1118,7 +1122,7 @@ def keep_alive( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._post( - f"/v1/devboxes/{id}/keep_alive", + path_template("/v1/devboxes/{id}/keep_alive", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -1133,6 +1137,7 @@ def list_disk_snapshots( self, *, devbox_id: str | Omit = omit, + include_total_count: bool | Omit = omit, limit: int | Omit = omit, metadata_key: str | Omit = omit, metadata_key_in: str | Omit = omit, @@ -1152,6 +1157,9 @@ def list_disk_snapshots( Args: devbox_id: Devbox ID to filter by. + include_total_count: If true (default), includes total_count in the response. Set to false to skip + the count query for better performance on large datasets. + limit: The limit of items to return. Default is 20. Max is 5000. metadata_key: Filter snapshots by metadata key-value pair. Can be used multiple times for @@ -1182,6 +1190,7 @@ def list_disk_snapshots( query=maybe_transform( { "devbox_id": devbox_id, + "include_total_count": include_total_count, "limit": limit, "metadata_key": metadata_key, "metadata_key_in": metadata_key_in, @@ -1233,7 +1242,7 @@ def read_file_contents( timeout = 600 extra_headers = {"Accept": "text/plain", **(extra_headers or {})} return self._post( - f"/v1/devboxes/{id}/read_file_contents", + path_template("/v1/devboxes/{id}/read_file_contents", id=id), body=maybe_transform( {"file_path": file_path}, devbox_read_file_contents_params.DevboxReadFileContentsParams ), @@ -1247,14 +1256,10 @@ def read_file_contents( cast_to=str, ) - @typing_extensions.deprecated( - "remove_tunnel is deprecated; V2 tunnels cannot be removed and close on devbox shutdown." - ) def remove_tunnel( self, id: str, *, - port: int, # 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, @@ -1263,14 +1268,10 @@ def remove_tunnel( timeout: float | httpx.Timeout | None | NotGiven = not_given, idempotency_key: str | None = None, ) -> object: - """[Deprecated] V2 tunnels cannot be removed and close on devbox shutdown. - - This endpoint - removes a legacy tunnel. + """ + Remove an existing V2 tunnel from the Devbox. Args: - port: Devbox port that tunnel will expose. - extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -1284,8 +1285,7 @@ def remove_tunnel( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._post( - f"/v1/devboxes/{id}/remove_tunnel", - body=maybe_transform({"port": port}, devbox_remove_tunnel_params.DevboxRemoveTunnelParams), + path_template("/v1/devboxes/{id}/remove_tunnel", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -1328,7 +1328,7 @@ def resume( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._post( - f"/v1/devboxes/{id}/resume", + path_template("/v1/devboxes/{id}/resume", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -1369,7 +1369,7 @@ def retrieve_resource_usage( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get( - f"/v1/devboxes/{id}/usage", + path_template("/v1/devboxes/{id}/usage", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -1413,7 +1413,7 @@ def shutdown( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._post( - f"/v1/devboxes/{id}/shutdown", + path_template("/v1/devboxes/{id}/shutdown", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -1466,7 +1466,7 @@ def snapshot_disk( if not is_given(timeout) and self._client.timeout == DEFAULT_TIMEOUT: timeout = 600 return self._post( - f"/v1/devboxes/{id}/snapshot_disk", + path_template("/v1/devboxes/{id}/snapshot_disk", id=id), body=maybe_transform( { "commit_message": commit_message, @@ -1525,7 +1525,7 @@ def snapshot_disk_async( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._post( - f"/v1/devboxes/{id}/snapshot_disk_async", + path_template("/v1/devboxes/{id}/snapshot_disk_async", id=id), body=maybe_transform( { "commit_message": commit_message, @@ -1575,7 +1575,7 @@ def suspend( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._post( - f"/v1/devboxes/{id}/suspend", + path_template("/v1/devboxes/{id}/suspend", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -1636,7 +1636,7 @@ def upload_file( # multipart/form-data; boundary=---abc-- extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} return self._post( - f"/v1/devboxes/{id}/upload_file", + path_template("/v1/devboxes/{id}/upload_file", id=id), body=maybe_transform(body, devbox_upload_file_params.DevboxUploadFileParams), files=files, options=make_request_options( @@ -1694,7 +1694,11 @@ def wait_for_command( if not execution_id: raise ValueError(f"Expected a non-empty value for `execution_id` but received {execution_id!r}") return self._post( - f"/v1/devboxes/{devbox_id}/executions/{execution_id}/wait_for_status", + path_template( + "/v1/devboxes/{devbox_id}/executions/{execution_id}/wait_for_status", + devbox_id=devbox_id, + execution_id=execution_id, + ), body=maybe_transform( { "statuses": statuses, @@ -1753,7 +1757,7 @@ def write_file_contents( if not is_given(timeout) and self._client.timeout == DEFAULT_TIMEOUT: timeout = 600 return self._post( - f"/v1/devboxes/{id}/write_file_contents", + path_template("/v1/devboxes/{id}/write_file_contents", id=id), body=maybe_transform( { "contents": contents, @@ -1969,7 +1973,7 @@ async def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._get( - f"/v1/devboxes/{id}", + path_template("/v1/devboxes/{id}", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -2181,7 +2185,7 @@ async def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._post( - f"/v1/devboxes/{id}", + path_template("/v1/devboxes/{id}", id=id), body=await async_maybe_transform( { "metadata": metadata, @@ -2202,6 +2206,7 @@ async def update( def list( self, *, + include_total_count: bool | Omit = omit, limit: int | Omit = omit, starting_after: str | Omit = omit, status: Literal[ @@ -2219,6 +2224,9 @@ def list( List all Devboxes while optionally filtering by status. Args: + include_total_count: If true (default), includes total_count in the response. Set to false to skip + the count query for better performance on large datasets. + limit: The limit of items to return. Default is 20. Max is 5000. starting_after: Load the next page of data starting after the item with the given ID. @@ -2243,6 +2251,7 @@ def list( timeout=timeout, query=maybe_transform( { + "include_total_count": include_total_count, "limit": limit, "starting_after": starting_after, "status": status, @@ -2282,7 +2291,7 @@ async def create_ssh_key( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._post( - f"/v1/devboxes/{id}/create_ssh_key", + path_template("/v1/devboxes/{id}/create_ssh_key", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -2322,7 +2331,7 @@ async def delete_disk_snapshot( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._post( - f"/v1/devboxes/disk_snapshots/{id}/delete", + path_template("/v1/devboxes/disk_snapshots/{id}/delete", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -2370,7 +2379,7 @@ async def download_file( timeout = 600 extra_headers = {"Accept": "application/octet-stream", **(extra_headers or {})} return await self._post( - f"/v1/devboxes/{id}/download_file", + path_template("/v1/devboxes/{id}/download_file", id=id), body=await async_maybe_transform({"path": path}, devbox_download_file_params.DevboxDownloadFileParams), options=make_request_options( extra_headers=extra_headers, @@ -2423,7 +2432,7 @@ async def enable_tunnel( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._post( - f"/v1/devboxes/{id}/enable_tunnel", + path_template("/v1/devboxes/{id}/enable_tunnel", id=id), body=await async_maybe_transform( { "auth_mode": auth_mode, @@ -2500,7 +2509,7 @@ async def execute( if not is_given(timeout) and self._client.timeout == DEFAULT_TIMEOUT: timeout = 600 return await self._post( - f"/v1/devboxes/{id}/execute", + path_template("/v1/devboxes/{id}/execute", id=id), body=await async_maybe_transform( { "command": command, @@ -2627,7 +2636,7 @@ async def execute_async( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._post( - f"/v1/devboxes/{id}/execute_async", + path_template("/v1/devboxes/{id}/execute_async", id=id), body=await async_maybe_transform( { "command": command, @@ -2699,7 +2708,7 @@ async def execute_sync( if not is_given(timeout) and self._client.timeout == DEFAULT_TIMEOUT: timeout = 600 return await self._post( - f"/v1/devboxes/{id}/execute_sync", + path_template("/v1/devboxes/{id}/execute_sync", id=id), body=await async_maybe_transform( { "command": command, @@ -2748,7 +2757,7 @@ async def keep_alive( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._post( - f"/v1/devboxes/{id}/keep_alive", + path_template("/v1/devboxes/{id}/keep_alive", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -2763,6 +2772,7 @@ def list_disk_snapshots( self, *, devbox_id: str | Omit = omit, + include_total_count: bool | Omit = omit, limit: int | Omit = omit, metadata_key: str | Omit = omit, metadata_key_in: str | Omit = omit, @@ -2782,6 +2792,9 @@ def list_disk_snapshots( Args: devbox_id: Devbox ID to filter by. + include_total_count: If true (default), includes total_count in the response. Set to false to skip + the count query for better performance on large datasets. + limit: The limit of items to return. Default is 20. Max is 5000. metadata_key: Filter snapshots by metadata key-value pair. Can be used multiple times for @@ -2812,6 +2825,7 @@ def list_disk_snapshots( query=maybe_transform( { "devbox_id": devbox_id, + "include_total_count": include_total_count, "limit": limit, "metadata_key": metadata_key, "metadata_key_in": metadata_key_in, @@ -2863,7 +2877,7 @@ async def read_file_contents( timeout = 600 extra_headers = {"Accept": "text/plain", **(extra_headers or {})} return await self._post( - f"/v1/devboxes/{id}/read_file_contents", + path_template("/v1/devboxes/{id}/read_file_contents", id=id), body=await async_maybe_transform( {"file_path": file_path}, devbox_read_file_contents_params.DevboxReadFileContentsParams ), @@ -2877,14 +2891,10 @@ async def read_file_contents( cast_to=str, ) - @typing_extensions.deprecated( - "remove_tunnel is deprecated; V2 tunnels cannot be removed and close on devbox shutdown." - ) async def remove_tunnel( self, id: str, *, - port: int, # 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, @@ -2893,14 +2903,10 @@ async def remove_tunnel( timeout: float | httpx.Timeout | None | NotGiven = not_given, idempotency_key: str | None = None, ) -> object: - """[Deprecated] V2 tunnels cannot be removed and close on devbox shutdown. - - This endpoint - removes a legacy tunnel. + """ + Remove an existing V2 tunnel from the Devbox. Args: - port: Devbox port that tunnel will expose. - extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -2914,8 +2920,7 @@ async def remove_tunnel( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._post( - f"/v1/devboxes/{id}/remove_tunnel", - body=await async_maybe_transform({"port": port}, devbox_remove_tunnel_params.DevboxRemoveTunnelParams), + path_template("/v1/devboxes/{id}/remove_tunnel", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -2958,7 +2963,7 @@ async def resume( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._post( - f"/v1/devboxes/{id}/resume", + path_template("/v1/devboxes/{id}/resume", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -2999,7 +3004,7 @@ async def retrieve_resource_usage( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._get( - f"/v1/devboxes/{id}/usage", + path_template("/v1/devboxes/{id}/usage", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -3043,7 +3048,7 @@ async def shutdown( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._post( - f"/v1/devboxes/{id}/shutdown", + path_template("/v1/devboxes/{id}/shutdown", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -3096,7 +3101,7 @@ async def snapshot_disk( if not is_given(timeout) and self._client.timeout == DEFAULT_TIMEOUT: timeout = 600 return await self._post( - f"/v1/devboxes/{id}/snapshot_disk", + path_template("/v1/devboxes/{id}/snapshot_disk", id=id), body=await async_maybe_transform( { "commit_message": commit_message, @@ -3155,7 +3160,7 @@ async def snapshot_disk_async( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._post( - f"/v1/devboxes/{id}/snapshot_disk_async", + path_template("/v1/devboxes/{id}/snapshot_disk_async", id=id), body=await async_maybe_transform( { "commit_message": commit_message, @@ -3205,7 +3210,7 @@ async def suspend( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._post( - f"/v1/devboxes/{id}/suspend", + path_template("/v1/devboxes/{id}/suspend", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -3266,7 +3271,7 @@ async def upload_file( # multipart/form-data; boundary=---abc-- extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} return await self._post( - f"/v1/devboxes/{id}/upload_file", + path_template("/v1/devboxes/{id}/upload_file", id=id), body=await async_maybe_transform(body, devbox_upload_file_params.DevboxUploadFileParams), files=files, options=make_request_options( @@ -3324,7 +3329,11 @@ async def wait_for_command( if not execution_id: raise ValueError(f"Expected a non-empty value for `execution_id` but received {execution_id!r}") return await self._post( - f"/v1/devboxes/{devbox_id}/executions/{execution_id}/wait_for_status", + path_template( + "/v1/devboxes/{devbox_id}/executions/{execution_id}/wait_for_status", + devbox_id=devbox_id, + execution_id=execution_id, + ), body=await async_maybe_transform( { "statuses": statuses, @@ -3385,7 +3394,7 @@ async def write_file_contents( if not is_given(timeout) and self._client.timeout == DEFAULT_TIMEOUT: timeout = 600 return await self._post( - f"/v1/devboxes/{id}/write_file_contents", + path_template("/v1/devboxes/{id}/write_file_contents", id=id), body=await async_maybe_transform( { "contents": contents, @@ -3453,10 +3462,8 @@ def __init__(self, devboxes: DevboxesResource) -> None: self.read_file_contents = to_raw_response_wrapper( devboxes.read_file_contents, ) - self.remove_tunnel = ( # pyright: ignore[reportDeprecated] - to_raw_response_wrapper( - devboxes.remove_tunnel, # pyright: ignore[reportDeprecated], - ) + self.remove_tunnel = to_raw_response_wrapper( + devboxes.remove_tunnel, ) self.resume = to_raw_response_wrapper( devboxes.resume, @@ -3556,10 +3563,8 @@ def __init__(self, devboxes: AsyncDevboxesResource) -> None: self.read_file_contents = async_to_raw_response_wrapper( devboxes.read_file_contents, ) - self.remove_tunnel = ( # pyright: ignore[reportDeprecated] - async_to_raw_response_wrapper( - devboxes.remove_tunnel, # pyright: ignore[reportDeprecated], - ) + self.remove_tunnel = async_to_raw_response_wrapper( + devboxes.remove_tunnel, ) self.resume = async_to_raw_response_wrapper( devboxes.resume, @@ -3659,10 +3664,8 @@ def __init__(self, devboxes: DevboxesResource) -> None: self.read_file_contents = to_streamed_response_wrapper( devboxes.read_file_contents, ) - self.remove_tunnel = ( # pyright: ignore[reportDeprecated] - to_streamed_response_wrapper( - devboxes.remove_tunnel, # pyright: ignore[reportDeprecated], - ) + self.remove_tunnel = to_streamed_response_wrapper( + devboxes.remove_tunnel, ) self.resume = to_streamed_response_wrapper( devboxes.resume, @@ -3762,10 +3765,8 @@ def __init__(self, devboxes: AsyncDevboxesResource) -> None: self.read_file_contents = async_to_streamed_response_wrapper( devboxes.read_file_contents, ) - self.remove_tunnel = ( # pyright: ignore[reportDeprecated] - async_to_streamed_response_wrapper( - devboxes.remove_tunnel, # pyright: ignore[reportDeprecated], - ) + self.remove_tunnel = async_to_streamed_response_wrapper( + devboxes.remove_tunnel, ) self.resume = async_to_streamed_response_wrapper( devboxes.resume, diff --git a/src/runloop_api_client/resources/devboxes/disk_snapshots.py b/src/runloop_api_client/resources/devboxes/disk_snapshots.py index b896adbb6..c4f723359 100644 --- a/src/runloop_api_client/resources/devboxes/disk_snapshots.py +++ b/src/runloop_api_client/resources/devboxes/disk_snapshots.py @@ -7,7 +7,7 @@ import httpx from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ..._utils import maybe_transform, async_maybe_transform +from ..._utils import path_template, maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import ( @@ -88,7 +88,7 @@ def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._post( - f"/v1/devboxes/disk_snapshots/{id}", + path_template("/v1/devboxes/disk_snapshots/{id}", id=id), body=maybe_transform( { "commit_message": commit_message, @@ -111,6 +111,7 @@ def list( self, *, devbox_id: str | Omit = omit, + include_total_count: bool | Omit = omit, limit: int | Omit = omit, metadata_key: str | Omit = omit, metadata_key_in: str | Omit = omit, @@ -130,6 +131,9 @@ def list( Args: devbox_id: Devbox ID to filter by. + include_total_count: If true (default), includes total_count in the response. Set to false to skip + the count query for better performance on large datasets. + limit: The limit of items to return. Default is 20. Max is 5000. metadata_key: Filter snapshots by metadata key-value pair. Can be used multiple times for @@ -160,6 +164,7 @@ def list( query=maybe_transform( { "devbox_id": devbox_id, + "include_total_count": include_total_count, "limit": limit, "metadata_key": metadata_key, "metadata_key_in": metadata_key_in, @@ -201,7 +206,7 @@ def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._post( - f"/v1/devboxes/disk_snapshots/{id}/delete", + path_template("/v1/devboxes/disk_snapshots/{id}/delete", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -239,7 +244,7 @@ def query_status( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get( - f"/v1/devboxes/disk_snapshots/{id}/status", + path_template("/v1/devboxes/disk_snapshots/{id}/status", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -339,7 +344,7 @@ async def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._post( - f"/v1/devboxes/disk_snapshots/{id}", + path_template("/v1/devboxes/disk_snapshots/{id}", id=id), body=await async_maybe_transform( { "commit_message": commit_message, @@ -362,6 +367,7 @@ def list( self, *, devbox_id: str | Omit = omit, + include_total_count: bool | Omit = omit, limit: int | Omit = omit, metadata_key: str | Omit = omit, metadata_key_in: str | Omit = omit, @@ -381,6 +387,9 @@ def list( Args: devbox_id: Devbox ID to filter by. + include_total_count: If true (default), includes total_count in the response. Set to false to skip + the count query for better performance on large datasets. + limit: The limit of items to return. Default is 20. Max is 5000. metadata_key: Filter snapshots by metadata key-value pair. Can be used multiple times for @@ -411,6 +420,7 @@ def list( query=maybe_transform( { "devbox_id": devbox_id, + "include_total_count": include_total_count, "limit": limit, "metadata_key": metadata_key, "metadata_key_in": metadata_key_in, @@ -452,7 +462,7 @@ async def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._post( - f"/v1/devboxes/disk_snapshots/{id}/delete", + path_template("/v1/devboxes/disk_snapshots/{id}/delete", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -490,7 +500,7 @@ async def query_status( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._get( - f"/v1/devboxes/disk_snapshots/{id}/status", + path_template("/v1/devboxes/disk_snapshots/{id}/status", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/runloop_api_client/resources/devboxes/executions.py b/src/runloop_api_client/resources/devboxes/executions.py index 8304c038d..ff7638798 100755 --- a/src/runloop_api_client/resources/devboxes/executions.py +++ b/src/runloop_api_client/resources/devboxes/executions.py @@ -9,7 +9,7 @@ import httpx from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ..._utils import is_given, maybe_transform, async_maybe_transform +from ..._utils import is_given, path_template, maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import ( @@ -104,7 +104,9 @@ def retrieve( if not execution_id: raise ValueError(f"Expected a non-empty value for `execution_id` but received {execution_id!r}") return self._get( - f"/v1/devboxes/{devbox_id}/executions/{execution_id}", + path_template( + "/v1/devboxes/{devbox_id}/executions/{execution_id}", devbox_id=devbox_id, execution_id=execution_id + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -210,7 +212,7 @@ def execute_async( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._post( - f"/v1/devboxes/{id}/execute_async", + path_template("/v1/devboxes/{id}/execute_async", id=id), body=maybe_transform( { "command": command, @@ -278,7 +280,7 @@ def execute_sync( if not is_given(timeout) and self._client.timeout == DEFAULT_TIMEOUT: timeout = 600 return self._post( - f"/v1/devboxes/{id}/execute_sync", + path_template("/v1/devboxes/{id}/execute_sync", id=id), body=maybe_transform( { "command": command, @@ -334,7 +336,11 @@ def kill( if not execution_id: raise ValueError(f"Expected a non-empty value for `execution_id` but received {execution_id!r}") return self._post( - f"/v1/devboxes/{devbox_id}/executions/{execution_id}/kill", + path_template( + "/v1/devboxes/{devbox_id}/executions/{execution_id}/kill", + devbox_id=devbox_id, + execution_id=execution_id, + ), body=maybe_transform({"kill_process_group": kill_process_group}, execution_kill_params.ExecutionKillParams), options=make_request_options( extra_headers=extra_headers, @@ -384,7 +390,11 @@ def send_std_in( if not execution_id: raise ValueError(f"Expected a non-empty value for `execution_id` but received {execution_id!r}") return self._post( - f"/v1/devboxes/{devbox_id}/executions/{execution_id}/send_std_in", + path_template( + "/v1/devboxes/{devbox_id}/executions/{execution_id}/send_std_in", + devbox_id=devbox_id, + execution_id=execution_id, + ), body=maybe_transform( { "signal": signal, @@ -435,12 +445,18 @@ def stream_stderr_updates( if not execution_id: raise ValueError(f"Expected a non-empty value for `execution_id` but received {execution_id!r}") + stream_path = path_template( + "/v1/devboxes/{devbox_id}/executions/{execution_id}/stream_stderr_updates", + devbox_id=devbox_id, + execution_id=execution_id, + ) + default_headers: Headers = {"Accept": "text/event-stream"} merged_headers = default_headers if extra_headers is None else {**default_headers, **extra_headers} if merged_headers and merged_headers.get(RAW_RESPONSE_HEADER): return self._get( - f"/v1/devboxes/{devbox_id}/executions/{execution_id}/stream_stderr_updates", + stream_path, options=make_request_options( extra_headers=merged_headers, extra_query=extra_query, @@ -458,7 +474,7 @@ def stream_stderr_updates( def create_stream(last_offset: str | None) -> Stream[ExecutionUpdateChunk]: new_offset = last_offset if last_offset is not None else (None if isinstance(offset, NotGiven) else offset) return self._get( - f"/v1/devboxes/{devbox_id}/executions/{execution_id}/stream_stderr_updates", + stream_path, options=make_request_options( extra_headers=merged_headers, extra_query=extra_query, @@ -520,12 +536,18 @@ def stream_stdout_updates( if not execution_id: raise ValueError(f"Expected a non-empty value for `execution_id` but received {execution_id!r}") + stream_path = path_template( + "/v1/devboxes/{devbox_id}/executions/{execution_id}/stream_stdout_updates", + devbox_id=devbox_id, + execution_id=execution_id, + ) + default_headers: Headers = {"Accept": "text/event-stream"} merged_headers = default_headers if extra_headers is None else {**default_headers, **extra_headers} if merged_headers and merged_headers.get(RAW_RESPONSE_HEADER): return self._get( - f"/v1/devboxes/{devbox_id}/executions/{execution_id}/stream_stdout_updates", + stream_path, options=make_request_options( extra_headers=merged_headers, extra_query=extra_query, @@ -543,7 +565,7 @@ def stream_stdout_updates( def create_stream(last_offset: str | None) -> Stream[ExecutionUpdateChunk]: new_offset = last_offset if last_offset is not None else (None if isinstance(offset, NotGiven) else offset) return self._get( - f"/v1/devboxes/{devbox_id}/executions/{execution_id}/stream_stdout_updates", + stream_path, options=make_request_options( extra_headers=merged_headers, extra_query=extra_query, @@ -626,7 +648,9 @@ async def retrieve( if not execution_id: raise ValueError(f"Expected a non-empty value for `execution_id` but received {execution_id!r}") return await self._get( - f"/v1/devboxes/{devbox_id}/executions/{execution_id}", + path_template( + "/v1/devboxes/{devbox_id}/executions/{execution_id}", devbox_id=devbox_id, execution_id=execution_id + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -730,7 +754,7 @@ async def execute_async( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._post( - f"/v1/devboxes/{id}/execute_async", + path_template("/v1/devboxes/{id}/execute_async", id=id), body=await async_maybe_transform( { "command": command, @@ -798,7 +822,7 @@ async def execute_sync( if not is_given(timeout) and self._client.timeout == DEFAULT_TIMEOUT: timeout = 600 return await self._post( - f"/v1/devboxes/{id}/execute_sync", + path_template("/v1/devboxes/{id}/execute_sync", id=id), body=await async_maybe_transform( { "command": command, @@ -854,7 +878,11 @@ async def kill( if not execution_id: raise ValueError(f"Expected a non-empty value for `execution_id` but received {execution_id!r}") return await self._post( - f"/v1/devboxes/{devbox_id}/executions/{execution_id}/kill", + path_template( + "/v1/devboxes/{devbox_id}/executions/{execution_id}/kill", + devbox_id=devbox_id, + execution_id=execution_id, + ), body=await async_maybe_transform( {"kill_process_group": kill_process_group}, execution_kill_params.ExecutionKillParams ), @@ -906,7 +934,11 @@ async def send_std_in( if not execution_id: raise ValueError(f"Expected a non-empty value for `execution_id` but received {execution_id!r}") return await self._post( - f"/v1/devboxes/{devbox_id}/executions/{execution_id}/send_std_in", + path_template( + "/v1/devboxes/{devbox_id}/executions/{execution_id}/send_std_in", + devbox_id=devbox_id, + execution_id=execution_id, + ), body=await async_maybe_transform( { "signal": signal, @@ -957,12 +989,18 @@ async def stream_stderr_updates( if not execution_id: raise ValueError(f"Expected a non-empty value for `execution_id` but received {execution_id!r}") + stream_path = path_template( + "/v1/devboxes/{devbox_id}/executions/{execution_id}/stream_stderr_updates", + devbox_id=devbox_id, + execution_id=execution_id, + ) + default_headers: Headers = {"Accept": "text/event-stream"} merged_headers = default_headers if extra_headers is None else {**default_headers, **extra_headers} if merged_headers and merged_headers.get(RAW_RESPONSE_HEADER): return await self._get( - f"/v1/devboxes/{devbox_id}/executions/{execution_id}/stream_stderr_updates", + stream_path, options=make_request_options( extra_headers=merged_headers, extra_query=extra_query, @@ -980,7 +1018,7 @@ async def stream_stderr_updates( async def create_stream(last_offset: str | None) -> AsyncStream[ExecutionUpdateChunk]: new_offset = last_offset if last_offset is not None else (None if isinstance(offset, NotGiven) else offset) return await self._get( - f"/v1/devboxes/{devbox_id}/executions/{execution_id}/stream_stderr_updates", + stream_path, options=make_request_options( extra_headers=merged_headers, extra_query=extra_query, @@ -1042,13 +1080,19 @@ async def stream_stdout_updates( if not execution_id: raise ValueError(f"Expected a non-empty value for `execution_id` but received {execution_id!r}") + stream_path = path_template( + "/v1/devboxes/{devbox_id}/executions/{execution_id}/stream_stdout_updates", + devbox_id=devbox_id, + execution_id=execution_id, + ) + default_headers: Headers = {"Accept": "text/event-stream"} merged_headers = default_headers if extra_headers is None else {**default_headers, **extra_headers} # If caller requested a raw or streaming response wrapper, return the underlying stream as-is if merged_headers and merged_headers.get(RAW_RESPONSE_HEADER): return await self._get( - f"/v1/devboxes/{devbox_id}/executions/{execution_id}/stream_stdout_updates", + stream_path, options=make_request_options( extra_headers=merged_headers, extra_query=extra_query, @@ -1066,7 +1110,7 @@ async def stream_stdout_updates( async def create_stream(last_offset: str | None) -> AsyncStream[ExecutionUpdateChunk]: new_offset = last_offset if last_offset is not None else (None if isinstance(offset, NotGiven) else offset) return await self._get( - f"/v1/devboxes/{devbox_id}/executions/{execution_id}/stream_stdout_updates", + stream_path, options=make_request_options( extra_headers=merged_headers, extra_query=extra_query, diff --git a/src/runloop_api_client/resources/devboxes/logs.py b/src/runloop_api_client/resources/devboxes/logs.py index 19d2c06e1..1e7383914 100644 --- a/src/runloop_api_client/resources/devboxes/logs.py +++ b/src/runloop_api_client/resources/devboxes/logs.py @@ -5,7 +5,7 @@ import httpx from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ..._utils import maybe_transform, async_maybe_transform +from ..._utils import path_template, maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import ( @@ -73,7 +73,7 @@ def list( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get( - f"/v1/devboxes/{id}/logs", + path_template("/v1/devboxes/{id}/logs", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -143,7 +143,7 @@ async def list( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._get( - f"/v1/devboxes/{id}/logs", + path_template("/v1/devboxes/{id}/logs", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, diff --git a/src/runloop_api_client/resources/gateway_configs.py b/src/runloop_api_client/resources/gateway_configs.py index 86ec22ce6..7874f7a79 100644 --- a/src/runloop_api_client/resources/gateway_configs.py +++ b/src/runloop_api_client/resources/gateway_configs.py @@ -8,7 +8,7 @@ from ..types import gateway_config_list_params, gateway_config_create_params, gateway_config_update_params from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from .._utils import maybe_transform, async_maybe_transform +from .._utils import path_template, maybe_transform, async_maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import ( @@ -132,7 +132,7 @@ def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get( - f"/v1/gateway-configs/{id}", + path_template("/v1/gateway-configs/{id}", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -181,7 +181,7 @@ def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._post( - f"/v1/gateway-configs/{id}", + path_template("/v1/gateway-configs/{id}", id=id), body=maybe_transform( { "auth_mechanism": auth_mechanism, @@ -205,6 +205,7 @@ def list( self, *, id: str | Omit = omit, + include_total_count: bool | Omit = omit, limit: int | Omit = omit, name: str | Omit = omit, starting_after: str | Omit = omit, @@ -222,6 +223,9 @@ def list( Args: id: Filter by ID. + include_total_count: If true (default), includes total_count in the response. Set to false to skip + the count query for better performance on large datasets. + limit: The limit of items to return. Default is 20. Max is 5000. name: Filter by name (partial match supported). @@ -247,6 +251,7 @@ def list( query=maybe_transform( { "id": id, + "include_total_count": include_total_count, "limit": limit, "name": name, "starting_after": starting_after, @@ -287,7 +292,7 @@ def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._post( - f"/v1/gateway-configs/{id}/delete", + path_template("/v1/gateway-configs/{id}/delete", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -407,7 +412,7 @@ async def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._get( - f"/v1/gateway-configs/{id}", + path_template("/v1/gateway-configs/{id}", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -456,7 +461,7 @@ async def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._post( - f"/v1/gateway-configs/{id}", + path_template("/v1/gateway-configs/{id}", id=id), body=await async_maybe_transform( { "auth_mechanism": auth_mechanism, @@ -480,6 +485,7 @@ def list( self, *, id: str | Omit = omit, + include_total_count: bool | Omit = omit, limit: int | Omit = omit, name: str | Omit = omit, starting_after: str | Omit = omit, @@ -497,6 +503,9 @@ def list( Args: id: Filter by ID. + include_total_count: If true (default), includes total_count in the response. Set to false to skip + the count query for better performance on large datasets. + limit: The limit of items to return. Default is 20. Max is 5000. name: Filter by name (partial match supported). @@ -522,6 +531,7 @@ def list( query=maybe_transform( { "id": id, + "include_total_count": include_total_count, "limit": limit, "name": name, "starting_after": starting_after, @@ -562,7 +572,7 @@ async def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._post( - f"/v1/gateway-configs/{id}/delete", + path_template("/v1/gateway-configs/{id}/delete", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, diff --git a/src/runloop_api_client/resources/mcp_configs.py b/src/runloop_api_client/resources/mcp_configs.py index 24b05cc2d..b097db18b 100644 --- a/src/runloop_api_client/resources/mcp_configs.py +++ b/src/runloop_api_client/resources/mcp_configs.py @@ -8,7 +8,7 @@ from ..types import mcp_config_list_params, mcp_config_create_params, mcp_config_update_params from .._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given -from .._utils import maybe_transform, async_maybe_transform +from .._utils import path_template, maybe_transform, async_maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import ( @@ -134,7 +134,7 @@ def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get( - f"/v1/mcp-configs/{id}", + path_template("/v1/mcp-configs/{id}", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -184,7 +184,7 @@ def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._post( - f"/v1/mcp-configs/{id}", + path_template("/v1/mcp-configs/{id}", id=id), body=maybe_transform( { "allowed_tools": allowed_tools, @@ -208,6 +208,7 @@ def list( self, *, id: str | Omit = omit, + include_total_count: bool | Omit = omit, limit: int | Omit = omit, name: str | Omit = omit, starting_after: str | Omit = omit, @@ -224,6 +225,9 @@ def list( Args: id: Filter by ID. + include_total_count: If true (default), includes total_count in the response. Set to false to skip + the count query for better performance on large datasets. + limit: The limit of items to return. Default is 20. Max is 5000. name: Filter by name (prefix match supported). @@ -249,6 +253,7 @@ def list( query=maybe_transform( { "id": id, + "include_total_count": include_total_count, "limit": limit, "name": name, "starting_after": starting_after, @@ -289,7 +294,7 @@ def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._post( - f"/v1/mcp-configs/{id}/delete", + path_template("/v1/mcp-configs/{id}/delete", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -411,7 +416,7 @@ async def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._get( - f"/v1/mcp-configs/{id}", + path_template("/v1/mcp-configs/{id}", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -461,7 +466,7 @@ async def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._post( - f"/v1/mcp-configs/{id}", + path_template("/v1/mcp-configs/{id}", id=id), body=await async_maybe_transform( { "allowed_tools": allowed_tools, @@ -485,6 +490,7 @@ def list( self, *, id: str | Omit = omit, + include_total_count: bool | Omit = omit, limit: int | Omit = omit, name: str | Omit = omit, starting_after: str | Omit = omit, @@ -501,6 +507,9 @@ def list( Args: id: Filter by ID. + include_total_count: If true (default), includes total_count in the response. Set to false to skip + the count query for better performance on large datasets. + limit: The limit of items to return. Default is 20. Max is 5000. name: Filter by name (prefix match supported). @@ -526,6 +535,7 @@ def list( query=maybe_transform( { "id": id, + "include_total_count": include_total_count, "limit": limit, "name": name, "starting_after": starting_after, @@ -566,7 +576,7 @@ async def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._post( - f"/v1/mcp-configs/{id}/delete", + path_template("/v1/mcp-configs/{id}/delete", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, diff --git a/src/runloop_api_client/resources/network_policies.py b/src/runloop_api_client/resources/network_policies.py index f50d4fe6d..7ba7abd9e 100644 --- a/src/runloop_api_client/resources/network_policies.py +++ b/src/runloop_api_client/resources/network_policies.py @@ -8,7 +8,7 @@ from ..types import network_policy_list_params, network_policy_create_params, network_policy_update_params from .._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given -from .._utils import maybe_transform, async_maybe_transform +from .._utils import path_template, maybe_transform, async_maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import ( @@ -149,7 +149,7 @@ def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get( - f"/v1/network-policies/{id}", + path_template("/v1/network-policies/{id}", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -208,7 +208,7 @@ def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._post( - f"/v1/network-policies/{id}", + path_template("/v1/network-policies/{id}", id=id), body=maybe_transform( { "allow_agent_gateway": allow_agent_gateway, @@ -235,6 +235,7 @@ def list( self, *, id: str | Omit = omit, + include_total_count: bool | Omit = omit, limit: int | Omit = omit, name: str | Omit = omit, starting_after: str | Omit = omit, @@ -251,6 +252,9 @@ def list( Args: id: Filter by ID. + include_total_count: If true (default), includes total_count in the response. Set to false to skip + the count query for better performance on large datasets. + limit: The limit of items to return. Default is 20. Max is 5000. name: Filter by name (partial match supported). @@ -276,6 +280,7 @@ def list( query=maybe_transform( { "id": id, + "include_total_count": include_total_count, "limit": limit, "name": name, "starting_after": starting_after, @@ -316,7 +321,7 @@ def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._post( - f"/v1/network-policies/{id}/delete", + path_template("/v1/network-policies/{id}/delete", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -453,7 +458,7 @@ async def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._get( - f"/v1/network-policies/{id}", + path_template("/v1/network-policies/{id}", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -512,7 +517,7 @@ async def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._post( - f"/v1/network-policies/{id}", + path_template("/v1/network-policies/{id}", id=id), body=await async_maybe_transform( { "allow_agent_gateway": allow_agent_gateway, @@ -539,6 +544,7 @@ def list( self, *, id: str | Omit = omit, + include_total_count: bool | Omit = omit, limit: int | Omit = omit, name: str | Omit = omit, starting_after: str | Omit = omit, @@ -555,6 +561,9 @@ def list( Args: id: Filter by ID. + include_total_count: If true (default), includes total_count in the response. Set to false to skip + the count query for better performance on large datasets. + limit: The limit of items to return. Default is 20. Max is 5000. name: Filter by name (partial match supported). @@ -580,6 +589,7 @@ def list( query=maybe_transform( { "id": id, + "include_total_count": include_total_count, "limit": limit, "name": name, "starting_after": starting_after, @@ -620,7 +630,7 @@ async def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._post( - f"/v1/network-policies/{id}/delete", + path_template("/v1/network-policies/{id}/delete", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, diff --git a/src/runloop_api_client/resources/objects.py b/src/runloop_api_client/resources/objects.py index 409d5f6f3..5df9476ec 100644 --- a/src/runloop_api_client/resources/objects.py +++ b/src/runloop_api_client/resources/objects.py @@ -9,7 +9,7 @@ from ..types import object_list_params, object_create_params, object_download_params, object_list_public_params from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from .._utils import maybe_transform, async_maybe_transform +from .._utils import path_template, maybe_transform, async_maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import ( @@ -133,7 +133,7 @@ def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get( - f"/v1/objects/{id}", + path_template("/v1/objects/{id}", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -144,6 +144,7 @@ def list( self, *, content_type: Literal["unspecified", "text", "binary", "gzip", "tar", "tgz"] | Omit = omit, + include_total_count: bool | Omit = omit, limit: int | Omit = omit, name: str | Omit = omit, search: str | Omit = omit, @@ -162,6 +163,9 @@ def list( Args: content_type: Filter storage objects by content type. + include_total_count: If true (default), includes total_count in the response. Set to false to skip + the count query for better performance on large datasets. + limit: The limit of items to return. Default is 20. Max is 5000. name: Filter storage objects by name (partial match supported). @@ -191,6 +195,7 @@ def list( query=maybe_transform( { "content_type": content_type, + "include_total_count": include_total_count, "limit": limit, "name": name, "search": search, @@ -234,7 +239,7 @@ def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._post( - f"/v1/objects/{id}/delete", + path_template("/v1/objects/{id}/delete", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -275,7 +280,7 @@ def complete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._post( - f"/v1/objects/{id}/complete", + path_template("/v1/objects/{id}/complete", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -317,7 +322,7 @@ def download( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get( - f"/v1/objects/{id}/download", + path_template("/v1/objects/{id}/download", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -334,6 +339,7 @@ def list_public( self, *, content_type: Literal["unspecified", "text", "binary", "gzip", "tar", "tgz"] | Omit = omit, + include_total_count: bool | Omit = omit, limit: int | Omit = omit, name: str | Omit = omit, search: str | Omit = omit, @@ -352,6 +358,9 @@ def list_public( Args: content_type: Filter storage objects by content type. + include_total_count: If true (default), includes total_count in the response. Set to false to skip + the count query for better performance on large datasets. + limit: The limit of items to return. Default is 20. Max is 5000. name: Filter storage objects by name (partial match supported). @@ -381,6 +390,7 @@ def list_public( query=maybe_transform( { "content_type": content_type, + "include_total_count": include_total_count, "limit": limit, "name": name, "search": search, @@ -501,7 +511,7 @@ async def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._get( - f"/v1/objects/{id}", + path_template("/v1/objects/{id}", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -512,6 +522,7 @@ def list( self, *, content_type: Literal["unspecified", "text", "binary", "gzip", "tar", "tgz"] | Omit = omit, + include_total_count: bool | Omit = omit, limit: int | Omit = omit, name: str | Omit = omit, search: str | Omit = omit, @@ -530,6 +541,9 @@ def list( Args: content_type: Filter storage objects by content type. + include_total_count: If true (default), includes total_count in the response. Set to false to skip + the count query for better performance on large datasets. + limit: The limit of items to return. Default is 20. Max is 5000. name: Filter storage objects by name (partial match supported). @@ -559,6 +573,7 @@ def list( query=maybe_transform( { "content_type": content_type, + "include_total_count": include_total_count, "limit": limit, "name": name, "search": search, @@ -602,7 +617,7 @@ async def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._post( - f"/v1/objects/{id}/delete", + path_template("/v1/objects/{id}/delete", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -643,7 +658,7 @@ async def complete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._post( - f"/v1/objects/{id}/complete", + path_template("/v1/objects/{id}/complete", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -685,7 +700,7 @@ async def download( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._get( - f"/v1/objects/{id}/download", + path_template("/v1/objects/{id}/download", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -702,6 +717,7 @@ def list_public( self, *, content_type: Literal["unspecified", "text", "binary", "gzip", "tar", "tgz"] | Omit = omit, + include_total_count: bool | Omit = omit, limit: int | Omit = omit, name: str | Omit = omit, search: str | Omit = omit, @@ -720,6 +736,9 @@ def list_public( Args: content_type: Filter storage objects by content type. + include_total_count: If true (default), includes total_count in the response. Set to false to skip + the count query for better performance on large datasets. + limit: The limit of items to return. Default is 20. Max is 5000. name: Filter storage objects by name (partial match supported). @@ -749,6 +768,7 @@ def list_public( query=maybe_transform( { "content_type": content_type, + "include_total_count": include_total_count, "limit": limit, "name": name, "search": search, diff --git a/src/runloop_api_client/resources/repositories.py b/src/runloop_api_client/resources/repositories.py index a22075540..5b961cf64 100644 --- a/src/runloop_api_client/resources/repositories.py +++ b/src/runloop_api_client/resources/repositories.py @@ -14,7 +14,7 @@ repository_refresh_params, ) from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from .._utils import maybe_transform, async_maybe_transform +from .._utils import path_template, maybe_transform, async_maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import ( @@ -138,7 +138,7 @@ def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get( - f"/v1/repositories/{id}", + path_template("/v1/repositories/{id}", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -148,6 +148,7 @@ def retrieve( def list( self, *, + include_total_count: bool | Omit = omit, limit: int | Omit = omit, name: str | Omit = omit, owner: str | Omit = omit, @@ -163,6 +164,9 @@ def list( List all available repository connections. Args: + include_total_count: If true (default), includes total_count in the response. Set to false to skip + the count query for better performance on large datasets. + limit: The limit of items to return. Default is 20. Max is 5000. name: Filter by repository name @@ -189,6 +193,7 @@ def list( timeout=timeout, query=maybe_transform( { + "include_total_count": include_total_count, "limit": limit, "name": name, "owner": owner, @@ -230,7 +235,7 @@ def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._post( - f"/v1/repositories/{id}/delete", + path_template("/v1/repositories/{id}/delete", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -274,7 +279,7 @@ def inspect( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._post( - f"/v1/repositories/{id}/inspect", + path_template("/v1/repositories/{id}/inspect", id=id), body=maybe_transform( {"github_auth_token": github_auth_token}, repository_inspect_params.RepositoryInspectParams ), @@ -315,7 +320,7 @@ def list_inspections( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get( - f"/v1/repositories/{id}/inspections", + path_template("/v1/repositories/{id}/inspections", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -359,7 +364,7 @@ def refresh( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._post( - f"/v1/repositories/{id}/refresh", + path_template("/v1/repositories/{id}/refresh", id=id), body=maybe_transform( { "blueprint_id": blueprint_id, @@ -403,7 +408,7 @@ def retrieve_inspection( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get( - f"/v1/repositories/inspections/{id}", + path_template("/v1/repositories/inspections/{id}", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -517,7 +522,7 @@ async def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._get( - f"/v1/repositories/{id}", + path_template("/v1/repositories/{id}", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -527,6 +532,7 @@ async def retrieve( def list( self, *, + include_total_count: bool | Omit = omit, limit: int | Omit = omit, name: str | Omit = omit, owner: str | Omit = omit, @@ -542,6 +548,9 @@ def list( List all available repository connections. Args: + include_total_count: If true (default), includes total_count in the response. Set to false to skip + the count query for better performance on large datasets. + limit: The limit of items to return. Default is 20. Max is 5000. name: Filter by repository name @@ -568,6 +577,7 @@ def list( timeout=timeout, query=maybe_transform( { + "include_total_count": include_total_count, "limit": limit, "name": name, "owner": owner, @@ -609,7 +619,7 @@ async def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._post( - f"/v1/repositories/{id}/delete", + path_template("/v1/repositories/{id}/delete", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -653,7 +663,7 @@ async def inspect( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._post( - f"/v1/repositories/{id}/inspect", + path_template("/v1/repositories/{id}/inspect", id=id), body=await async_maybe_transform( {"github_auth_token": github_auth_token}, repository_inspect_params.RepositoryInspectParams ), @@ -694,7 +704,7 @@ async def list_inspections( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._get( - f"/v1/repositories/{id}/inspections", + path_template("/v1/repositories/{id}/inspections", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -738,7 +748,7 @@ async def refresh( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._post( - f"/v1/repositories/{id}/refresh", + path_template("/v1/repositories/{id}/refresh", id=id), body=await async_maybe_transform( { "blueprint_id": blueprint_id, @@ -782,7 +792,7 @@ async def retrieve_inspection( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._get( - f"/v1/repositories/inspections/{id}", + path_template("/v1/repositories/inspections/{id}", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/runloop_api_client/resources/scenarios/runs.py b/src/runloop_api_client/resources/scenarios/runs.py index 3ea9a960f..67c5c4428 100644 --- a/src/runloop_api_client/resources/scenarios/runs.py +++ b/src/runloop_api_client/resources/scenarios/runs.py @@ -5,7 +5,7 @@ import httpx from ..._types import NOT_GIVEN, Body, Omit, Query, Headers, NotGiven, omit, not_given -from ..._utils import maybe_transform +from ..._utils import path_template, maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import ( @@ -79,7 +79,7 @@ def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get( - f"/v1/scenarios/runs/{id}", + path_template("/v1/scenarios/runs/{id}", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -90,6 +90,7 @@ def list( self, *, benchmark_run_id: str | Omit = omit, + include_total_count: bool | Omit = omit, limit: int | Omit = omit, name: str | Omit = omit, scenario_id: str | Omit = omit, @@ -108,6 +109,9 @@ def list( Args: benchmark_run_id: Filter by benchmark run ID + include_total_count: If true (default), includes total_count in the response. Set to false to skip + the count query for better performance on large datasets. + limit: The limit of items to return. Default is 20. Max is 5000. name: Filter by name @@ -137,6 +141,7 @@ def list( query=maybe_transform( { "benchmark_run_id": benchmark_run_id, + "include_total_count": include_total_count, "limit": limit, "name": name, "scenario_id": scenario_id, @@ -180,7 +185,7 @@ def cancel( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._post( - f"/v1/scenarios/runs/{id}/cancel", + path_template("/v1/scenarios/runs/{id}/cancel", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -222,7 +227,7 @@ def complete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._post( - f"/v1/scenarios/runs/{id}/complete", + path_template("/v1/scenarios/runs/{id}/complete", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -264,7 +269,7 @@ def download_logs( raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") extra_headers = {"Accept": "application/zip", **(extra_headers or {})} return self._post( - f"/v1/scenarios/runs/{id}/download_logs", + path_template("/v1/scenarios/runs/{id}/download_logs", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -304,7 +309,7 @@ def score( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._post( - f"/v1/scenarios/runs/{id}/score", + path_template("/v1/scenarios/runs/{id}/score", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -499,7 +504,7 @@ async def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._get( - f"/v1/scenarios/runs/{id}", + path_template("/v1/scenarios/runs/{id}", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -510,6 +515,7 @@ def list( self, *, benchmark_run_id: str | Omit = omit, + include_total_count: bool | Omit = omit, limit: int | Omit = omit, name: str | Omit = omit, scenario_id: str | Omit = omit, @@ -528,6 +534,9 @@ def list( Args: benchmark_run_id: Filter by benchmark run ID + include_total_count: If true (default), includes total_count in the response. Set to false to skip + the count query for better performance on large datasets. + limit: The limit of items to return. Default is 20. Max is 5000. name: Filter by name @@ -557,6 +566,7 @@ def list( query=maybe_transform( { "benchmark_run_id": benchmark_run_id, + "include_total_count": include_total_count, "limit": limit, "name": name, "scenario_id": scenario_id, @@ -600,7 +610,7 @@ async def cancel( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._post( - f"/v1/scenarios/runs/{id}/cancel", + path_template("/v1/scenarios/runs/{id}/cancel", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -642,7 +652,7 @@ async def complete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._post( - f"/v1/scenarios/runs/{id}/complete", + path_template("/v1/scenarios/runs/{id}/complete", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -684,7 +694,7 @@ async def download_logs( raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") extra_headers = {"Accept": "application/zip", **(extra_headers or {})} return await self._post( - f"/v1/scenarios/runs/{id}/download_logs", + path_template("/v1/scenarios/runs/{id}/download_logs", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -724,7 +734,7 @@ async def score( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._post( - f"/v1/scenarios/runs/{id}/score", + path_template("/v1/scenarios/runs/{id}/score", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, diff --git a/src/runloop_api_client/resources/scenarios/scenarios.py b/src/runloop_api_client/resources/scenarios/scenarios.py index 5c31e4282..e3ce8c91b 100644 --- a/src/runloop_api_client/resources/scenarios/scenarios.py +++ b/src/runloop_api_client/resources/scenarios/scenarios.py @@ -31,7 +31,7 @@ AsyncScorersResourceWithStreamingResponse, ) from ..._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given -from ..._utils import maybe_transform, async_maybe_transform +from ..._utils import path_template, maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import ( @@ -197,7 +197,7 @@ def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get( - f"/v1/scenarios/{id}", + path_template("/v1/scenarios/{id}", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -268,7 +268,7 @@ def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._post( - f"/v1/scenarios/{id}", + path_template("/v1/scenarios/{id}", id=id), body=maybe_transform( { "environment_parameters": environment_parameters, @@ -298,6 +298,7 @@ def list( self, *, benchmark_id: str | Omit = omit, + include_total_count: bool | Omit = omit, limit: int | Omit = omit, name: str | Omit = omit, starting_after: str | Omit = omit, @@ -315,6 +316,9 @@ def list( Args: benchmark_id: Filter scenarios by benchmark ID. + include_total_count: If true (default), includes total_count in the response. Set to false to skip + the count query for better performance on large datasets. + limit: The limit of items to return. Default is 20. Max is 5000. name: Query for Scenarios with a given name. @@ -342,6 +346,7 @@ def list( query=maybe_transform( { "benchmark_id": benchmark_id, + "include_total_count": include_total_count, "limit": limit, "name": name, "starting_after": starting_after, @@ -384,7 +389,7 @@ def archive( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._post( - f"/v1/scenarios/{id}/archive", + path_template("/v1/scenarios/{id}/archive", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -398,6 +403,7 @@ def archive( def list_public( self, *, + include_total_count: bool | Omit = omit, limit: int | Omit = omit, name: str | Omit = omit, starting_after: str | Omit = omit, @@ -412,6 +418,9 @@ def list_public( List all public scenarios matching filter. Args: + include_total_count: If true (default), includes total_count in the response. Set to false to skip + the count query for better performance on large datasets. + limit: The limit of items to return. Default is 20. Max is 5000. name: Query for Scenarios with a given name. @@ -436,6 +445,7 @@ def list_public( timeout=timeout, query=maybe_transform( { + "include_total_count": include_total_count, "limit": limit, "name": name, "starting_after": starting_after, @@ -709,7 +719,7 @@ async def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._get( - f"/v1/scenarios/{id}", + path_template("/v1/scenarios/{id}", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -780,7 +790,7 @@ async def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._post( - f"/v1/scenarios/{id}", + path_template("/v1/scenarios/{id}", id=id), body=await async_maybe_transform( { "environment_parameters": environment_parameters, @@ -810,6 +820,7 @@ def list( self, *, benchmark_id: str | Omit = omit, + include_total_count: bool | Omit = omit, limit: int | Omit = omit, name: str | Omit = omit, starting_after: str | Omit = omit, @@ -827,6 +838,9 @@ def list( Args: benchmark_id: Filter scenarios by benchmark ID. + include_total_count: If true (default), includes total_count in the response. Set to false to skip + the count query for better performance on large datasets. + limit: The limit of items to return. Default is 20. Max is 5000. name: Query for Scenarios with a given name. @@ -854,6 +868,7 @@ def list( query=maybe_transform( { "benchmark_id": benchmark_id, + "include_total_count": include_total_count, "limit": limit, "name": name, "starting_after": starting_after, @@ -896,7 +911,7 @@ async def archive( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._post( - f"/v1/scenarios/{id}/archive", + path_template("/v1/scenarios/{id}/archive", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -910,6 +925,7 @@ async def archive( def list_public( self, *, + include_total_count: bool | Omit = omit, limit: int | Omit = omit, name: str | Omit = omit, starting_after: str | Omit = omit, @@ -924,6 +940,9 @@ def list_public( List all public scenarios matching filter. Args: + include_total_count: If true (default), includes total_count in the response. Set to false to skip + the count query for better performance on large datasets. + limit: The limit of items to return. Default is 20. Max is 5000. name: Query for Scenarios with a given name. @@ -948,6 +967,7 @@ def list_public( timeout=timeout, query=maybe_transform( { + "include_total_count": include_total_count, "limit": limit, "name": name, "starting_after": starting_after, diff --git a/src/runloop_api_client/resources/scenarios/scorers.py b/src/runloop_api_client/resources/scenarios/scorers.py index cdb011dc7..1472d86af 100644 --- a/src/runloop_api_client/resources/scenarios/scorers.py +++ b/src/runloop_api_client/resources/scenarios/scorers.py @@ -5,7 +5,7 @@ import httpx from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ..._utils import maybe_transform, async_maybe_transform +from ..._utils import path_template, maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import ( @@ -122,7 +122,7 @@ def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get( - f"/v1/scenarios/scorers/{id}", + path_template("/v1/scenarios/scorers/{id}", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -165,7 +165,7 @@ def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._post( - f"/v1/scenarios/scorers/{id}", + path_template("/v1/scenarios/scorers/{id}", id=id), body=maybe_transform( { "bash_script": bash_script, @@ -186,6 +186,7 @@ def update( def list( self, *, + include_total_count: bool | Omit = omit, limit: int | Omit = omit, starting_after: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -199,6 +200,9 @@ def list( List all Scenario Scorers matching filter. Args: + include_total_count: If true (default), includes total_count in the response. Set to false to skip + the count query for better performance on large datasets. + limit: The limit of items to return. Default is 20. Max is 5000. starting_after: Load the next page of data starting after the item with the given ID. @@ -221,6 +225,7 @@ def list( timeout=timeout, query=maybe_transform( { + "include_total_count": include_total_count, "limit": limit, "starting_after": starting_after, }, @@ -328,7 +333,7 @@ async def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._get( - f"/v1/scenarios/scorers/{id}", + path_template("/v1/scenarios/scorers/{id}", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -371,7 +376,7 @@ async def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._post( - f"/v1/scenarios/scorers/{id}", + path_template("/v1/scenarios/scorers/{id}", id=id), body=await async_maybe_transform( { "bash_script": bash_script, @@ -392,6 +397,7 @@ async def update( def list( self, *, + include_total_count: bool | Omit = omit, limit: int | Omit = omit, starting_after: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -405,6 +411,9 @@ def list( List all Scenario Scorers matching filter. Args: + include_total_count: If true (default), includes total_count in the response. Set to false to skip + the count query for better performance on large datasets. + limit: The limit of items to return. Default is 20. Max is 5000. starting_after: Load the next page of data starting after the item with the given ID. @@ -427,6 +436,7 @@ def list( timeout=timeout, query=maybe_transform( { + "include_total_count": include_total_count, "limit": limit, "starting_after": starting_after, }, diff --git a/src/runloop_api_client/resources/secrets.py b/src/runloop_api_client/resources/secrets.py index fa7d45471..38a9d8fc0 100644 --- a/src/runloop_api_client/resources/secrets.py +++ b/src/runloop_api_client/resources/secrets.py @@ -6,7 +6,7 @@ from ..types import secret_list_params, secret_create_params, secret_update_params from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from .._utils import maybe_transform, async_maybe_transform +from .._utils import path_template, maybe_transform, async_maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import ( @@ -164,7 +164,7 @@ def update( if not name: raise ValueError(f"Expected a non-empty value for `name` but received {name!r}") return self._post( - f"/v1/secrets/{name}", + path_template("/v1/secrets/{name}", name=name), body=maybe_transform({"value": value}, secret_update_params.SecretUpdateParams), options=make_request_options( extra_headers=extra_headers, @@ -246,7 +246,7 @@ def delete( if not name: raise ValueError(f"Expected a non-empty value for `name` but received {name!r}") return self._post( - f"/v1/secrets/{name}/delete", + path_template("/v1/secrets/{name}/delete", name=name), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -400,7 +400,7 @@ async def update( if not name: raise ValueError(f"Expected a non-empty value for `name` but received {name!r}") return await self._post( - f"/v1/secrets/{name}", + path_template("/v1/secrets/{name}", name=name), body=await async_maybe_transform({"value": value}, secret_update_params.SecretUpdateParams), options=make_request_options( extra_headers=extra_headers, @@ -482,7 +482,7 @@ async def delete( if not name: raise ValueError(f"Expected a non-empty value for `name` but received {name!r}") return await self._post( - f"/v1/secrets/{name}/delete", + path_template("/v1/secrets/{name}/delete", name=name), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, diff --git a/src/runloop_api_client/sdk/_types.py b/src/runloop_api_client/sdk/_types.py index c66efebe3..82e4bbb9a 100644 --- a/src/runloop_api_client/sdk/_types.py +++ b/src/runloop_api_client/sdk/_types.py @@ -26,7 +26,6 @@ NetworkPolicyListParams, DevboxDownloadFileParams, DevboxEnableTunnelParams, - DevboxRemoveTunnelParams, DevboxSnapshotDiskParams, GatewayConfigCreateParams, GatewayConfigUpdateParams, @@ -127,7 +126,7 @@ class SDKDevboxEnableTunnelParams(DevboxEnableTunnelParams, LongRequestOptions): pass -class SDKDevboxRemoveTunnelParams(DevboxRemoveTunnelParams, LongRequestOptions): +class SDKDevboxRemoveTunnelParams(LongRequestOptions): pass diff --git a/src/runloop_api_client/sdk/async_devbox.py b/src/runloop_api_client/sdk/async_devbox.py index 9a97cb5f3..cdedcb5db 100644 --- a/src/runloop_api_client/sdk/async_devbox.py +++ b/src/runloop_api_client/sdk/async_devbox.py @@ -840,7 +840,7 @@ async def remove_tunnel( :rtype: object Example: - >>> await devbox.net.remove_tunnel(port=8080) + >>> await devbox.net.remove_tunnel() """ warnings.warn( "remove_tunnel is deprecated; V2 tunnels cannot be removed and close on devbox shutdown.", diff --git a/src/runloop_api_client/sdk/devbox.py b/src/runloop_api_client/sdk/devbox.py index ac1743a6b..aec345715 100644 --- a/src/runloop_api_client/sdk/devbox.py +++ b/src/runloop_api_client/sdk/devbox.py @@ -843,7 +843,7 @@ def remove_tunnel( :rtype: object Example: - >>> devbox.net.remove_tunnel(port=8080) + >>> devbox.net.remove_tunnel() """ warnings.warn( "remove_tunnel is deprecated; V2 tunnels cannot be removed and close on devbox shutdown.", diff --git a/src/runloop_api_client/types/__init__.py b/src/runloop_api_client/types/__init__.py index 693d5c6f8..11f82aa7a 100644 --- a/src/runloop_api_client/types/__init__.py +++ b/src/runloop_api_client/types/__init__.py @@ -12,6 +12,7 @@ LaunchParameters as LaunchParameters, CodeMountParameters as CodeMountParameters, ) +from .axon_view import AxonView as AxonView from .agent_view import AgentView as AgentView from .devbox_view import DevboxView as DevboxView from .object_view import ObjectView as ObjectView @@ -19,9 +20,11 @@ from .tunnel_view import TunnelView as TunnelView from .input_context import InputContext as InputContext from .scenario_view import ScenarioView as ScenarioView +from .axon_list_view import AxonListView as AxonListView from .benchmark_view import BenchmarkView as BenchmarkView from .blueprint_view import BlueprintView as BlueprintView from .agent_list_view import AgentListView as AgentListView +from .axon_event_view import AxonEventView as AxonEventView from .mcp_config_view import McpConfigView as McpConfigView from .devbox_list_view import DevboxListView as DevboxListView from .object_list_view import ObjectListView as ObjectListView @@ -30,17 +33,20 @@ from .secret_list_view import SecretListView as SecretListView from .agent_list_params import AgentListParams as AgentListParams from .scenario_run_view import ScenarioRunView as ScenarioRunView +from .axon_create_params import AxonCreateParams as AxonCreateParams from .benchmark_job_view import BenchmarkJobView as BenchmarkJobView from .benchmark_run_view import BenchmarkRunView as BenchmarkRunView from .devbox_list_params import DevboxListParams as DevboxListParams from .object_list_params import ObjectListParams as ObjectListParams from .secret_list_params import SecretListParams as SecretListParams from .agent_create_params import AgentCreateParams as AgentCreateParams +from .axon_publish_params import AxonPublishParams as AxonPublishParams from .blueprint_build_log import BlueprintBuildLog as BlueprintBuildLog from .blueprint_list_view import BlueprintListView as BlueprintListView from .gateway_config_view import GatewayConfigView as GatewayConfigView from .input_context_param import InputContextParam as InputContextParam from .network_policy_view import NetworkPolicyView as NetworkPolicyView +from .publish_result_view import PublishResultView as PublishResultView from .devbox_create_params import DevboxCreateParams as DevboxCreateParams from .devbox_snapshot_view import DevboxSnapshotView as DevboxSnapshotView from .devbox_update_params import DevboxUpdateParams as DevboxUpdateParams @@ -99,7 +105,6 @@ from .devbox_download_file_params import DevboxDownloadFileParams as DevboxDownloadFileParams from .devbox_enable_tunnel_params import DevboxEnableTunnelParams as DevboxEnableTunnelParams from .devbox_execute_async_params import DevboxExecuteAsyncParams as DevboxExecuteAsyncParams -from .devbox_remove_tunnel_params import DevboxRemoveTunnelParams as DevboxRemoveTunnelParams from .devbox_snapshot_disk_params import DevboxSnapshotDiskParams as DevboxSnapshotDiskParams from .scenario_list_public_params import ScenarioListPublicParams as ScenarioListPublicParams from .benchmark_definitions_params import BenchmarkDefinitionsParams as BenchmarkDefinitionsParams diff --git a/src/runloop_api_client/types/agent_list_params.py b/src/runloop_api_client/types/agent_list_params.py index 3df89fc25..5c3857fb5 100644 --- a/src/runloop_api_client/types/agent_list_params.py +++ b/src/runloop_api_client/types/agent_list_params.py @@ -8,6 +8,12 @@ class AgentListParams(TypedDict, total=False): + include_total_count: bool + """If true (default), includes total_count in the response. + + Set to false to skip the count query for better performance on large datasets. + """ + is_public: bool """Filter agents by public visibility.""" diff --git a/src/runloop_api_client/types/agent_list_view.py b/src/runloop_api_client/types/agent_list_view.py index 9e57a9769..f3dfbd09f 100644 --- a/src/runloop_api_client/types/agent_list_view.py +++ b/src/runloop_api_client/types/agent_list_view.py @@ -17,14 +17,5 @@ class AgentListView(BaseModel): has_more: bool """Whether there are more Agents to fetch.""" - remaining_count: Optional[int] = None - """The count of remaining Agents. - - Deprecated: will be removed in a future breaking change. - """ - total_count: Optional[int] = None - """The total count of Agents. - - Deprecated: will be removed in a future breaking change. - """ + """The total count of Agents.""" diff --git a/src/runloop_api_client/types/axon_create_params.py b/src/runloop_api_client/types/axon_create_params.py new file mode 100644 index 000000000..0906db0ae --- /dev/null +++ b/src/runloop_api_client/types/axon_create_params.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import TypedDict + +__all__ = ["AxonCreateParams"] + + +class AxonCreateParams(TypedDict, total=False): + name: Optional[str] + """(Optional) Name for the axon.""" diff --git a/src/runloop_api_client/types/axon_event_view.py b/src/runloop_api_client/types/axon_event_view.py new file mode 100644 index 000000000..63f09b80a --- /dev/null +++ b/src/runloop_api_client/types/axon_event_view.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from .._models import BaseModel + +__all__ = ["AxonEventView"] + + +class AxonEventView(BaseModel): + axon_id: str + """The axon identifier.""" + + event_type: str + """Event type (e.g. push, pull_request).""" + + origin: Literal["EXTERNAL_EVENT", "AGENT_EVENT", "USER_EVENT", "SYSTEM_EVENT"] + """Event origin.""" + + payload: str + """JSON-encoded event payload.""" + + sequence: int + """Monotonic sequence number.""" + + source: str + """Event source (e.g. github, slack).""" + + timestamp_ms: int + """Timestamp in milliseconds since epoch.""" diff --git a/src/runloop_api_client/types/axon_list_view.py b/src/runloop_api_client/types/axon_list_view.py new file mode 100644 index 000000000..bd6c4d31c --- /dev/null +++ b/src/runloop_api_client/types/axon_list_view.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List + +from .._models import BaseModel +from .axon_view import AxonView + +__all__ = ["AxonListView"] + + +class AxonListView(BaseModel): + axons: List[AxonView] + """List of active axons.""" diff --git a/src/runloop_api_client/types/axon_publish_params.py b/src/runloop_api_client/types/axon_publish_params.py new file mode 100644 index 000000000..98fcd91bf --- /dev/null +++ b/src/runloop_api_client/types/axon_publish_params.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["AxonPublishParams"] + + +class AxonPublishParams(TypedDict, total=False): + event_type: Required[str] + """The event type (e.g. push, pull_request).""" + + origin: Required[Literal["EXTERNAL_EVENT", "AGENT_EVENT", "USER_EVENT"]] + """Event origin.""" + + payload: Required[str] + """Event payload.""" + + source: Required[str] + """The source of the event (e.g. github, slack).""" diff --git a/src/runloop_api_client/types/axon_view.py b/src/runloop_api_client/types/axon_view.py new file mode 100644 index 000000000..61d1bd243 --- /dev/null +++ b/src/runloop_api_client/types/axon_view.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from .._models import BaseModel + +__all__ = ["AxonView"] + + +class AxonView(BaseModel): + id: str + """The axon identifier.""" + + created_at_ms: int + """Creation time in milliseconds since epoch.""" + + name: Optional[str] = None + """The name of the axon.""" diff --git a/src/runloop_api_client/types/benchmark_job_list_params.py b/src/runloop_api_client/types/benchmark_job_list_params.py index c0db8843c..9ab17e8eb 100644 --- a/src/runloop_api_client/types/benchmark_job_list_params.py +++ b/src/runloop_api_client/types/benchmark_job_list_params.py @@ -8,6 +8,12 @@ class BenchmarkJobListParams(TypedDict, total=False): + include_total_count: bool + """If true (default), includes total_count in the response. + + Set to false to skip the count query for better performance on large datasets. + """ + limit: int """The limit of items to return. Default is 20. Max is 5000.""" diff --git a/src/runloop_api_client/types/benchmark_job_list_view.py b/src/runloop_api_client/types/benchmark_job_list_view.py index f0e1da7d0..f4f2458c3 100644 --- a/src/runloop_api_client/types/benchmark_job_list_view.py +++ b/src/runloop_api_client/types/benchmark_job_list_view.py @@ -14,6 +14,4 @@ class BenchmarkJobListView(BaseModel): jobs: List[BenchmarkJobView] """List of BenchmarkJobs matching filter.""" - remaining_count: Optional[int] = None - total_count: Optional[int] = None diff --git a/src/runloop_api_client/types/benchmark_list_params.py b/src/runloop_api_client/types/benchmark_list_params.py index 4e8b0c78b..d8be9aeca 100644 --- a/src/runloop_api_client/types/benchmark_list_params.py +++ b/src/runloop_api_client/types/benchmark_list_params.py @@ -8,6 +8,12 @@ class BenchmarkListParams(TypedDict, total=False): + include_total_count: bool + """If true (default), includes total_count in the response. + + Set to false to skip the count query for better performance on large datasets. + """ + limit: int """The limit of items to return. Default is 20. Max is 5000.""" diff --git a/src/runloop_api_client/types/benchmark_list_public_params.py b/src/runloop_api_client/types/benchmark_list_public_params.py index 6dec4283b..9be7ffdf4 100644 --- a/src/runloop_api_client/types/benchmark_list_public_params.py +++ b/src/runloop_api_client/types/benchmark_list_public_params.py @@ -8,6 +8,12 @@ class BenchmarkListPublicParams(TypedDict, total=False): + include_total_count: bool + """If true (default), includes total_count in the response. + + Set to false to skip the count query for better performance on large datasets. + """ + limit: int """The limit of items to return. Default is 20. Max is 5000.""" diff --git a/src/runloop_api_client/types/benchmark_run_list_params.py b/src/runloop_api_client/types/benchmark_run_list_params.py index 28d4e2e87..4f4c8a550 100644 --- a/src/runloop_api_client/types/benchmark_run_list_params.py +++ b/src/runloop_api_client/types/benchmark_run_list_params.py @@ -11,6 +11,12 @@ class BenchmarkRunListParams(TypedDict, total=False): benchmark_id: str """The Benchmark ID to filter by.""" + include_total_count: bool + """If true (default), includes total_count in the response. + + Set to false to skip the count query for better performance on large datasets. + """ + limit: int """The limit of items to return. Default is 20. Max is 5000.""" diff --git a/src/runloop_api_client/types/benchmark_run_list_scenario_runs_params.py b/src/runloop_api_client/types/benchmark_run_list_scenario_runs_params.py index c88c09167..dddfa256a 100644 --- a/src/runloop_api_client/types/benchmark_run_list_scenario_runs_params.py +++ b/src/runloop_api_client/types/benchmark_run_list_scenario_runs_params.py @@ -8,6 +8,12 @@ class BenchmarkRunListScenarioRunsParams(TypedDict, total=False): + include_total_count: bool + """If true (default), includes total_count in the response. + + Set to false to skip the count query for better performance on large datasets. + """ + limit: int """The limit of items to return. Default is 20. Max is 5000.""" diff --git a/src/runloop_api_client/types/benchmark_run_list_view.py b/src/runloop_api_client/types/benchmark_run_list_view.py index e85506bab..efee2176d 100644 --- a/src/runloop_api_client/types/benchmark_run_list_view.py +++ b/src/runloop_api_client/types/benchmark_run_list_view.py @@ -14,6 +14,4 @@ class BenchmarkRunListView(BaseModel): runs: List[BenchmarkRunView] """List of BenchmarkRuns matching filter.""" - remaining_count: Optional[int] = None - total_count: Optional[int] = None diff --git a/src/runloop_api_client/types/blueprint_list_params.py b/src/runloop_api_client/types/blueprint_list_params.py index f72de7d2f..c61df2640 100644 --- a/src/runloop_api_client/types/blueprint_list_params.py +++ b/src/runloop_api_client/types/blueprint_list_params.py @@ -8,6 +8,12 @@ class BlueprintListParams(TypedDict, total=False): + include_total_count: bool + """If true (default), includes total_count in the response. + + Set to false to skip the count query for better performance on large datasets. + """ + limit: int """The limit of items to return. Default is 20. Max is 5000.""" diff --git a/src/runloop_api_client/types/blueprint_list_public_params.py b/src/runloop_api_client/types/blueprint_list_public_params.py index e0f224f32..a6b66dc9b 100644 --- a/src/runloop_api_client/types/blueprint_list_public_params.py +++ b/src/runloop_api_client/types/blueprint_list_public_params.py @@ -8,6 +8,12 @@ class BlueprintListPublicParams(TypedDict, total=False): + include_total_count: bool + """If true (default), includes total_count in the response. + + Set to false to skip the count query for better performance on large datasets. + """ + limit: int """The limit of items to return. Default is 20. Max is 5000.""" diff --git a/src/runloop_api_client/types/blueprint_list_view.py b/src/runloop_api_client/types/blueprint_list_view.py index 7d97314f5..e5eee905b 100644 --- a/src/runloop_api_client/types/blueprint_list_view.py +++ b/src/runloop_api_client/types/blueprint_list_view.py @@ -14,6 +14,4 @@ class BlueprintListView(BaseModel): has_more: bool - remaining_count: Optional[int] = None - total_count: Optional[int] = None diff --git a/src/runloop_api_client/types/devbox_list_disk_snapshots_params.py b/src/runloop_api_client/types/devbox_list_disk_snapshots_params.py index d26c3fbd8..48e0fc9e3 100644 --- a/src/runloop_api_client/types/devbox_list_disk_snapshots_params.py +++ b/src/runloop_api_client/types/devbox_list_disk_snapshots_params.py @@ -13,6 +13,12 @@ class DevboxListDiskSnapshotsParams(TypedDict, total=False): devbox_id: str """Devbox ID to filter by.""" + include_total_count: bool + """If true (default), includes total_count in the response. + + Set to false to skip the count query for better performance on large datasets. + """ + limit: int """The limit of items to return. Default is 20. Max is 5000.""" diff --git a/src/runloop_api_client/types/devbox_list_params.py b/src/runloop_api_client/types/devbox_list_params.py index c508762da..d8bae28d7 100644 --- a/src/runloop_api_client/types/devbox_list_params.py +++ b/src/runloop_api_client/types/devbox_list_params.py @@ -8,6 +8,12 @@ class DevboxListParams(TypedDict, total=False): + include_total_count: bool + """If true (default), includes total_count in the response. + + Set to false to skip the count query for better performance on large datasets. + """ + limit: int """The limit of items to return. Default is 20. Max is 5000.""" diff --git a/src/runloop_api_client/types/devbox_list_view.py b/src/runloop_api_client/types/devbox_list_view.py index 6bd522ee5..6380f9326 100644 --- a/src/runloop_api_client/types/devbox_list_view.py +++ b/src/runloop_api_client/types/devbox_list_view.py @@ -14,6 +14,4 @@ class DevboxListView(BaseModel): has_more: bool - remaining_count: Optional[int] = None - total_count: Optional[int] = None diff --git a/src/runloop_api_client/types/devbox_remove_tunnel_params.py b/src/runloop_api_client/types/devbox_remove_tunnel_params.py deleted file mode 100644 index eb58e2702..000000000 --- a/src/runloop_api_client/types/devbox_remove_tunnel_params.py +++ /dev/null @@ -1,12 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Required, TypedDict - -__all__ = ["DevboxRemoveTunnelParams"] - - -class DevboxRemoveTunnelParams(TypedDict, total=False): - port: Required[int] - """Devbox port that tunnel will expose.""" diff --git a/src/runloop_api_client/types/devbox_snapshot_list_view.py b/src/runloop_api_client/types/devbox_snapshot_list_view.py index ca54f2eb1..0fdfd0594 100644 --- a/src/runloop_api_client/types/devbox_snapshot_list_view.py +++ b/src/runloop_api_client/types/devbox_snapshot_list_view.py @@ -14,6 +14,4 @@ class DevboxSnapshotListView(BaseModel): snapshots: List[DevboxSnapshotView] """List of snapshots matching filter.""" - remaining_count: Optional[int] = None - total_count: Optional[int] = None diff --git a/src/runloop_api_client/types/devboxes/disk_snapshot_list_params.py b/src/runloop_api_client/types/devboxes/disk_snapshot_list_params.py index 73e60f457..7a00118e4 100644 --- a/src/runloop_api_client/types/devboxes/disk_snapshot_list_params.py +++ b/src/runloop_api_client/types/devboxes/disk_snapshot_list_params.py @@ -13,6 +13,12 @@ class DiskSnapshotListParams(TypedDict, total=False): devbox_id: str """Devbox ID to filter by.""" + include_total_count: bool + """If true (default), includes total_count in the response. + + Set to false to skip the count query for better performance on large datasets. + """ + limit: int """The limit of items to return. Default is 20. Max is 5000.""" diff --git a/src/runloop_api_client/types/gateway_config_list_params.py b/src/runloop_api_client/types/gateway_config_list_params.py index cc8706b95..ef69f83b4 100644 --- a/src/runloop_api_client/types/gateway_config_list_params.py +++ b/src/runloop_api_client/types/gateway_config_list_params.py @@ -11,6 +11,12 @@ class GatewayConfigListParams(TypedDict, total=False): id: str """Filter by ID.""" + include_total_count: bool + """If true (default), includes total_count in the response. + + Set to false to skip the count query for better performance on large datasets. + """ + limit: int """The limit of items to return. Default is 20. Max is 5000.""" diff --git a/src/runloop_api_client/types/gateway_config_list_view.py b/src/runloop_api_client/types/gateway_config_list_view.py index 05511971f..745e6376b 100644 --- a/src/runloop_api_client/types/gateway_config_list_view.py +++ b/src/runloop_api_client/types/gateway_config_list_view.py @@ -18,7 +18,4 @@ class GatewayConfigListView(BaseModel): """Whether there are more results available beyond this page.""" total_count: Optional[int] = None - """Total count of GatewayConfigs that match the query. - - Deprecated: will be removed in a future breaking change. - """ + """Total count of GatewayConfigs that match the query.""" diff --git a/src/runloop_api_client/types/mcp_config_list_params.py b/src/runloop_api_client/types/mcp_config_list_params.py index 8b786ba13..684de89ec 100644 --- a/src/runloop_api_client/types/mcp_config_list_params.py +++ b/src/runloop_api_client/types/mcp_config_list_params.py @@ -11,6 +11,12 @@ class McpConfigListParams(TypedDict, total=False): id: str """Filter by ID.""" + include_total_count: bool + """If true (default), includes total_count in the response. + + Set to false to skip the count query for better performance on large datasets. + """ + limit: int """The limit of items to return. Default is 20. Max is 5000.""" diff --git a/src/runloop_api_client/types/mcp_config_list_view.py b/src/runloop_api_client/types/mcp_config_list_view.py index 4992698c5..ed742162d 100644 --- a/src/runloop_api_client/types/mcp_config_list_view.py +++ b/src/runloop_api_client/types/mcp_config_list_view.py @@ -18,7 +18,4 @@ class McpConfigListView(BaseModel): """The list of McpConfigs.""" total_count: Optional[int] = None - """Total count of McpConfigs that match the query. - - Deprecated: will be removed in a future breaking change. - """ + """Total count of McpConfigs that match the query.""" diff --git a/src/runloop_api_client/types/network_policy_list_params.py b/src/runloop_api_client/types/network_policy_list_params.py index 160da795f..cdbc84f1a 100644 --- a/src/runloop_api_client/types/network_policy_list_params.py +++ b/src/runloop_api_client/types/network_policy_list_params.py @@ -11,6 +11,12 @@ class NetworkPolicyListParams(TypedDict, total=False): id: str """Filter by ID.""" + include_total_count: bool + """If true (default), includes total_count in the response. + + Set to false to skip the count query for better performance on large datasets. + """ + limit: int """The limit of items to return. Default is 20. Max is 5000.""" diff --git a/src/runloop_api_client/types/network_policy_list_view.py b/src/runloop_api_client/types/network_policy_list_view.py index ba031d0a9..2530a3699 100644 --- a/src/runloop_api_client/types/network_policy_list_view.py +++ b/src/runloop_api_client/types/network_policy_list_view.py @@ -18,7 +18,4 @@ class NetworkPolicyListView(BaseModel): """The list of NetworkPolicies.""" total_count: Optional[int] = None - """Total count of items in this response. - - Deprecated: will be removed in a future breaking change. - """ + """Total count of items in this response.""" diff --git a/src/runloop_api_client/types/object_list_params.py b/src/runloop_api_client/types/object_list_params.py index eca1c7cdd..5ec2e88bc 100644 --- a/src/runloop_api_client/types/object_list_params.py +++ b/src/runloop_api_client/types/object_list_params.py @@ -11,6 +11,12 @@ class ObjectListParams(TypedDict, total=False): content_type: Literal["unspecified", "text", "binary", "gzip", "tar", "tgz"] """Filter storage objects by content type.""" + include_total_count: bool + """If true (default), includes total_count in the response. + + Set to false to skip the count query for better performance on large datasets. + """ + limit: int """The limit of items to return. Default is 20. Max is 5000.""" diff --git a/src/runloop_api_client/types/object_list_public_params.py b/src/runloop_api_client/types/object_list_public_params.py index 67475b263..a0b53dcd6 100644 --- a/src/runloop_api_client/types/object_list_public_params.py +++ b/src/runloop_api_client/types/object_list_public_params.py @@ -11,6 +11,12 @@ class ObjectListPublicParams(TypedDict, total=False): content_type: Literal["unspecified", "text", "binary", "gzip", "tar", "tgz"] """Filter storage objects by content type.""" + include_total_count: bool + """If true (default), includes total_count in the response. + + Set to false to skip the count query for better performance on large datasets. + """ + limit: int """The limit of items to return. Default is 20. Max is 5000.""" diff --git a/src/runloop_api_client/types/object_list_view.py b/src/runloop_api_client/types/object_list_view.py index 689c0899b..4b2658c1f 100644 --- a/src/runloop_api_client/types/object_list_view.py +++ b/src/runloop_api_client/types/object_list_view.py @@ -17,14 +17,5 @@ class ObjectListView(BaseModel): objects: List[ObjectView] """List of Object entities.""" - remaining_count: Optional[int] = None - """Number of Objects remaining after this page. - - Deprecated: will be removed in a future breaking change. - """ - total_count: Optional[int] = None - """Total number of Objects across all pages. - - Deprecated: will be removed in a future breaking change. - """ + """Total number of Objects across all pages.""" diff --git a/src/runloop_api_client/types/publish_result_view.py b/src/runloop_api_client/types/publish_result_view.py new file mode 100644 index 000000000..f74e56a65 --- /dev/null +++ b/src/runloop_api_client/types/publish_result_view.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .._models import BaseModel + +__all__ = ["PublishResultView"] + + +class PublishResultView(BaseModel): + sequence: int + """Assigned sequence number.""" + + timestamp_ms: int + """Timestamp in milliseconds since epoch.""" diff --git a/src/runloop_api_client/types/repository_connection_list_view.py b/src/runloop_api_client/types/repository_connection_list_view.py index eea040bc4..3146f27fd 100644 --- a/src/runloop_api_client/types/repository_connection_list_view.py +++ b/src/runloop_api_client/types/repository_connection_list_view.py @@ -14,6 +14,4 @@ class RepositoryConnectionListView(BaseModel): repositories: List[RepositoryConnectionView] """List of repositories matching filter.""" - remaining_count: Optional[int] = None - total_count: Optional[int] = None diff --git a/src/runloop_api_client/types/repository_list_params.py b/src/runloop_api_client/types/repository_list_params.py index d5f7b248a..813842cab 100644 --- a/src/runloop_api_client/types/repository_list_params.py +++ b/src/runloop_api_client/types/repository_list_params.py @@ -8,6 +8,12 @@ class RepositoryListParams(TypedDict, total=False): + include_total_count: bool + """If true (default), includes total_count in the response. + + Set to false to skip the count query for better performance on large datasets. + """ + limit: int """The limit of items to return. Default is 20. Max is 5000.""" diff --git a/src/runloop_api_client/types/scenario_definition_list_view.py b/src/runloop_api_client/types/scenario_definition_list_view.py index f39cf1ed9..25bad1da5 100644 --- a/src/runloop_api_client/types/scenario_definition_list_view.py +++ b/src/runloop_api_client/types/scenario_definition_list_view.py @@ -14,6 +14,4 @@ class ScenarioDefinitionListView(BaseModel): scenarios: List[ScenarioView] """List of Scenarios matching filter.""" - remaining_count: Optional[int] = None - total_count: Optional[int] = None diff --git a/src/runloop_api_client/types/scenario_list_params.py b/src/runloop_api_client/types/scenario_list_params.py index 45ff3a87b..3d34d711a 100644 --- a/src/runloop_api_client/types/scenario_list_params.py +++ b/src/runloop_api_client/types/scenario_list_params.py @@ -11,6 +11,12 @@ class ScenarioListParams(TypedDict, total=False): benchmark_id: str """Filter scenarios by benchmark ID.""" + include_total_count: bool + """If true (default), includes total_count in the response. + + Set to false to skip the count query for better performance on large datasets. + """ + limit: int """The limit of items to return. Default is 20. Max is 5000.""" diff --git a/src/runloop_api_client/types/scenario_list_public_params.py b/src/runloop_api_client/types/scenario_list_public_params.py index be7e40b8d..a2e71da64 100644 --- a/src/runloop_api_client/types/scenario_list_public_params.py +++ b/src/runloop_api_client/types/scenario_list_public_params.py @@ -8,6 +8,12 @@ class ScenarioListPublicParams(TypedDict, total=False): + include_total_count: bool + """If true (default), includes total_count in the response. + + Set to false to skip the count query for better performance on large datasets. + """ + limit: int """The limit of items to return. Default is 20. Max is 5000.""" diff --git a/src/runloop_api_client/types/scenario_run_list_view.py b/src/runloop_api_client/types/scenario_run_list_view.py index 142292dda..6083dbde2 100644 --- a/src/runloop_api_client/types/scenario_run_list_view.py +++ b/src/runloop_api_client/types/scenario_run_list_view.py @@ -14,6 +14,4 @@ class ScenarioRunListView(BaseModel): runs: List[ScenarioRunView] """List of ScenarioRuns matching filter.""" - remaining_count: Optional[int] = None - total_count: Optional[int] = None diff --git a/src/runloop_api_client/types/scenarios/run_list_params.py b/src/runloop_api_client/types/scenarios/run_list_params.py index 97eeb425a..6e7373a07 100644 --- a/src/runloop_api_client/types/scenarios/run_list_params.py +++ b/src/runloop_api_client/types/scenarios/run_list_params.py @@ -11,6 +11,12 @@ class RunListParams(TypedDict, total=False): benchmark_run_id: str """Filter by benchmark run ID""" + include_total_count: bool + """If true (default), includes total_count in the response. + + Set to false to skip the count query for better performance on large datasets. + """ + limit: int """The limit of items to return. Default is 20. Max is 5000.""" diff --git a/src/runloop_api_client/types/scenarios/scorer_list_params.py b/src/runloop_api_client/types/scenarios/scorer_list_params.py index f80e7f6ac..a387f1a86 100644 --- a/src/runloop_api_client/types/scenarios/scorer_list_params.py +++ b/src/runloop_api_client/types/scenarios/scorer_list_params.py @@ -8,6 +8,12 @@ class ScorerListParams(TypedDict, total=False): + include_total_count: bool + """If true (default), includes total_count in the response. + + Set to false to skip the count query for better performance on large datasets. + """ + limit: int """The limit of items to return. Default is 20. Max is 5000.""" diff --git a/src/runloop_api_client/types/secret_list_view.py b/src/runloop_api_client/types/secret_list_view.py index 2f10bae2c..bb6b69d12 100644 --- a/src/runloop_api_client/types/secret_list_view.py +++ b/src/runloop_api_client/types/secret_list_view.py @@ -17,14 +17,5 @@ class SecretListView(BaseModel): secrets: List[SecretView] """List of Secret objects. Values are omitted for security.""" - remaining_count: Optional[int] = None - """Number of Secrets remaining after this page. - - Deprecated: will be removed in a future breaking change. - """ - total_count: Optional[int] = None - """Total number of Secrets across all pages. - - Deprecated: will be removed in a future breaking change. - """ + """Total number of Secrets across all pages.""" diff --git a/src/runloop_api_client/types/shared/launch_parameters.py b/src/runloop_api_client/types/shared/launch_parameters.py index 0264fa5c8..7c1fdc2a7 100644 --- a/src/runloop_api_client/types/shared/launch_parameters.py +++ b/src/runloop_api_client/types/shared/launch_parameters.py @@ -37,10 +37,9 @@ class LaunchParameters(BaseModel): """The target architecture for the Devbox. If unset, defaults to x86_64.""" available_ports: Optional[List[int]] = None - """A list of ports to make available on the Devbox. + """[Deprecated] A list of ports to make available on the Devbox. - Only ports made available will be surfaced to create tunnels via the - 'createTunnel' API. + This field is ignored. """ custom_cpu_cores: Optional[int] = None diff --git a/src/runloop_api_client/types/shared_params/launch_parameters.py b/src/runloop_api_client/types/shared_params/launch_parameters.py index 5c785b9f9..c30f90d86 100644 --- a/src/runloop_api_client/types/shared_params/launch_parameters.py +++ b/src/runloop_api_client/types/shared_params/launch_parameters.py @@ -39,10 +39,9 @@ class LaunchParameters(TypedDict, total=False): """The target architecture for the Devbox. If unset, defaults to x86_64.""" available_ports: Optional[Iterable[int]] - """A list of ports to make available on the Devbox. + """[Deprecated] A list of ports to make available on the Devbox. - Only ports made available will be surfaced to create tunnels via the - 'createTunnel' API. + This field is ignored. """ custom_cpu_cores: Optional[int] diff --git a/tests/api_resources/devboxes/test_disk_snapshots.py b/tests/api_resources/devboxes/test_disk_snapshots.py index 170e618ba..3d861089e 100644 --- a/tests/api_resources/devboxes/test_disk_snapshots.py +++ b/tests/api_resources/devboxes/test_disk_snapshots.py @@ -81,6 +81,7 @@ def test_method_list(self, client: Runloop) -> None: def test_method_list_with_all_params(self, client: Runloop) -> None: disk_snapshot = client.devboxes.disk_snapshots.list( devbox_id="devbox_id", + include_total_count=True, limit=0, metadata_key="metadata[key]", metadata_key_in="metadata[key][in]", @@ -354,6 +355,7 @@ async def test_method_list(self, async_client: AsyncRunloop) -> None: async def test_method_list_with_all_params(self, async_client: AsyncRunloop) -> None: disk_snapshot = await async_client.devboxes.disk_snapshots.list( devbox_id="devbox_id", + include_total_count=True, limit=0, metadata_key="metadata[key]", metadata_key_in="metadata[key][in]", diff --git a/tests/api_resources/scenarios/test_runs.py b/tests/api_resources/scenarios/test_runs.py index f3ac8eb88..429a815ee 100644 --- a/tests/api_resources/scenarios/test_runs.py +++ b/tests/api_resources/scenarios/test_runs.py @@ -73,6 +73,7 @@ def test_method_list(self, client: Runloop) -> None: def test_method_list_with_all_params(self, client: Runloop) -> None: run = client.scenarios.runs.list( benchmark_run_id="benchmark_run_id", + include_total_count=True, limit=0, name="name", scenario_id="scenario_id", @@ -324,6 +325,7 @@ async def test_method_list(self, async_client: AsyncRunloop) -> None: async def test_method_list_with_all_params(self, async_client: AsyncRunloop) -> None: run = await async_client.scenarios.runs.list( benchmark_run_id="benchmark_run_id", + include_total_count=True, limit=0, name="name", scenario_id="scenario_id", diff --git a/tests/api_resources/scenarios/test_scorers.py b/tests/api_resources/scenarios/test_scorers.py index 359e0dcc7..cd15e860d 100644 --- a/tests/api_resources/scenarios/test_scorers.py +++ b/tests/api_resources/scenarios/test_scorers.py @@ -149,6 +149,7 @@ def test_method_list(self, client: Runloop) -> None: @parametrize def test_method_list_with_all_params(self, client: Runloop) -> None: scorer = client.scenarios.scorers.list( + include_total_count=True, limit=0, starting_after="starting_after", ) @@ -306,6 +307,7 @@ async def test_method_list(self, async_client: AsyncRunloop) -> None: @parametrize async def test_method_list_with_all_params(self, async_client: AsyncRunloop) -> None: scorer = await async_client.scenarios.scorers.list( + include_total_count=True, limit=0, starting_after="starting_after", ) diff --git a/tests/api_resources/test_agents.py b/tests/api_resources/test_agents.py index 693eec250..a6304d70d 100644 --- a/tests/api_resources/test_agents.py +++ b/tests/api_resources/test_agents.py @@ -128,6 +128,7 @@ def test_method_list(self, client: Runloop) -> None: @parametrize def test_method_list_with_all_params(self, client: Runloop) -> None: agent = client.agents.list( + include_total_count=True, is_public=True, limit=0, name="name", @@ -273,6 +274,7 @@ async def test_method_list(self, async_client: AsyncRunloop) -> None: @parametrize async def test_method_list_with_all_params(self, async_client: AsyncRunloop) -> None: agent = await async_client.agents.list( + include_total_count=True, is_public=True, limit=0, name="name", diff --git a/tests/api_resources/test_axons.py b/tests/api_resources/test_axons.py new file mode 100644 index 000000000..bda5f1a23 --- /dev/null +++ b/tests/api_resources/test_axons.py @@ -0,0 +1,396 @@ +# 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 pytest + +from tests.utils import assert_matches_type +from runloop_api_client import Runloop, AsyncRunloop +from runloop_api_client.types import AxonView, AxonListView, PublishResultView + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestAxons: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_create(self, client: Runloop) -> None: + axon = client.axons.create() + assert_matches_type(AxonView, axon, path=["response"]) + + @parametrize + def test_method_create_with_all_params(self, client: Runloop) -> None: + axon = client.axons.create( + name="name", + ) + assert_matches_type(AxonView, axon, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: Runloop) -> None: + response = client.axons.with_raw_response.create() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + axon = response.parse() + assert_matches_type(AxonView, axon, path=["response"]) + + @parametrize + def test_streaming_response_create(self, client: Runloop) -> None: + with client.axons.with_streaming_response.create() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + axon = response.parse() + assert_matches_type(AxonView, axon, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_retrieve(self, client: Runloop) -> None: + axon = client.axons.retrieve( + "id", + ) + assert_matches_type(AxonView, axon, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: Runloop) -> None: + response = client.axons.with_raw_response.retrieve( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + axon = response.parse() + assert_matches_type(AxonView, axon, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: Runloop) -> None: + with client.axons.with_streaming_response.retrieve( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + axon = response.parse() + assert_matches_type(AxonView, axon, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_retrieve(self, client: Runloop) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.axons.with_raw_response.retrieve( + "", + ) + + @parametrize + def test_method_list(self, client: Runloop) -> None: + axon = client.axons.list() + assert_matches_type(AxonListView, axon, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: Runloop) -> None: + response = client.axons.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + axon = response.parse() + assert_matches_type(AxonListView, axon, path=["response"]) + + @parametrize + def test_streaming_response_list(self, client: Runloop) -> None: + with client.axons.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + axon = response.parse() + assert_matches_type(AxonListView, axon, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_publish(self, client: Runloop) -> None: + axon = client.axons.publish( + id="id", + event_type="event_type", + origin="EXTERNAL_EVENT", + payload="payload", + source="source", + ) + assert_matches_type(PublishResultView, axon, path=["response"]) + + @parametrize + def test_raw_response_publish(self, client: Runloop) -> None: + response = client.axons.with_raw_response.publish( + id="id", + event_type="event_type", + origin="EXTERNAL_EVENT", + payload="payload", + source="source", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + axon = response.parse() + assert_matches_type(PublishResultView, axon, path=["response"]) + + @parametrize + def test_streaming_response_publish(self, client: Runloop) -> None: + with client.axons.with_streaming_response.publish( + id="id", + event_type="event_type", + origin="EXTERNAL_EVENT", + payload="payload", + source="source", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + axon = response.parse() + assert_matches_type(PublishResultView, axon, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_publish(self, client: Runloop) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.axons.with_raw_response.publish( + id="", + event_type="event_type", + origin="EXTERNAL_EVENT", + payload="payload", + source="source", + ) + + @parametrize + def test_method_subscribe_sse(self, client: Runloop) -> None: + axon_stream = client.axons.subscribe_sse( + "id", + ) + axon_stream.response.close() + + @parametrize + def test_raw_response_subscribe_sse(self, client: Runloop) -> None: + response = client.axons.with_raw_response.subscribe_sse( + "id", + ) + + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + stream = response.parse() + stream.close() + + @parametrize + def test_streaming_response_subscribe_sse(self, client: Runloop) -> None: + with client.axons.with_streaming_response.subscribe_sse( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + stream = response.parse() + stream.close() + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_subscribe_sse(self, client: Runloop) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.axons.with_raw_response.subscribe_sse( + "", + ) + + +class TestAsyncAxons: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_create(self, async_client: AsyncRunloop) -> None: + axon = await async_client.axons.create() + assert_matches_type(AxonView, axon, path=["response"]) + + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncRunloop) -> None: + axon = await async_client.axons.create( + name="name", + ) + assert_matches_type(AxonView, axon, path=["response"]) + + @parametrize + async def test_raw_response_create(self, async_client: AsyncRunloop) -> None: + response = await async_client.axons.with_raw_response.create() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + axon = await response.parse() + assert_matches_type(AxonView, axon, path=["response"]) + + @parametrize + async def test_streaming_response_create(self, async_client: AsyncRunloop) -> None: + async with async_client.axons.with_streaming_response.create() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + axon = await response.parse() + assert_matches_type(AxonView, axon, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_retrieve(self, async_client: AsyncRunloop) -> None: + axon = await async_client.axons.retrieve( + "id", + ) + assert_matches_type(AxonView, axon, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncRunloop) -> None: + response = await async_client.axons.with_raw_response.retrieve( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + axon = await response.parse() + assert_matches_type(AxonView, axon, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncRunloop) -> None: + async with async_client.axons.with_streaming_response.retrieve( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + axon = await response.parse() + assert_matches_type(AxonView, axon, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncRunloop) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.axons.with_raw_response.retrieve( + "", + ) + + @parametrize + async def test_method_list(self, async_client: AsyncRunloop) -> None: + axon = await async_client.axons.list() + assert_matches_type(AxonListView, axon, path=["response"]) + + @parametrize + async def test_raw_response_list(self, async_client: AsyncRunloop) -> None: + response = await async_client.axons.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + axon = await response.parse() + assert_matches_type(AxonListView, axon, path=["response"]) + + @parametrize + async def test_streaming_response_list(self, async_client: AsyncRunloop) -> None: + async with async_client.axons.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + axon = await response.parse() + assert_matches_type(AxonListView, axon, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_publish(self, async_client: AsyncRunloop) -> None: + axon = await async_client.axons.publish( + id="id", + event_type="event_type", + origin="EXTERNAL_EVENT", + payload="payload", + source="source", + ) + assert_matches_type(PublishResultView, axon, path=["response"]) + + @parametrize + async def test_raw_response_publish(self, async_client: AsyncRunloop) -> None: + response = await async_client.axons.with_raw_response.publish( + id="id", + event_type="event_type", + origin="EXTERNAL_EVENT", + payload="payload", + source="source", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + axon = await response.parse() + assert_matches_type(PublishResultView, axon, path=["response"]) + + @parametrize + async def test_streaming_response_publish(self, async_client: AsyncRunloop) -> None: + async with async_client.axons.with_streaming_response.publish( + id="id", + event_type="event_type", + origin="EXTERNAL_EVENT", + payload="payload", + source="source", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + axon = await response.parse() + assert_matches_type(PublishResultView, axon, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_publish(self, async_client: AsyncRunloop) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.axons.with_raw_response.publish( + id="", + event_type="event_type", + origin="EXTERNAL_EVENT", + payload="payload", + source="source", + ) + + @parametrize + async def test_method_subscribe_sse(self, async_client: AsyncRunloop) -> None: + axon_stream = await async_client.axons.subscribe_sse( + "id", + ) + await axon_stream.response.aclose() + + @parametrize + async def test_raw_response_subscribe_sse(self, async_client: AsyncRunloop) -> None: + response = await async_client.axons.with_raw_response.subscribe_sse( + "id", + ) + + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + stream = await response.parse() + await stream.close() + + @parametrize + async def test_streaming_response_subscribe_sse(self, async_client: AsyncRunloop) -> None: + async with async_client.axons.with_streaming_response.subscribe_sse( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + stream = await response.parse() + await stream.close() + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_subscribe_sse(self, async_client: AsyncRunloop) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.axons.with_raw_response.subscribe_sse( + "", + ) diff --git a/tests/api_resources/test_benchmark_jobs.py b/tests/api_resources/test_benchmark_jobs.py index 461943458..9b577a0c2 100644 --- a/tests/api_resources/test_benchmark_jobs.py +++ b/tests/api_resources/test_benchmark_jobs.py @@ -102,6 +102,7 @@ def test_method_list(self, client: Runloop) -> None: @parametrize def test_method_list_with_all_params(self, client: Runloop) -> None: benchmark_job = client.benchmark_jobs.list( + include_total_count=True, limit=0, name="name", starting_after="starting_after", @@ -216,6 +217,7 @@ async def test_method_list(self, async_client: AsyncRunloop) -> None: @parametrize async def test_method_list_with_all_params(self, async_client: AsyncRunloop) -> None: benchmark_job = await async_client.benchmark_jobs.list( + include_total_count=True, limit=0, name="name", starting_after="starting_after", diff --git a/tests/api_resources/test_benchmark_runs.py b/tests/api_resources/test_benchmark_runs.py index 854a44574..5e8709aff 100644 --- a/tests/api_resources/test_benchmark_runs.py +++ b/tests/api_resources/test_benchmark_runs.py @@ -68,6 +68,7 @@ def test_method_list(self, client: Runloop) -> None: def test_method_list_with_all_params(self, client: Runloop) -> None: benchmark_run = client.benchmark_runs.list( benchmark_id="benchmark_id", + include_total_count=True, limit=0, name="name", starting_after="starting_after", @@ -181,6 +182,7 @@ def test_method_list_scenario_runs(self, client: Runloop) -> None: def test_method_list_scenario_runs_with_all_params(self, client: Runloop) -> None: benchmark_run = client.benchmark_runs.list_scenario_runs( id="id", + include_total_count=True, limit=0, starting_after="starting_after", state="running", @@ -271,6 +273,7 @@ async def test_method_list(self, async_client: AsyncRunloop) -> None: async def test_method_list_with_all_params(self, async_client: AsyncRunloop) -> None: benchmark_run = await async_client.benchmark_runs.list( benchmark_id="benchmark_id", + include_total_count=True, limit=0, name="name", starting_after="starting_after", @@ -384,6 +387,7 @@ async def test_method_list_scenario_runs(self, async_client: AsyncRunloop) -> No async def test_method_list_scenario_runs_with_all_params(self, async_client: AsyncRunloop) -> None: benchmark_run = await async_client.benchmark_runs.list_scenario_runs( id="id", + include_total_count=True, limit=0, starting_after="starting_after", state="running", diff --git a/tests/api_resources/test_benchmarks.py b/tests/api_resources/test_benchmarks.py index 803c75c60..2e1909775 100644 --- a/tests/api_resources/test_benchmarks.py +++ b/tests/api_resources/test_benchmarks.py @@ -164,6 +164,7 @@ def test_method_list(self, client: Runloop) -> None: @parametrize def test_method_list_with_all_params(self, client: Runloop) -> None: benchmark = client.benchmarks.list( + include_total_count=True, limit=0, name="name", starting_after="starting_after", @@ -245,6 +246,7 @@ def test_method_list_public(self, client: Runloop) -> None: @parametrize def test_method_list_public_with_all_params(self, client: Runloop) -> None: benchmark = client.benchmarks.list_public( + include_total_count=True, limit=0, starting_after="starting_after", ) @@ -537,6 +539,7 @@ async def test_method_list(self, async_client: AsyncRunloop) -> None: @parametrize async def test_method_list_with_all_params(self, async_client: AsyncRunloop) -> None: benchmark = await async_client.benchmarks.list( + include_total_count=True, limit=0, name="name", starting_after="starting_after", @@ -618,6 +621,7 @@ async def test_method_list_public(self, async_client: AsyncRunloop) -> None: @parametrize async def test_method_list_public_with_all_params(self, async_client: AsyncRunloop) -> None: benchmark = await async_client.benchmarks.list_public( + include_total_count=True, limit=0, starting_after="starting_after", ) diff --git a/tests/api_resources/test_blueprints.py b/tests/api_resources/test_blueprints.py index d325ea2db..7ae5fada4 100644 --- a/tests/api_resources/test_blueprints.py +++ b/tests/api_resources/test_blueprints.py @@ -182,6 +182,7 @@ def test_method_list(self, client: Runloop) -> None: @parametrize def test_method_list_with_all_params(self, client: Runloop) -> None: blueprint = client.blueprints.list( + include_total_count=True, limit=0, name="name", starting_after="starting_after", @@ -325,6 +326,7 @@ def test_method_list_public(self, client: Runloop) -> None: @parametrize def test_method_list_public_with_all_params(self, client: Runloop) -> None: blueprint = client.blueprints.list_public( + include_total_count=True, limit=0, name="name", starting_after="starting_after", @@ -648,6 +650,7 @@ async def test_method_list(self, async_client: AsyncRunloop) -> None: @parametrize async def test_method_list_with_all_params(self, async_client: AsyncRunloop) -> None: blueprint = await async_client.blueprints.list( + include_total_count=True, limit=0, name="name", starting_after="starting_after", @@ -791,6 +794,7 @@ async def test_method_list_public(self, async_client: AsyncRunloop) -> None: @parametrize async def test_method_list_public_with_all_params(self, async_client: AsyncRunloop) -> None: blueprint = await async_client.blueprints.list_public( + include_total_count=True, limit=0, name="name", starting_after="starting_after", diff --git a/tests/api_resources/test_devboxes.py b/tests/api_resources/test_devboxes.py index b7eaeb2f0..ab78cd157 100644 --- a/tests/api_resources/test_devboxes.py +++ b/tests/api_resources/test_devboxes.py @@ -230,6 +230,7 @@ def test_method_list(self, client: Runloop) -> None: @parametrize def test_method_list_with_all_params(self, client: Runloop) -> None: devbox = client.devboxes.list( + include_total_count=True, limit=0, starting_after="starting_after", status="provisioning", @@ -653,6 +654,7 @@ def test_method_list_disk_snapshots(self, client: Runloop) -> None: def test_method_list_disk_snapshots_with_all_params(self, client: Runloop) -> None: devbox = client.devboxes.list_disk_snapshots( devbox_id="devbox_id", + include_total_count=True, limit=0, metadata_key="metadata[key]", metadata_key_in="metadata[key][in]", @@ -725,21 +727,16 @@ def test_path_params_read_file_contents(self, client: Runloop) -> None: @parametrize def test_method_remove_tunnel(self, client: Runloop) -> None: - with pytest.warns(DeprecationWarning): - devbox = client.devboxes.remove_tunnel( - id="id", - port=0, - ) - + devbox = client.devboxes.remove_tunnel( + "id", + ) assert_matches_type(object, devbox, path=["response"]) @parametrize def test_raw_response_remove_tunnel(self, client: Runloop) -> None: - with pytest.warns(DeprecationWarning): - response = client.devboxes.with_raw_response.remove_tunnel( - id="id", - port=0, - ) + response = client.devboxes.with_raw_response.remove_tunnel( + "id", + ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -748,27 +745,23 @@ def test_raw_response_remove_tunnel(self, client: Runloop) -> None: @parametrize def test_streaming_response_remove_tunnel(self, client: Runloop) -> None: - with pytest.warns(DeprecationWarning): - with client.devboxes.with_streaming_response.remove_tunnel( - id="id", - port=0, - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" + with client.devboxes.with_streaming_response.remove_tunnel( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" - devbox = response.parse() - assert_matches_type(object, devbox, path=["response"]) + devbox = response.parse() + assert_matches_type(object, devbox, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_remove_tunnel(self, client: Runloop) -> None: - with pytest.warns(DeprecationWarning): - with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - client.devboxes.with_raw_response.remove_tunnel( - id="", - port=0, - ) + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.devboxes.with_raw_response.remove_tunnel( + "", + ) @parametrize def test_method_resume(self, client: Runloop) -> None: @@ -1851,6 +1844,7 @@ async def test_method_list(self, async_client: AsyncRunloop) -> None: @parametrize async def test_method_list_with_all_params(self, async_client: AsyncRunloop) -> None: devbox = await async_client.devboxes.list( + include_total_count=True, limit=0, starting_after="starting_after", status="provisioning", @@ -2274,6 +2268,7 @@ async def test_method_list_disk_snapshots(self, async_client: AsyncRunloop) -> N async def test_method_list_disk_snapshots_with_all_params(self, async_client: AsyncRunloop) -> None: devbox = await async_client.devboxes.list_disk_snapshots( devbox_id="devbox_id", + include_total_count=True, limit=0, metadata_key="metadata[key]", metadata_key_in="metadata[key][in]", @@ -2346,21 +2341,16 @@ async def test_path_params_read_file_contents(self, async_client: AsyncRunloop) @parametrize async def test_method_remove_tunnel(self, async_client: AsyncRunloop) -> None: - with pytest.warns(DeprecationWarning): - devbox = await async_client.devboxes.remove_tunnel( - id="id", - port=0, - ) - + devbox = await async_client.devboxes.remove_tunnel( + "id", + ) assert_matches_type(object, devbox, path=["response"]) @parametrize async def test_raw_response_remove_tunnel(self, async_client: AsyncRunloop) -> None: - with pytest.warns(DeprecationWarning): - response = await async_client.devboxes.with_raw_response.remove_tunnel( - id="id", - port=0, - ) + response = await async_client.devboxes.with_raw_response.remove_tunnel( + "id", + ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -2369,27 +2359,23 @@ async def test_raw_response_remove_tunnel(self, async_client: AsyncRunloop) -> N @parametrize async def test_streaming_response_remove_tunnel(self, async_client: AsyncRunloop) -> None: - with pytest.warns(DeprecationWarning): - async with async_client.devboxes.with_streaming_response.remove_tunnel( - id="id", - port=0, - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" + async with async_client.devboxes.with_streaming_response.remove_tunnel( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" - devbox = await response.parse() - assert_matches_type(object, devbox, path=["response"]) + devbox = await response.parse() + assert_matches_type(object, devbox, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_remove_tunnel(self, async_client: AsyncRunloop) -> None: - with pytest.warns(DeprecationWarning): - with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - await async_client.devboxes.with_raw_response.remove_tunnel( - id="", - port=0, - ) + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.devboxes.with_raw_response.remove_tunnel( + "", + ) @parametrize async def test_method_resume(self, async_client: AsyncRunloop) -> None: diff --git a/tests/api_resources/test_gateway_configs.py b/tests/api_resources/test_gateway_configs.py index 6265b9875..5a60422dd 100644 --- a/tests/api_resources/test_gateway_configs.py +++ b/tests/api_resources/test_gateway_configs.py @@ -169,6 +169,7 @@ def test_method_list(self, client: Runloop) -> None: def test_method_list_with_all_params(self, client: Runloop) -> None: gateway_config = client.gateway_configs.list( id="id", + include_total_count=True, limit=0, name="name", starting_after="starting_after", @@ -388,6 +389,7 @@ async def test_method_list(self, async_client: AsyncRunloop) -> None: async def test_method_list_with_all_params(self, async_client: AsyncRunloop) -> None: gateway_config = await async_client.gateway_configs.list( id="id", + include_total_count=True, limit=0, name="name", starting_after="starting_after", diff --git a/tests/api_resources/test_mcp_configs.py b/tests/api_resources/test_mcp_configs.py index 0d3135668..ea6ed337c 100644 --- a/tests/api_resources/test_mcp_configs.py +++ b/tests/api_resources/test_mcp_configs.py @@ -163,6 +163,7 @@ def test_method_list(self, client: Runloop) -> None: def test_method_list_with_all_params(self, client: Runloop) -> None: mcp_config = client.mcp_configs.list( id="id", + include_total_count=True, limit=0, name="name", starting_after="starting_after", @@ -376,6 +377,7 @@ async def test_method_list(self, async_client: AsyncRunloop) -> None: async def test_method_list_with_all_params(self, async_client: AsyncRunloop) -> None: mcp_config = await async_client.mcp_configs.list( id="id", + include_total_count=True, limit=0, name="name", starting_after="starting_after", diff --git a/tests/api_resources/test_network_policies.py b/tests/api_resources/test_network_policies.py index 6a1587cb9..e7b5fb038 100644 --- a/tests/api_resources/test_network_policies.py +++ b/tests/api_resources/test_network_policies.py @@ -163,6 +163,7 @@ def test_method_list(self, client: Runloop) -> None: def test_method_list_with_all_params(self, client: Runloop) -> None: network_policy = client.network_policies.list( id="id", + include_total_count=True, limit=0, name="name", starting_after="starting_after", @@ -376,6 +377,7 @@ async def test_method_list(self, async_client: AsyncRunloop) -> None: async def test_method_list_with_all_params(self, async_client: AsyncRunloop) -> None: network_policy = await async_client.network_policies.list( id="id", + include_total_count=True, limit=0, name="name", starting_after="starting_after", diff --git a/tests/api_resources/test_objects.py b/tests/api_resources/test_objects.py index 2593e03bf..ca5d06651 100644 --- a/tests/api_resources/test_objects.py +++ b/tests/api_resources/test_objects.py @@ -112,6 +112,7 @@ def test_method_list(self, client: Runloop) -> None: def test_method_list_with_all_params(self, client: Runloop) -> None: object_ = client.objects.list( content_type="unspecified", + include_total_count=True, limit=0, name="name", search="search", @@ -271,6 +272,7 @@ def test_method_list_public(self, client: Runloop) -> None: def test_method_list_public_with_all_params(self, client: Runloop) -> None: object_ = client.objects.list_public( content_type="unspecified", + include_total_count=True, limit=0, name="name", search="search", @@ -396,6 +398,7 @@ async def test_method_list(self, async_client: AsyncRunloop) -> None: async def test_method_list_with_all_params(self, async_client: AsyncRunloop) -> None: object_ = await async_client.objects.list( content_type="unspecified", + include_total_count=True, limit=0, name="name", search="search", @@ -555,6 +558,7 @@ async def test_method_list_public(self, async_client: AsyncRunloop) -> None: async def test_method_list_public_with_all_params(self, async_client: AsyncRunloop) -> None: object_ = await async_client.objects.list_public( content_type="unspecified", + include_total_count=True, limit=0, name="name", search="search", diff --git a/tests/api_resources/test_repositories.py b/tests/api_resources/test_repositories.py index 282688d80..20fa91e6d 100644 --- a/tests/api_resources/test_repositories.py +++ b/tests/api_resources/test_repositories.py @@ -114,6 +114,7 @@ def test_method_list(self, client: Runloop) -> None: @parametrize def test_method_list_with_all_params(self, client: Runloop) -> None: repository = client.repositories.list( + include_total_count=True, limit=0, name="name", owner="owner", @@ -451,6 +452,7 @@ async def test_method_list(self, async_client: AsyncRunloop) -> None: @parametrize async def test_method_list_with_all_params(self, async_client: AsyncRunloop) -> None: repository = await async_client.repositories.list( + include_total_count=True, limit=0, name="name", owner="owner", diff --git a/tests/api_resources/test_scenarios.py b/tests/api_resources/test_scenarios.py index 736a8395c..8182687c0 100644 --- a/tests/api_resources/test_scenarios.py +++ b/tests/api_resources/test_scenarios.py @@ -292,6 +292,7 @@ def test_method_list(self, client: Runloop) -> None: def test_method_list_with_all_params(self, client: Runloop) -> None: scenario = client.scenarios.list( benchmark_id="benchmark_id", + include_total_count=True, limit=0, name="name", starting_after="starting_after", @@ -365,6 +366,7 @@ def test_method_list_public(self, client: Runloop) -> None: @parametrize def test_method_list_public_with_all_params(self, client: Runloop) -> None: scenario = client.scenarios.list_public( + include_total_count=True, limit=0, name="name", starting_after="starting_after", @@ -741,6 +743,7 @@ async def test_method_list(self, async_client: AsyncRunloop) -> None: async def test_method_list_with_all_params(self, async_client: AsyncRunloop) -> None: scenario = await async_client.scenarios.list( benchmark_id="benchmark_id", + include_total_count=True, limit=0, name="name", starting_after="starting_after", @@ -814,6 +817,7 @@ async def test_method_list_public(self, async_client: AsyncRunloop) -> None: @parametrize async def test_method_list_public_with_all_params(self, async_client: AsyncRunloop) -> None: scenario = await async_client.scenarios.list_public( + include_total_count=True, limit=0, name="name", starting_after="starting_after", diff --git a/tests/sdk/async_devbox/test_interfaces.py b/tests/sdk/async_devbox/test_interfaces.py index 7e766ea06..79376132e 100644 --- a/tests/sdk/async_devbox/test_interfaces.py +++ b/tests/sdk/async_devbox/test_interfaces.py @@ -186,7 +186,6 @@ async def test_remove_tunnel(self, mock_async_client: AsyncMock) -> None: devbox = AsyncDevbox(mock_async_client, "dbx_123") with pytest.warns(DeprecationWarning, match="remove_tunnel is deprecated"): result = await devbox.net.remove_tunnel( - port=8080, extra_headers={"X-Custom": "value"}, extra_query={"param": "value"}, extra_body={"key": "value"}, diff --git a/tests/sdk/devbox/test_interfaces.py b/tests/sdk/devbox/test_interfaces.py index 401849f28..7d0c1d567 100644 --- a/tests/sdk/devbox/test_interfaces.py +++ b/tests/sdk/devbox/test_interfaces.py @@ -287,7 +287,6 @@ def test_remove_tunnel(self, mock_client: Mock) -> None: devbox = Devbox(mock_client, "dbx_123") with pytest.warns(DeprecationWarning, match="remove_tunnel is deprecated"): result = devbox.net.remove_tunnel( - port=8080, extra_headers={"X-Custom": "value"}, extra_query={"param": "value"}, extra_body={"key": "value"}, @@ -298,7 +297,6 @@ def test_remove_tunnel(self, mock_client: Mock) -> None: assert result is not None # Verify return value is propagated mock_client.devboxes.remove_tunnel.assert_called_once_with( "dbx_123", - port=8080, extra_headers={"X-Custom": "value"}, extra_query={"param": "value"}, extra_body={"key": "value"}, diff --git a/tests/test_utils/test_path.py b/tests/test_utils/test_path.py new file mode 100644 index 000000000..f69d655a1 --- /dev/null +++ b/tests/test_utils/test_path.py @@ -0,0 +1,89 @@ +from __future__ import annotations + +from typing import Any + +import pytest + +from runloop_api_client._utils._path import path_template + + +@pytest.mark.parametrize( + "template, kwargs, expected", + [ + ("/v1/{id}", dict(id="abc"), "/v1/abc"), + ("/v1/{a}/{b}", dict(a="x", b="y"), "/v1/x/y"), + ("/v1/{a}{b}/path/{c}?val={d}#{e}", dict(a="x", b="y", c="z", d="u", e="v"), "/v1/xy/path/z?val=u#v"), + ("/{w}/{w}", dict(w="echo"), "/echo/echo"), + ("/v1/static", {}, "/v1/static"), + ("", {}, ""), + ("/v1/?q={n}&count=10", dict(n=42), "/v1/?q=42&count=10"), + ("/v1/{v}", dict(v=None), "/v1/null"), + ("/v1/{v}", dict(v=True), "/v1/true"), + ("/v1/{v}", dict(v=False), "/v1/false"), + ("/v1/{v}", dict(v=".hidden"), "/v1/.hidden"), # dot prefix ok + ("/v1/{v}", dict(v="file.txt"), "/v1/file.txt"), # dot in middle ok + ("/v1/{v}", dict(v="..."), "/v1/..."), # triple dot ok + ("/v1/{a}{b}", dict(a=".", b="txt"), "/v1/.txt"), # dot var combining with adjacent to be ok + ("/items?q={v}#{f}", dict(v=".", f=".."), "/items?q=.#.."), # dots in query/fragment are fine + ( + "/v1/{a}?query={b}", + dict(a="../../other/endpoint", b="a&bad=true"), + "/v1/..%2F..%2Fother%2Fendpoint?query=a%26bad%3Dtrue", + ), + ("/v1/{val}", dict(val="a/b/c"), "/v1/a%2Fb%2Fc"), + ("/v1/{val}", dict(val="a/b/c?query=value"), "/v1/a%2Fb%2Fc%3Fquery=value"), + ("/v1/{val}", dict(val="a/b/c?query=value&bad=true"), "/v1/a%2Fb%2Fc%3Fquery=value&bad=true"), + ("/v1/{val}", dict(val="%20"), "/v1/%2520"), # escapes escape sequences in input + # Query: slash and ? are safe, # is not + ("/items?q={v}", dict(v="a/b"), "/items?q=a/b"), + ("/items?q={v}", dict(v="a?b"), "/items?q=a?b"), + ("/items?q={v}", dict(v="a#b"), "/items?q=a%23b"), + ("/items?q={v}", dict(v="a b"), "/items?q=a%20b"), + # Fragment: slash and ? are safe + ("/docs#{v}", dict(v="a/b"), "/docs#a/b"), + ("/docs#{v}", dict(v="a?b"), "/docs#a?b"), + # Path: slash, ? and # are all encoded + ("/v1/{v}", dict(v="a/b"), "/v1/a%2Fb"), + ("/v1/{v}", dict(v="a?b"), "/v1/a%3Fb"), + ("/v1/{v}", dict(v="a#b"), "/v1/a%23b"), + # same var encoded differently by component + ( + "/v1/{v}?q={v}#{v}", + dict(v="a/b?c#d"), + "/v1/a%2Fb%3Fc%23d?q=a/b?c%23d#a/b?c%23d", + ), + ("/v1/{val}", dict(val="x?admin=true"), "/v1/x%3Fadmin=true"), # query injection + ("/v1/{val}", dict(val="x#admin"), "/v1/x%23admin"), # fragment injection + ], +) +def test_interpolation(template: str, kwargs: dict[str, Any], expected: str) -> None: + assert path_template(template, **kwargs) == expected + + +def test_missing_kwarg_raises_key_error() -> None: + with pytest.raises(KeyError, match="org_id"): + path_template("/v1/{org_id}") + + +@pytest.mark.parametrize( + "template, kwargs", + [ + ("{a}/path", dict(a=".")), + ("{a}/path", dict(a="..")), + ("/v1/{a}", dict(a=".")), + ("/v1/{a}", dict(a="..")), + ("/v1/{a}/path", dict(a=".")), + ("/v1/{a}/path", dict(a="..")), + ("/v1/{a}{b}", dict(a=".", b=".")), # adjacent vars → ".." + ("/v1/{a}.", dict(a=".")), # var + static → ".." + ("/v1/{a}{b}", dict(a="", b=".")), # empty + dot → "." + ("/v1/%2e/{x}", dict(x="ok")), # encoded dot in static text + ("/v1/%2e./{x}", dict(x="ok")), # mixed encoded ".." in static + ("/v1/.%2E/{x}", dict(x="ok")), # mixed encoded ".." in static + ("/v1/{v}?q=1", dict(v="..")), + ("/v1/{v}#frag", dict(v="..")), + ], +) +def test_dot_segment_rejected(template: str, kwargs: dict[str, Any]) -> None: + with pytest.raises(ValueError, match="dot-segment"): + path_template(template, **kwargs)