diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 397c4203e..efc2d51b4 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.22.0" + ".": "1.22.1" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index e0dbe7824..f79cbaf49 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 119 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/runloop-ai/runloop-7235763cbdfd60834a897f356688d758b598a1dd723623330ea398dea2abea68.yml +configured_endpoints: 120 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/runloop-ai/runloop-96b0ac0a148db6fde2e8363ea2dcfaa63f2dc23cf35c30c5fcfffbefc222e5d1.yml openapi_spec_hash: 01b9dbab4b732e4b83952debd108e404 -config_hash: 444e00951b440bf92e7548b2807584a4 +config_hash: ed1fdd7c9f0a25647e16b602bad4ff2e diff --git a/CHANGELOG.md b/CHANGELOG.md index 93640514e..338e576e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.22.1 (2026-06-01) + +Full Changelog: [v1.22.0...v1.22.1](https://github.com/runloopai/api-client-python/compare/v1.22.0...v1.22.1) + +### Build System + +* **stainless:** map /v1/accounts/me to accounts.me in the SDK ([#9569](https://github.com/runloopai/api-client-python/issues/9569)) ([3f9cbe1](https://github.com/runloopai/api-client-python/commit/3f9cbe1e92c2eb9c1696f52cb11419bcafde6bd9)) + ## 1.22.0 (2026-05-27) Full Changelog: [v1.21.0...v1.22.0](https://github.com/runloopai/api-client-python/compare/v1.21.0...v1.22.0) diff --git a/api.md b/api.md index 30535e6f1..fbd1bc6ab 100644 --- a/api.md +++ b/api.md @@ -17,6 +17,18 @@ from runloop_api_client.types import ( ) ``` +# Accounts + +Types: + +```python +from runloop_api_client.types import AccountView +``` + +Methods: + +- client.accounts.me() -> AccountView + # Benchmarks Types: diff --git a/pyproject.toml b/pyproject.toml index 1b48f1988..2e0894a4c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "runloop_api_client" -version = "1.22.0" +version = "1.22.1" description = "The official Python library for the runloop API" dynamic = ["readme"] license = "MIT" diff --git a/src/runloop_api_client/_client.py b/src/runloop_api_client/_client.py index 9e422721e..ed052ec87 100644 --- a/src/runloop_api_client/_client.py +++ b/src/runloop_api_client/_client.py @@ -42,6 +42,7 @@ apikeys, objects, secrets, + accounts, devboxes, scenarios, benchmarks, @@ -58,6 +59,7 @@ from .resources.apikeys import ApikeysResource, AsyncApikeysResource from .resources.objects import ObjectsResource, AsyncObjectsResource from .resources.secrets import SecretsResource, AsyncSecretsResource + from .resources.accounts import AccountsResource, AsyncAccountsResource from .resources.benchmarks import BenchmarksResource, AsyncBenchmarksResource from .resources.blueprints import BlueprintsResource, AsyncBlueprintsResource from .resources.axons.axons import AxonsResource, AsyncAxonsResource @@ -144,6 +146,12 @@ def __init__( self._idempotency_header = "x-request-id" + @cached_property + def accounts(self) -> AccountsResource: + from .resources.accounts import AccountsResource + + return AccountsResource(self) + @cached_property def benchmarks(self) -> BenchmarksResource: from .resources.benchmarks import BenchmarksResource @@ -432,6 +440,12 @@ def __init__( self._idempotency_header = "x-request-id" + @cached_property + def accounts(self) -> AsyncAccountsResource: + from .resources.accounts import AsyncAccountsResource + + return AsyncAccountsResource(self) + @cached_property def benchmarks(self) -> AsyncBenchmarksResource: from .resources.benchmarks import AsyncBenchmarksResource @@ -655,6 +669,12 @@ class RunloopWithRawResponse: def __init__(self, client: Runloop) -> None: self._client = client + @cached_property + def accounts(self) -> accounts.AccountsResourceWithRawResponse: + from .resources.accounts import AccountsResourceWithRawResponse + + return AccountsResourceWithRawResponse(self._client.accounts) + @cached_property def benchmarks(self) -> benchmarks.BenchmarksResourceWithRawResponse: from .resources.benchmarks import BenchmarksResourceWithRawResponse @@ -758,6 +778,12 @@ class AsyncRunloopWithRawResponse: def __init__(self, client: AsyncRunloop) -> None: self._client = client + @cached_property + def accounts(self) -> accounts.AsyncAccountsResourceWithRawResponse: + from .resources.accounts import AsyncAccountsResourceWithRawResponse + + return AsyncAccountsResourceWithRawResponse(self._client.accounts) + @cached_property def benchmarks(self) -> benchmarks.AsyncBenchmarksResourceWithRawResponse: from .resources.benchmarks import AsyncBenchmarksResourceWithRawResponse @@ -861,6 +887,12 @@ class RunloopWithStreamedResponse: def __init__(self, client: Runloop) -> None: self._client = client + @cached_property + def accounts(self) -> accounts.AccountsResourceWithStreamingResponse: + from .resources.accounts import AccountsResourceWithStreamingResponse + + return AccountsResourceWithStreamingResponse(self._client.accounts) + @cached_property def benchmarks(self) -> benchmarks.BenchmarksResourceWithStreamingResponse: from .resources.benchmarks import BenchmarksResourceWithStreamingResponse @@ -964,6 +996,12 @@ class AsyncRunloopWithStreamedResponse: def __init__(self, client: AsyncRunloop) -> None: self._client = client + @cached_property + def accounts(self) -> accounts.AsyncAccountsResourceWithStreamingResponse: + from .resources.accounts import AsyncAccountsResourceWithStreamingResponse + + return AsyncAccountsResourceWithStreamingResponse(self._client.accounts) + @cached_property def benchmarks(self) -> benchmarks.AsyncBenchmarksResourceWithStreamingResponse: from .resources.benchmarks import AsyncBenchmarksResourceWithStreamingResponse diff --git a/src/runloop_api_client/_version.py b/src/runloop_api_client/_version.py index 27a4b2948..8acca19a1 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.22.0" # x-release-please-version +__version__ = "1.22.1" # x-release-please-version diff --git a/src/runloop_api_client/resources/__init__.py b/src/runloop_api_client/resources/__init__.py index e7442ab06..2e6ca5d16 100644 --- a/src/runloop_api_client/resources/__init__.py +++ b/src/runloop_api_client/resources/__init__.py @@ -48,6 +48,14 @@ SecretsResourceWithStreamingResponse, AsyncSecretsResourceWithStreamingResponse, ) +from .accounts import ( + AccountsResource, + AsyncAccountsResource, + AccountsResourceWithRawResponse, + AsyncAccountsResourceWithRawResponse, + AccountsResourceWithStreamingResponse, + AsyncAccountsResourceWithStreamingResponse, +) from .devboxes import ( DevboxesResource, AsyncDevboxesResource, @@ -130,6 +138,12 @@ ) __all__ = [ + "AccountsResource", + "AsyncAccountsResource", + "AccountsResourceWithRawResponse", + "AsyncAccountsResourceWithRawResponse", + "AccountsResourceWithStreamingResponse", + "AsyncAccountsResourceWithStreamingResponse", "BenchmarksResource", "AsyncBenchmarksResource", "BenchmarksResourceWithRawResponse", diff --git a/src/runloop_api_client/resources/accounts.py b/src/runloop_api_client/resources/accounts.py new file mode 100644 index 000000000..cc41fa591 --- /dev/null +++ b/src/runloop_api_client/resources/accounts.py @@ -0,0 +1,141 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from .._types import Body, Query, Headers, NotGiven, not_given +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.account_view import AccountView + +__all__ = ["AccountsResource", "AsyncAccountsResource"] + + +class AccountsResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> AccountsResourceWithRawResponse: + """ + 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 AccountsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AccountsResourceWithStreamingResponse: + """ + 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 AccountsResourceWithStreamingResponse(self) + + def me( + 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, + ) -> AccountView: + """ + Returns the account the API key or session is authenticated against, including + id, name, tier, and billing summary. + """ + return self._get( + "/v1/accounts/me", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=AccountView, + ) + + +class AsyncAccountsResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncAccountsResourceWithRawResponse: + """ + 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 AsyncAccountsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncAccountsResourceWithStreamingResponse: + """ + 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 AsyncAccountsResourceWithStreamingResponse(self) + + async def me( + 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, + ) -> AccountView: + """ + Returns the account the API key or session is authenticated against, including + id, name, tier, and billing summary. + """ + return await self._get( + "/v1/accounts/me", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=AccountView, + ) + + +class AccountsResourceWithRawResponse: + def __init__(self, accounts: AccountsResource) -> None: + self._accounts = accounts + + self.me = to_raw_response_wrapper( + accounts.me, + ) + + +class AsyncAccountsResourceWithRawResponse: + def __init__(self, accounts: AsyncAccountsResource) -> None: + self._accounts = accounts + + self.me = async_to_raw_response_wrapper( + accounts.me, + ) + + +class AccountsResourceWithStreamingResponse: + def __init__(self, accounts: AccountsResource) -> None: + self._accounts = accounts + + self.me = to_streamed_response_wrapper( + accounts.me, + ) + + +class AsyncAccountsResourceWithStreamingResponse: + def __init__(self, accounts: AsyncAccountsResource) -> None: + self._accounts = accounts + + self.me = async_to_streamed_response_wrapper( + accounts.me, + ) diff --git a/src/runloop_api_client/types/__init__.py b/src/runloop_api_client/types/__init__.py index 428a5a7a2..6d23d82f3 100644 --- a/src/runloop_api_client/types/__init__.py +++ b/src/runloop_api_client/types/__init__.py @@ -22,6 +22,7 @@ from .object_view import ObjectView as ObjectView from .secret_view import SecretView as SecretView from .tunnel_view import TunnelView as TunnelView +from .account_view import AccountView as AccountView from .input_context import InputContext as InputContext from .scenario_view import ScenarioView as ScenarioView from .axon_list_view import AxonListView as AxonListView diff --git a/src/runloop_api_client/types/account_view.py b/src/runloop_api_client/types/account_view.py new file mode 100644 index 000000000..0507674c1 --- /dev/null +++ b/src/runloop_api_client/types/account_view.py @@ -0,0 +1,97 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from .._models import BaseModel + +__all__ = ["AccountView", "Billing", "BillingAws", "BillingStripe"] + + +class BillingAws(BaseModel): + """AWS Marketplace billing information.""" + + customer_identifier: Optional[str] = None + """The AWS account ID used for Marketplace billing (12-digit).""" + + license_arn: Optional[str] = None + """The AWS Marketplace license ARN.""" + + subscription_status: Optional[str] = None + """The AWS Marketplace subscription status.""" + + +class BillingStripe(BaseModel): + """Stripe billing information.""" + + active_subscription: Optional[str] = None + """The active Stripe subscription ID.""" + + customer_id: Optional[str] = None + """The Stripe customer ID.""" + + +class Billing(BaseModel): + """The account billing information.""" + + account_billing_type: Literal["STRIPE", "AWS_MARKETPLACE", "STRIPE_PROJECTS", "UNRECOGNIZED"] + """The account billing type.""" + + aws: Optional[BillingAws] = None + """AWS Marketplace billing information.""" + + stripe: Optional[BillingStripe] = None + """Stripe billing information.""" + + stripe_customer_id: Optional[str] = None + """Deprecated: use stripe.customer_id.""" + + +class AccountView(BaseModel): + """Account information.""" + + id: str + """The account ID.""" + + account_status: Literal[ + "ACCOUNT_STATUS_INVALID", + "ACCOUNT_STATUS_ONBOARDING", + "ACCOUNT_STATUS_ENABLED", + "ACCOUNT_STATUS_DISABLED_BY_ADMIN", + "ACCOUNT_STATUS_DISABLED_QUOTA_REACHED", + "ACCOUNT_STATUS_TRIAL_CANCELLED", + "ACCOUNT_STATUS_STRIPE_PENDING_RESOURCES", + "UNRECOGNIZED", + ] + """The account status.""" + + billing: Billing + """The account billing information.""" + + created_at: str + """The account creation timestamp.""" + + name: str + """The account name.""" + + tier: Literal[ + "ACCOUNT_TIER_INVALID", + "ACCOUNT_TIER_BASIC", + "ACCOUNT_TIER_PRO", + "ACCOUNT_TIER_ENTERPRISE", + "ACCOUNT_TIER_TRIAL", + "UNRECOGNIZED", + ] + """The account tier.""" + + account_billing_type: Optional[Literal["STRIPE", "AWS_MARKETPLACE", "STRIPE_PROJECTS", "UNRECOGNIZED"]] = None + """Deprecated: use billing.account_billing_type.""" + + active_subscription: Optional[str] = None + """Deprecated: use billing.stripe.active_subscription.""" + + external_billing_account_id: Optional[str] = None + """Deprecated: use billing.aws.customer_identifier.""" + + stripe_customer_id: Optional[str] = None + """Deprecated: use billing.stripe.customer_id.""" diff --git a/tests/api_resources/test_accounts.py b/tests/api_resources/test_accounts.py new file mode 100644 index 000000000..6287863da --- /dev/null +++ b/tests/api_resources/test_accounts.py @@ -0,0 +1,74 @@ +# 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 AccountView + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestAccounts: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_me(self, client: Runloop) -> None: + account = client.accounts.me() + assert_matches_type(AccountView, account, path=["response"]) + + @parametrize + def test_raw_response_me(self, client: Runloop) -> None: + response = client.accounts.with_raw_response.me() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + account = response.parse() + assert_matches_type(AccountView, account, path=["response"]) + + @parametrize + def test_streaming_response_me(self, client: Runloop) -> None: + with client.accounts.with_streaming_response.me() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + account = response.parse() + assert_matches_type(AccountView, account, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncAccounts: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_me(self, async_client: AsyncRunloop) -> None: + account = await async_client.accounts.me() + assert_matches_type(AccountView, account, path=["response"]) + + @parametrize + async def test_raw_response_me(self, async_client: AsyncRunloop) -> None: + response = await async_client.accounts.with_raw_response.me() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + account = await response.parse() + assert_matches_type(AccountView, account, path=["response"]) + + @parametrize + async def test_streaming_response_me(self, async_client: AsyncRunloop) -> None: + async with async_client.accounts.with_streaming_response.me() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + account = await response.parse() + assert_matches_type(AccountView, account, path=["response"]) + + assert cast(Any, response.is_closed) is True