diff --git a/.gitignore b/.gitignore index 0f9a66a97..877974080 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +.prism.log .vscode _dev diff --git a/.release-please-manifest.json b/.release-please-manifest.json index b56c3d0b4..e8285b710 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.1.0-alpha.4" + ".": "0.1.0-alpha.5" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index 2049e5bd1..abad5da29 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,2 +1,2 @@ -configured_endpoints: 15 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/runloop-ai%2Frunloop-e741833ec80041045f2b16ea82c2bd4c7b2d4b4bf0ec0f5698b20ece98763d00.yml +configured_endpoints: 14 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/runloop-ai%2Frunloop-ce4dc6d4d8a6587f304abdd5adf686e08612cab1c5f1cae3cdf8e20ff4a98d2b.yml diff --git a/CHANGELOG.md b/CHANGELOG.md index b4691a9f9..9af1b51ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,16 @@ # Changelog +## 0.1.0-alpha.5 (2024-07-03) + +Full Changelog: [v0.1.0-alpha.4...v0.1.0-alpha.5](https://github.com/runloopai/api-client-python/compare/v0.1.0-alpha.4...v0.1.0-alpha.5) + +### Features + +* **api:** OpenAPI spec update via Stainless API ([#19](https://github.com/runloopai/api-client-python/issues/19)) ([9def751](https://github.com/runloopai/api-client-python/commit/9def7518972cdb0c0a03728408d8e10f79090ba5)) +* **api:** OpenAPI spec update via Stainless API ([#20](https://github.com/runloopai/api-client-python/issues/20)) ([a0bf266](https://github.com/runloopai/api-client-python/commit/a0bf2667f1831d91cc1375328f355dc561b98a37)) +* **api:** OpenAPI spec update via Stainless API ([#21](https://github.com/runloopai/api-client-python/issues/21)) ([9e9f3c2](https://github.com/runloopai/api-client-python/commit/9e9f3c2150928658c49e5c18569411766be60bbe)) +* **api:** OpenAPI spec update via Stainless API ([#22](https://github.com/runloopai/api-client-python/issues/22)) ([5bf8bc0](https://github.com/runloopai/api-client-python/commit/5bf8bc0c5030ed78d04b7a513a0d167aa3e9acf8)) + ## 0.1.0-alpha.4 (2024-06-26) Full Changelog: [v0.1.0-alpha.3...v0.1.0-alpha.4](https://github.com/runloopai/api-client-python/compare/v0.1.0-alpha.3...v0.1.0-alpha.4) diff --git a/api.md b/api.md index 25022156b..92527588e 100644 --- a/api.md +++ b/api.md @@ -17,7 +17,7 @@ Methods: - client.devboxes.create(\*\*params) -> DevboxView - client.devboxes.retrieve(id) -> DevboxView - client.devboxes.list(\*\*params) -> DevboxListView -- client.devboxes.execute_sync(id) -> DevboxExecutionDetailView +- client.devboxes.execute_sync(id, \*\*params) -> DevboxExecutionDetailView - client.devboxes.shutdown(id) -> DevboxView ## Logs @@ -60,18 +60,6 @@ Methods: - client.functions.invocations.list() -> FunctionInvocationListView - client.functions.invocations.kill(invocation_id) -> object -### Spans - -Types: - -```python -from runloop_api_client.types.functions.invocations import InvocationSpanListView -``` - -Methods: - -- client.functions.invocations.spans.list(invocation_id) -> InvocationSpanListView - # Projects Types: diff --git a/pyproject.toml b/pyproject.toml index d77d92bc3..6f0a42419 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "runloop_api_client" -version = "0.1.0-alpha.4" +version = "0.1.0-alpha.5" description = "The official Python library for the runloop API" dynamic = ["readme"] license = "MIT" @@ -58,6 +58,7 @@ dev-dependencies = [ "nox", "dirty-equals>=0.6.0", "importlib-metadata>=6.7.0", + "rich>=13.7.1", ] @@ -99,6 +100,21 @@ include = [ [tool.hatch.build.targets.wheel] packages = ["src/runloop_api_client"] +[tool.hatch.build.targets.sdist] +# Basically everything except hidden files/directories (such as .github, .devcontainers, .python-version, etc) +include = [ + "/*.toml", + "/*.json", + "/*.lock", + "/*.md", + "/mypy.ini", + "/noxfile.py", + "bin/*", + "examples/*", + "src/*", + "tests/*", +] + [tool.hatch.metadata.hooks.fancy-pypi-readme] content-type = "text/markdown" diff --git a/requirements-dev.lock b/requirements-dev.lock index 0852c56d1..18a7b48a4 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -10,7 +10,7 @@ -e file:. annotated-types==0.6.0 # via pydantic -anyio==4.1.0 +anyio==4.4.0 # via httpx # via runloop-api-client argcomplete==3.1.2 @@ -44,6 +44,10 @@ idna==3.4 importlib-metadata==7.0.0 iniconfig==2.0.0 # via pytest +markdown-it-py==3.0.0 + # via rich +mdurl==0.1.2 + # via markdown-it-py mypy==1.7.1 mypy-extensions==1.0.0 # via mypy @@ -63,6 +67,8 @@ pydantic==2.7.1 # via runloop-api-client pydantic-core==2.18.2 # via pydantic +pygments==2.18.0 + # via rich pyright==1.1.364 pytest==7.1.1 # via pytest-asyncio @@ -72,6 +78,7 @@ python-dateutil==2.8.2 pytz==2023.3.post1 # via dirty-equals respx==0.20.2 +rich==13.7.1 ruff==0.1.9 setuptools==68.2.2 # via nodeenv @@ -86,6 +93,7 @@ tomli==2.0.1 # via mypy # via pytest typing-extensions==4.8.0 + # via anyio # via mypy # via pydantic # via pydantic-core diff --git a/requirements.lock b/requirements.lock index 70c5bb66c..4b4984989 100644 --- a/requirements.lock +++ b/requirements.lock @@ -10,7 +10,7 @@ -e file:. annotated-types==0.6.0 # via pydantic -anyio==4.1.0 +anyio==4.4.0 # via httpx # via runloop-api-client certifi==2023.7.22 @@ -38,6 +38,7 @@ sniffio==1.3.0 # via httpx # via runloop-api-client typing-extensions==4.8.0 + # via anyio # via pydantic # via pydantic-core # via runloop-api-client diff --git a/src/runloop_api_client/_base_client.py b/src/runloop_api_client/_base_client.py index 7e585689b..8537b9733 100644 --- a/src/runloop_api_client/_base_client.py +++ b/src/runloop_api_client/_base_client.py @@ -58,6 +58,7 @@ HttpxSendArgs, AsyncTransport, RequestOptions, + HttpxRequestFiles, ModelBuilderProtocol, ) from ._utils import is_dict, is_list, asyncify, is_given, lru_cache, is_mapping @@ -459,6 +460,7 @@ def _build_request( headers = self._build_headers(options) params = _merge_mappings(self.default_query, options.params) content_type = headers.get("Content-Type") + files = options.files # If the given Content-Type header is multipart/form-data then it # has to be removed so that httpx can generate the header with @@ -472,7 +474,7 @@ def _build_request( headers.pop("Content-Type") # As we are now sending multipart/form-data instead of application/json - # we need to tell httpx to use it, https://www.python-httpx.org/advanced/#multipart-file-encoding + # we need to tell httpx to use it, https://www.python-httpx.org/advanced/clients/#multipart-file-encoding if json_data: if not is_dict(json_data): raise TypeError( @@ -480,6 +482,15 @@ def _build_request( ) kwargs["data"] = self._serialize_multipartform(json_data) + # httpx determines whether or not to send a "multipart/form-data" + # request based on the truthiness of the "files" argument. + # This gets around that issue by generating a dict value that + # evaluates to true. + # + # https://github.com/encode/httpx/discussions/2399#discussioncomment-3814186 + if not files: + files = cast(HttpxRequestFiles, ForceMultipartDict()) + # TODO: report this error to httpx return self._client.build_request( # pyright: ignore[reportUnknownMemberType] headers=headers, @@ -492,7 +503,7 @@ def _build_request( # https://github.com/microsoft/pyright/issues/3526#event-6715453066 params=self.qs.stringify(cast(Mapping[str, Any], params)) if params else None, json=json_data, - files=options.files, + files=files, **kwargs, ) @@ -1863,6 +1874,11 @@ def make_request_options( return options +class ForceMultipartDict(Dict[str, None]): + def __bool__(self) -> bool: + return True + + class OtherPlatform: def __init__(self, name: str) -> None: self.name = name diff --git a/src/runloop_api_client/_client.py b/src/runloop_api_client/_client.py index 42f5a0422..11b16c4a1 100644 --- a/src/runloop_api_client/_client.py +++ b/src/runloop_api_client/_client.py @@ -93,7 +93,7 @@ def __init__( if base_url is None: base_url = os.environ.get("RUNLOOP_BASE_URL") if base_url is None: - base_url = f"https://api.runloop.pro" + base_url = f"https://api.runloop.ai" super().__init__( version=__version__, @@ -265,7 +265,7 @@ def __init__( if base_url is None: base_url = os.environ.get("RUNLOOP_BASE_URL") if base_url is None: - base_url = f"https://api.runloop.pro" + base_url = f"https://api.runloop.ai" super().__init__( version=__version__, diff --git a/src/runloop_api_client/_models.py b/src/runloop_api_client/_models.py index 75c68cc73..5d95bb4b2 100644 --- a/src/runloop_api_client/_models.py +++ b/src/runloop_api_client/_models.py @@ -10,6 +10,7 @@ ClassVar, Protocol, Required, + ParamSpec, TypedDict, TypeGuard, final, @@ -67,6 +68,9 @@ __all__ = ["BaseModel", "GenericModel"] _T = TypeVar("_T") +_BaseModelT = TypeVar("_BaseModelT", bound="BaseModel") + +P = ParamSpec("P") @runtime_checkable @@ -379,6 +383,29 @@ def is_basemodel_type(type_: type) -> TypeGuard[type[BaseModel] | type[GenericMo return issubclass(origin, BaseModel) or issubclass(origin, GenericModel) +def build( + base_model_cls: Callable[P, _BaseModelT], + *args: P.args, + **kwargs: P.kwargs, +) -> _BaseModelT: + """Construct a BaseModel class without validation. + + This is useful for cases where you need to instantiate a `BaseModel` + from an API response as this provides type-safe params which isn't supported + by helpers like `construct_type()`. + + ```py + build(MyModel, my_field_a="foo", my_field_b=123) + ``` + """ + if args: + raise TypeError( + "Received positional arguments which are not supported; Keyword arguments must be used instead", + ) + + return cast(_BaseModelT, construct_type(type_=base_model_cls, value=kwargs)) + + def construct_type(*, value: object, type_: object) -> object: """Loose coercion to the expected type with construction of nested values. diff --git a/src/runloop_api_client/_utils/__init__.py b/src/runloop_api_client/_utils/__init__.py index 667e2473f..3efe66c8e 100644 --- a/src/runloop_api_client/_utils/__init__.py +++ b/src/runloop_api_client/_utils/__init__.py @@ -49,4 +49,7 @@ maybe_transform as maybe_transform, async_maybe_transform as async_maybe_transform, ) -from ._reflection import function_has_argument as function_has_argument +from ._reflection import ( + function_has_argument as function_has_argument, + assert_signatures_in_sync as assert_signatures_in_sync, +) diff --git a/src/runloop_api_client/_utils/_reflection.py b/src/runloop_api_client/_utils/_reflection.py index e134f58e0..9a53c7bd2 100644 --- a/src/runloop_api_client/_utils/_reflection.py +++ b/src/runloop_api_client/_utils/_reflection.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import inspect from typing import Any, Callable @@ -6,3 +8,35 @@ def function_has_argument(func: Callable[..., Any], arg_name: str) -> bool: """Returns whether or not the given function has a specific parameter""" sig = inspect.signature(func) return arg_name in sig.parameters + + +def assert_signatures_in_sync( + source_func: Callable[..., Any], + check_func: Callable[..., Any], + *, + exclude_params: set[str] = set(), +) -> None: + """Ensure that the signature of the second function matches the first.""" + + check_sig = inspect.signature(check_func) + source_sig = inspect.signature(source_func) + + errors: list[str] = [] + + for name, source_param in source_sig.parameters.items(): + if name in exclude_params: + continue + + custom_param = check_sig.parameters.get(name) + if not custom_param: + errors.append(f"the `{name}` param is missing") + continue + + if custom_param.annotation != source_param.annotation: + errors.append( + f"types for the `{name}` param are do not match; source={repr(source_param.annotation)} checking={repr(source_param.annotation)}" + ) + continue + + if errors: + raise AssertionError(f"{len(errors)} errors encountered when comparing signatures:\n\n" + "\n\n".join(errors)) diff --git a/src/runloop_api_client/_version.py b/src/runloop_api_client/_version.py index 23a34c2b2..8cdc8ca05 100644 --- a/src/runloop_api_client/_version.py +++ b/src/runloop_api_client/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "runloop_api_client" -__version__ = "0.1.0-alpha.4" # x-release-please-version +__version__ = "0.1.0-alpha.5" # x-release-please-version diff --git a/src/runloop_api_client/resources/devboxes/devboxes.py b/src/runloop_api_client/resources/devboxes/devboxes.py index 2bba79483..7d03a0706 100644 --- a/src/runloop_api_client/resources/devboxes/devboxes.py +++ b/src/runloop_api_client/resources/devboxes/devboxes.py @@ -14,7 +14,7 @@ LogsResourceWithStreamingResponse, AsyncLogsResourceWithStreamingResponse, ) -from ...types import devbox_list_params, devbox_create_params +from ...types import devbox_list_params, devbox_create_params, devbox_execute_sync_params from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven from ..._utils import ( maybe_transform, @@ -57,6 +57,7 @@ def create( code_handle: str | NotGiven = NOT_GIVEN, entrypoint: str | NotGiven = NOT_GIVEN, environment_variables: Dict[str, str] | NotGiven = NOT_GIVEN, + name: str | NotGiven = NOT_GIVEN, setup_commands: List[str] | NotGiven = NOT_GIVEN, # 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. @@ -79,6 +80,8 @@ def create( environment_variables: (Optional) Environment variables used to configure your Devbox. + name: (Optional) A user specified name to give the Devbox. + setup_commands: (Optional) List of commands needed to set up your Devbox. Examples might include fetching a tool or building your dependencies. Runloop will look optimize these steps for you. @@ -98,6 +101,7 @@ def create( "code_handle": code_handle, "entrypoint": entrypoint, "environment_variables": environment_variables, + "name": name, "setup_commands": setup_commands, }, devbox_create_params.DevboxCreateParams, @@ -185,6 +189,7 @@ def execute_sync( self, id: str, *, + command: str | NotGiven = NOT_GIVEN, # 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, @@ -196,6 +201,8 @@ def execute_sync( Synchronously execute a command on a devbox Args: + command: The command to execute on the Devbox. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -208,6 +215,7 @@ def execute_sync( raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._post( f"/v1/devboxes/{id}/execute_sync", + body=maybe_transform({"command": command}, devbox_execute_sync_params.DevboxExecuteSyncParams), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -268,6 +276,7 @@ async def create( code_handle: str | NotGiven = NOT_GIVEN, entrypoint: str | NotGiven = NOT_GIVEN, environment_variables: Dict[str, str] | NotGiven = NOT_GIVEN, + name: str | NotGiven = NOT_GIVEN, setup_commands: List[str] | NotGiven = NOT_GIVEN, # 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. @@ -290,6 +299,8 @@ async def create( environment_variables: (Optional) Environment variables used to configure your Devbox. + name: (Optional) A user specified name to give the Devbox. + setup_commands: (Optional) List of commands needed to set up your Devbox. Examples might include fetching a tool or building your dependencies. Runloop will look optimize these steps for you. @@ -309,6 +320,7 @@ async def create( "code_handle": code_handle, "entrypoint": entrypoint, "environment_variables": environment_variables, + "name": name, "setup_commands": setup_commands, }, devbox_create_params.DevboxCreateParams, @@ -396,6 +408,7 @@ async def execute_sync( self, id: str, *, + command: str | NotGiven = NOT_GIVEN, # 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, @@ -407,6 +420,8 @@ async def execute_sync( Synchronously execute a command on a devbox Args: + command: The command to execute on the Devbox. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -419,6 +434,7 @@ async def execute_sync( raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._post( f"/v1/devboxes/{id}/execute_sync", + body=await async_maybe_transform({"command": command}, devbox_execute_sync_params.DevboxExecuteSyncParams), 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/functions/invocations/__init__.py b/src/runloop_api_client/resources/functions/invocations/__init__.py index dafa50446..338f27980 100644 --- a/src/runloop_api_client/resources/functions/invocations/__init__.py +++ b/src/runloop_api_client/resources/functions/invocations/__init__.py @@ -1,13 +1,5 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from .spans import ( - SpansResource, - AsyncSpansResource, - SpansResourceWithRawResponse, - AsyncSpansResourceWithRawResponse, - SpansResourceWithStreamingResponse, - AsyncSpansResourceWithStreamingResponse, -) from .invocations import ( InvocationsResource, AsyncInvocationsResource, @@ -18,12 +10,6 @@ ) __all__ = [ - "SpansResource", - "AsyncSpansResource", - "SpansResourceWithRawResponse", - "AsyncSpansResourceWithRawResponse", - "SpansResourceWithStreamingResponse", - "AsyncSpansResourceWithStreamingResponse", "InvocationsResource", "AsyncInvocationsResource", "InvocationsResourceWithRawResponse", diff --git a/src/runloop_api_client/resources/functions/invocations/invocations.py b/src/runloop_api_client/resources/functions/invocations/invocations.py index 66cd346b2..3130b0976 100644 --- a/src/runloop_api_client/resources/functions/invocations/invocations.py +++ b/src/runloop_api_client/resources/functions/invocations/invocations.py @@ -4,14 +4,6 @@ import httpx -from .spans import ( - SpansResource, - AsyncSpansResource, - SpansResourceWithRawResponse, - AsyncSpansResourceWithRawResponse, - SpansResourceWithStreamingResponse, - AsyncSpansResourceWithStreamingResponse, -) from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven from ...._compat import cached_property from ...._resource import SyncAPIResource, AsyncAPIResource @@ -31,10 +23,6 @@ class InvocationsResource(SyncAPIResource): - @cached_property - def spans(self) -> SpansResource: - return SpansResource(self._client) - @cached_property def with_raw_response(self) -> InvocationsResourceWithRawResponse: return InvocationsResourceWithRawResponse(self) @@ -133,10 +121,6 @@ def kill( class AsyncInvocationsResource(AsyncAPIResource): - @cached_property - def spans(self) -> AsyncSpansResource: - return AsyncSpansResource(self._client) - @cached_property def with_raw_response(self) -> AsyncInvocationsResourceWithRawResponse: return AsyncInvocationsResourceWithRawResponse(self) @@ -248,10 +232,6 @@ def __init__(self, invocations: InvocationsResource) -> None: invocations.kill, ) - @cached_property - def spans(self) -> SpansResourceWithRawResponse: - return SpansResourceWithRawResponse(self._invocations.spans) - class AsyncInvocationsResourceWithRawResponse: def __init__(self, invocations: AsyncInvocationsResource) -> None: @@ -267,10 +247,6 @@ def __init__(self, invocations: AsyncInvocationsResource) -> None: invocations.kill, ) - @cached_property - def spans(self) -> AsyncSpansResourceWithRawResponse: - return AsyncSpansResourceWithRawResponse(self._invocations.spans) - class InvocationsResourceWithStreamingResponse: def __init__(self, invocations: InvocationsResource) -> None: @@ -286,10 +262,6 @@ def __init__(self, invocations: InvocationsResource) -> None: invocations.kill, ) - @cached_property - def spans(self) -> SpansResourceWithStreamingResponse: - return SpansResourceWithStreamingResponse(self._invocations.spans) - class AsyncInvocationsResourceWithStreamingResponse: def __init__(self, invocations: AsyncInvocationsResource) -> None: @@ -304,7 +276,3 @@ def __init__(self, invocations: AsyncInvocationsResource) -> None: self.kill = async_to_streamed_response_wrapper( invocations.kill, ) - - @cached_property - def spans(self) -> AsyncSpansResourceWithStreamingResponse: - return AsyncSpansResourceWithStreamingResponse(self._invocations.spans) diff --git a/src/runloop_api_client/resources/functions/invocations/spans.py b/src/runloop_api_client/resources/functions/invocations/spans.py deleted file mode 100644 index 3abe920d9..000000000 --- a/src/runloop_api_client/resources/functions/invocations/spans.py +++ /dev/null @@ -1,147 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -import httpx - -from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven -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 ...._base_client import ( - make_request_options, -) -from ....types.functions.invocations.invocation_span_list_view import InvocationSpanListView - -__all__ = ["SpansResource", "AsyncSpansResource"] - - -class SpansResource(SyncAPIResource): - @cached_property - def with_raw_response(self) -> SpansResourceWithRawResponse: - return SpansResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> SpansResourceWithStreamingResponse: - return SpansResourceWithStreamingResponse(self) - - def list( - self, - invocation_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, - ) -> InvocationSpanListView: - """Get the span points for the given invocation. - - This will return the span points - for the invocation. - - 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 invocation_id: - raise ValueError(f"Expected a non-empty value for `invocation_id` but received {invocation_id!r}") - return self._get( - f"/v1/functions/invocations/{invocation_id}/spans", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=InvocationSpanListView, - ) - - -class AsyncSpansResource(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncSpansResourceWithRawResponse: - return AsyncSpansResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncSpansResourceWithStreamingResponse: - return AsyncSpansResourceWithStreamingResponse(self) - - async def list( - self, - invocation_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, - ) -> InvocationSpanListView: - """Get the span points for the given invocation. - - This will return the span points - for the invocation. - - 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 invocation_id: - raise ValueError(f"Expected a non-empty value for `invocation_id` but received {invocation_id!r}") - return await self._get( - f"/v1/functions/invocations/{invocation_id}/spans", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=InvocationSpanListView, - ) - - -class SpansResourceWithRawResponse: - def __init__(self, spans: SpansResource) -> None: - self._spans = spans - - self.list = to_raw_response_wrapper( - spans.list, - ) - - -class AsyncSpansResourceWithRawResponse: - def __init__(self, spans: AsyncSpansResource) -> None: - self._spans = spans - - self.list = async_to_raw_response_wrapper( - spans.list, - ) - - -class SpansResourceWithStreamingResponse: - def __init__(self, spans: SpansResource) -> None: - self._spans = spans - - self.list = to_streamed_response_wrapper( - spans.list, - ) - - -class AsyncSpansResourceWithStreamingResponse: - def __init__(self, spans: AsyncSpansResource) -> None: - self._spans = spans - - self.list = async_to_streamed_response_wrapper( - spans.list, - ) diff --git a/src/runloop_api_client/types/__init__.py b/src/runloop_api_client/types/__init__.py index 8cbd8f02b..f31475c02 100644 --- a/src/runloop_api_client/types/__init__.py +++ b/src/runloop_api_client/types/__init__.py @@ -9,6 +9,7 @@ from .devbox_list_params import DevboxListParams as DevboxListParams from .function_list_view import FunctionListView as FunctionListView from .devbox_create_params import DevboxCreateParams as DevboxCreateParams +from .devbox_execute_sync_params import DevboxExecuteSyncParams as DevboxExecuteSyncParams from .function_invoke_sync_params import FunctionInvokeSyncParams as FunctionInvokeSyncParams from .devbox_execution_detail_view import DevboxExecutionDetailView as DevboxExecutionDetailView from .function_invoke_async_params import FunctionInvokeAsyncParams as FunctionInvokeAsyncParams diff --git a/src/runloop_api_client/types/devbox_create_params.py b/src/runloop_api_client/types/devbox_create_params.py index 588469cf4..49834e010 100644 --- a/src/runloop_api_client/types/devbox_create_params.py +++ b/src/runloop_api_client/types/devbox_create_params.py @@ -22,6 +22,9 @@ class DevboxCreateParams(TypedDict, total=False): environment_variables: Dict[str, str] """(Optional) Environment variables used to configure your Devbox.""" + name: str + """(Optional) A user specified name to give the Devbox.""" + setup_commands: List[str] """(Optional) List of commands needed to set up your Devbox. diff --git a/src/runloop_api_client/types/devbox_execute_sync_params.py b/src/runloop_api_client/types/devbox_execute_sync_params.py new file mode 100644 index 000000000..12dbc2e0f --- /dev/null +++ b/src/runloop_api_client/types/devbox_execute_sync_params.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["DevboxExecuteSyncParams"] + + +class DevboxExecuteSyncParams(TypedDict, total=False): + command: str + """The command to execute on the Devbox.""" diff --git a/src/runloop_api_client/types/devbox_view.py b/src/runloop_api_client/types/devbox_view.py index 826b180e9..5dec94295 100644 --- a/src/runloop_api_client/types/devbox_view.py +++ b/src/runloop_api_client/types/devbox_view.py @@ -14,6 +14,9 @@ class DevboxView(BaseModel): create_time_ms: Optional[int] = None """Creation time of the Devbox (Unix timestamp milliseconds).""" + name: Optional[str] = None + """The name of the Devbox.""" + status: Optional[str] = None """ The current status of the Devbox (provisioning, initializing, running, failure, diff --git a/src/runloop_api_client/types/devboxes/devbox_logs_list_view.py b/src/runloop_api_client/types/devboxes/devbox_logs_list_view.py index 94fafc41a..e73fec580 100644 --- a/src/runloop_api_client/types/devboxes/devbox_logs_list_view.py +++ b/src/runloop_api_client/types/devboxes/devbox_logs_list_view.py @@ -1,6 +1,7 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List, Optional +from typing_extensions import Literal from ..._models import BaseModel @@ -8,12 +9,24 @@ class Log(BaseModel): + cmd: Optional[str] = None + """The Command Executed""" + + cmd_id: Optional[str] = None + """Identifier of the associated command the log is sourced from.""" + + exit_code: Optional[int] = None + """The Exit Code of the command""" + level: Optional[str] = None """Log line severity level.""" message: Optional[str] = None """Log line message.""" + source: Optional[Literal["setup_commands", "entrypoint", "exec"]] = None + """The source of the log.""" + timestamp_ms: Optional[int] = None """Time of log (Unix timestamp milliseconds).""" diff --git a/src/runloop_api_client/types/functions/invocations/__init__.py b/src/runloop_api_client/types/functions/invocations/__init__.py index 4caaf4299..f8ee8b14b 100644 --- a/src/runloop_api_client/types/functions/invocations/__init__.py +++ b/src/runloop_api_client/types/functions/invocations/__init__.py @@ -1,5 +1,3 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations - -from .invocation_span_list_view import InvocationSpanListView as InvocationSpanListView diff --git a/src/runloop_api_client/types/functions/invocations/invocation_span_list_view.py b/src/runloop_api_client/types/functions/invocations/invocation_span_list_view.py deleted file mode 100644 index 786307ae8..000000000 --- a/src/runloop_api_client/types/functions/invocations/invocation_span_list_view.py +++ /dev/null @@ -1,16 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional - -from pydantic import Field as FieldInfo - -from ...._models import BaseModel - -__all__ = ["InvocationSpanListView"] - - -class InvocationSpanListView(BaseModel): - invocation_id: Optional[str] = FieldInfo(alias="invocationId", default=None) - - spans: Optional[object] = None - """List of spans matching given query.""" diff --git a/tests/api_resources/functions/invocations/test_spans.py b/tests/api_resources/functions/invocations/test_spans.py deleted file mode 100644 index a906a79e7..000000000 --- a/tests/api_resources/functions/invocations/test_spans.py +++ /dev/null @@ -1,98 +0,0 @@ -# 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.functions.invocations import InvocationSpanListView - -base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") - - -class TestSpans: - parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - - @parametrize - def test_method_list(self, client: Runloop) -> None: - span = client.functions.invocations.spans.list( - "string", - ) - assert_matches_type(InvocationSpanListView, span, path=["response"]) - - @parametrize - def test_raw_response_list(self, client: Runloop) -> None: - response = client.functions.invocations.spans.with_raw_response.list( - "string", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - span = response.parse() - assert_matches_type(InvocationSpanListView, span, path=["response"]) - - @parametrize - def test_streaming_response_list(self, client: Runloop) -> None: - with client.functions.invocations.spans.with_streaming_response.list( - "string", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - span = response.parse() - assert_matches_type(InvocationSpanListView, span, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - def test_path_params_list(self, client: Runloop) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `invocation_id` but received ''"): - client.functions.invocations.spans.with_raw_response.list( - "", - ) - - -class TestAsyncSpans: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) - - @parametrize - async def test_method_list(self, async_client: AsyncRunloop) -> None: - span = await async_client.functions.invocations.spans.list( - "string", - ) - assert_matches_type(InvocationSpanListView, span, path=["response"]) - - @parametrize - async def test_raw_response_list(self, async_client: AsyncRunloop) -> None: - response = await async_client.functions.invocations.spans.with_raw_response.list( - "string", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - span = await response.parse() - assert_matches_type(InvocationSpanListView, span, path=["response"]) - - @parametrize - async def test_streaming_response_list(self, async_client: AsyncRunloop) -> None: - async with async_client.functions.invocations.spans.with_streaming_response.list( - "string", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - span = await response.parse() - assert_matches_type(InvocationSpanListView, span, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - async def test_path_params_list(self, async_client: AsyncRunloop) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `invocation_id` but received ''"): - await async_client.functions.invocations.spans.with_raw_response.list( - "", - ) diff --git a/tests/api_resources/test_devboxes.py b/tests/api_resources/test_devboxes.py index e1bea0100..b6940e903 100644 --- a/tests/api_resources/test_devboxes.py +++ b/tests/api_resources/test_devboxes.py @@ -32,6 +32,7 @@ def test_method_create_with_all_params(self, client: Runloop) -> None: code_handle="string", entrypoint="string", environment_variables={"foo": "string"}, + name="string", setup_commands=["string", "string", "string"], ) assert_matches_type(DevboxView, devbox, path=["response"]) @@ -133,6 +134,14 @@ def test_method_execute_sync(self, client: Runloop) -> None: ) assert_matches_type(DevboxExecutionDetailView, devbox, path=["response"]) + @parametrize + def test_method_execute_sync_with_all_params(self, client: Runloop) -> None: + devbox = client.devboxes.execute_sync( + "string", + command="string", + ) + assert_matches_type(DevboxExecutionDetailView, devbox, path=["response"]) + @parametrize def test_raw_response_execute_sync(self, client: Runloop) -> None: response = client.devboxes.with_raw_response.execute_sync( @@ -217,6 +226,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncRunloop) - code_handle="string", entrypoint="string", environment_variables={"foo": "string"}, + name="string", setup_commands=["string", "string", "string"], ) assert_matches_type(DevboxView, devbox, path=["response"]) @@ -318,6 +328,14 @@ async def test_method_execute_sync(self, async_client: AsyncRunloop) -> None: ) assert_matches_type(DevboxExecutionDetailView, devbox, path=["response"]) + @parametrize + async def test_method_execute_sync_with_all_params(self, async_client: AsyncRunloop) -> None: + devbox = await async_client.devboxes.execute_sync( + "string", + command="string", + ) + assert_matches_type(DevboxExecutionDetailView, devbox, path=["response"]) + @parametrize async def test_raw_response_execute_sync(self, async_client: AsyncRunloop) -> None: response = await async_client.devboxes.with_raw_response.execute_sync(