From d2a42a7d68b0a697c0258931cb75d16daa169153 Mon Sep 17 00:00:00 2001 From: stlc-bot Date: Fri, 21 Aug 2026 08:03:54 +0000 Subject: [PATCH 1/3] Build SDK Stainless-Generated-From: 39cd5015a21b349c60c76c112646b1d5c395d88f --- .stats.yml | 2 +- api.md | 34 + src/mobilerun_sdk/_client.py | 38 + src/mobilerun_sdk/resources/__init__.py | 14 + .../resources/assistant/__init__.py | 33 + .../resources/assistant/assistant.py | 102 ++ .../resources/assistant/conversations.py | 1111 +++++++++++++++++ src/mobilerun_sdk/types/assistant/__init__.py | 32 + .../assistant/conversation_abort_params.py | 13 + .../assistant/conversation_abort_response.py | 11 + .../conversation_answer_permission_params.py | 15 + ...conversation_answer_permission_response.py | 11 + .../conversation_answer_question_params.py | 39 + .../conversation_answer_question_response.py | 11 + .../assistant/conversation_create_params.py | 24 + .../assistant/conversation_create_response.py | 50 + .../assistant/conversation_history_params.py | 15 + .../conversation_history_response.py | 68 + .../assistant/conversation_list_params.py | 17 + .../assistant/conversation_list_response.py | 105 ++ .../conversation_reject_question_params.py | 13 + .../conversation_reject_question_response.py | 11 + .../assistant/conversation_send_params.py | 17 + .../assistant/conversation_send_response.py | 17 + .../assistant/conversation_stream_params.py | 13 + .../assistant/conversation_stream_response.py | 7 + .../assistant/conversation_update_params.py | 18 + .../assistant/conversation_update_response.py | 50 + tests/api_resources/assistant/__init__.py | 1 + .../assistant/test_conversations.py | 858 +++++++++++++ 30 files changed, 2749 insertions(+), 1 deletion(-) create mode 100644 src/mobilerun_sdk/resources/assistant/__init__.py create mode 100644 src/mobilerun_sdk/resources/assistant/assistant.py create mode 100644 src/mobilerun_sdk/resources/assistant/conversations.py create mode 100644 src/mobilerun_sdk/types/assistant/__init__.py create mode 100644 src/mobilerun_sdk/types/assistant/conversation_abort_params.py create mode 100644 src/mobilerun_sdk/types/assistant/conversation_abort_response.py create mode 100644 src/mobilerun_sdk/types/assistant/conversation_answer_permission_params.py create mode 100644 src/mobilerun_sdk/types/assistant/conversation_answer_permission_response.py create mode 100644 src/mobilerun_sdk/types/assistant/conversation_answer_question_params.py create mode 100644 src/mobilerun_sdk/types/assistant/conversation_answer_question_response.py create mode 100644 src/mobilerun_sdk/types/assistant/conversation_create_params.py create mode 100644 src/mobilerun_sdk/types/assistant/conversation_create_response.py create mode 100644 src/mobilerun_sdk/types/assistant/conversation_history_params.py create mode 100644 src/mobilerun_sdk/types/assistant/conversation_history_response.py create mode 100644 src/mobilerun_sdk/types/assistant/conversation_list_params.py create mode 100644 src/mobilerun_sdk/types/assistant/conversation_list_response.py create mode 100644 src/mobilerun_sdk/types/assistant/conversation_reject_question_params.py create mode 100644 src/mobilerun_sdk/types/assistant/conversation_reject_question_response.py create mode 100644 src/mobilerun_sdk/types/assistant/conversation_send_params.py create mode 100644 src/mobilerun_sdk/types/assistant/conversation_send_response.py create mode 100644 src/mobilerun_sdk/types/assistant/conversation_stream_params.py create mode 100644 src/mobilerun_sdk/types/assistant/conversation_stream_response.py create mode 100644 src/mobilerun_sdk/types/assistant/conversation_update_params.py create mode 100644 src/mobilerun_sdk/types/assistant/conversation_update_response.py create mode 100644 tests/api_resources/assistant/__init__.py create mode 100644 tests/api_resources/assistant/test_conversations.py diff --git a/.stats.yml b/.stats.yml index fa7630c7..bd40b4e1 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1 +1 @@ -configured_endpoints: 217 +configured_endpoints: 227 diff --git a/api.md b/api.md index 444c197a..b366e4de 100644 --- a/api.md +++ b/api.md @@ -818,6 +818,40 @@ Methods: - client.agents.list() -> AgentListResponse +# Assistant + +## Conversations + +Types: + +```python +from mobilerun_sdk.types.assistant import ( + ConversationCreateResponse, + ConversationUpdateResponse, + ConversationListResponse, + ConversationAbortResponse, + ConversationAnswerPermissionResponse, + ConversationAnswerQuestionResponse, + ConversationHistoryResponse, + ConversationRejectQuestionResponse, + ConversationSendResponse, + ConversationStreamResponse, +) +``` + +Methods: + +- client.assistant.conversations.create(\*\*params) -> ConversationCreateResponse +- client.assistant.conversations.update(id, \*\*params) -> ConversationUpdateResponse +- client.assistant.conversations.list(\*\*params) -> ConversationListResponse +- client.assistant.conversations.abort(\*\*params) -> ConversationAbortResponse +- client.assistant.conversations.answer_permission(\*\*params) -> ConversationAnswerPermissionResponse +- client.assistant.conversations.answer_question(\*\*params) -> ConversationAnswerQuestionResponse +- client.assistant.conversations.history(\*\*params) -> ConversationHistoryResponse +- client.assistant.conversations.reject_question(\*\*params) -> ConversationRejectQuestionResponse +- client.assistant.conversations.send(\*\*params) -> ConversationSendResponse +- client.assistant.conversations.stream(\*\*params) -> str + # AppEvents Types: diff --git a/src/mobilerun_sdk/_client.py b/src/mobilerun_sdk/_client.py index 218abfaf..6fccbe0d 100644 --- a/src/mobilerun_sdk/_client.py +++ b/src/mobilerun_sdk/_client.py @@ -51,6 +51,7 @@ messages, profiles, webhooks, + assistant, workflows, app_events, credentials, @@ -71,6 +72,7 @@ from .resources.numbers.numbers import NumbersResource, AsyncNumbersResource from .resources.messages.messages import MessagesResource, AsyncMessagesResource from .resources.webhooks.webhooks import WebhooksResource, AsyncWebhooksResource + from .resources.assistant.assistant import AssistantResource, AsyncAssistantResource from .resources.workflows.workflows import WorkflowsResource, AsyncWorkflowsResource from .resources.app_events.app_events import AppEventsResource, AsyncAppEventsResource from .resources.credentials.credentials import CredentialsResource, AsyncCredentialsResource @@ -221,6 +223,12 @@ def agents(self) -> AgentsResource: return AgentsResource(self) + @cached_property + def assistant(self) -> AssistantResource: + from .resources.assistant import AssistantResource + + return AssistantResource(self) + @cached_property def app_events(self) -> AppEventsResource: from .resources.app_events import AppEventsResource @@ -515,6 +523,12 @@ def agents(self) -> AsyncAgentsResource: return AsyncAgentsResource(self) + @cached_property + def assistant(self) -> AsyncAssistantResource: + from .resources.assistant import AsyncAssistantResource + + return AsyncAssistantResource(self) + @cached_property def app_events(self) -> AsyncAppEventsResource: from .resources.app_events import AsyncAppEventsResource @@ -755,6 +769,12 @@ def agents(self) -> agents.AgentsResourceWithRawResponse: return AgentsResourceWithRawResponse(self._client.agents) + @cached_property + def assistant(self) -> assistant.AssistantResourceWithRawResponse: + from .resources.assistant import AssistantResourceWithRawResponse + + return AssistantResourceWithRawResponse(self._client.assistant) + @cached_property def app_events(self) -> app_events.AppEventsResourceWithRawResponse: from .resources.app_events import AppEventsResourceWithRawResponse @@ -872,6 +892,12 @@ def agents(self) -> agents.AsyncAgentsResourceWithRawResponse: return AsyncAgentsResourceWithRawResponse(self._client.agents) + @cached_property + def assistant(self) -> assistant.AsyncAssistantResourceWithRawResponse: + from .resources.assistant import AsyncAssistantResourceWithRawResponse + + return AsyncAssistantResourceWithRawResponse(self._client.assistant) + @cached_property def app_events(self) -> app_events.AsyncAppEventsResourceWithRawResponse: from .resources.app_events import AsyncAppEventsResourceWithRawResponse @@ -989,6 +1015,12 @@ def agents(self) -> agents.AgentsResourceWithStreamingResponse: return AgentsResourceWithStreamingResponse(self._client.agents) + @cached_property + def assistant(self) -> assistant.AssistantResourceWithStreamingResponse: + from .resources.assistant import AssistantResourceWithStreamingResponse + + return AssistantResourceWithStreamingResponse(self._client.assistant) + @cached_property def app_events(self) -> app_events.AppEventsResourceWithStreamingResponse: from .resources.app_events import AppEventsResourceWithStreamingResponse @@ -1106,6 +1138,12 @@ def agents(self) -> agents.AsyncAgentsResourceWithStreamingResponse: return AsyncAgentsResourceWithStreamingResponse(self._client.agents) + @cached_property + def assistant(self) -> assistant.AsyncAssistantResourceWithStreamingResponse: + from .resources.assistant import AsyncAssistantResourceWithStreamingResponse + + return AsyncAssistantResourceWithStreamingResponse(self._client.assistant) + @cached_property def app_events(self) -> app_events.AsyncAppEventsResourceWithStreamingResponse: from .resources.app_events import AsyncAppEventsResourceWithStreamingResponse diff --git a/src/mobilerun_sdk/resources/__init__.py b/src/mobilerun_sdk/resources/__init__.py index afef0f85..15310aae 100644 --- a/src/mobilerun_sdk/resources/__init__.py +++ b/src/mobilerun_sdk/resources/__init__.py @@ -112,6 +112,14 @@ WebhooksResourceWithStreamingResponse, AsyncWebhooksResourceWithStreamingResponse, ) +from .assistant import ( + AssistantResource, + AsyncAssistantResource, + AssistantResourceWithRawResponse, + AsyncAssistantResourceWithRawResponse, + AssistantResourceWithStreamingResponse, + AsyncAssistantResourceWithStreamingResponse, +) from .workflows import ( WorkflowsResource, AsyncWorkflowsResource, @@ -218,6 +226,12 @@ "AsyncAgentsResourceWithRawResponse", "AgentsResourceWithStreamingResponse", "AsyncAgentsResourceWithStreamingResponse", + "AssistantResource", + "AsyncAssistantResource", + "AssistantResourceWithRawResponse", + "AsyncAssistantResourceWithRawResponse", + "AssistantResourceWithStreamingResponse", + "AsyncAssistantResourceWithStreamingResponse", "AppEventsResource", "AsyncAppEventsResource", "AppEventsResourceWithRawResponse", diff --git a/src/mobilerun_sdk/resources/assistant/__init__.py b/src/mobilerun_sdk/resources/assistant/__init__.py new file mode 100644 index 00000000..86480a80 --- /dev/null +++ b/src/mobilerun_sdk/resources/assistant/__init__.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .assistant import ( + AssistantResource, + AsyncAssistantResource, + AssistantResourceWithRawResponse, + AsyncAssistantResourceWithRawResponse, + AssistantResourceWithStreamingResponse, + AsyncAssistantResourceWithStreamingResponse, +) +from .conversations import ( + ConversationsResource, + AsyncConversationsResource, + ConversationsResourceWithRawResponse, + AsyncConversationsResourceWithRawResponse, + ConversationsResourceWithStreamingResponse, + AsyncConversationsResourceWithStreamingResponse, +) + +__all__ = [ + "ConversationsResource", + "AsyncConversationsResource", + "ConversationsResourceWithRawResponse", + "AsyncConversationsResourceWithRawResponse", + "ConversationsResourceWithStreamingResponse", + "AsyncConversationsResourceWithStreamingResponse", + "AssistantResource", + "AsyncAssistantResource", + "AssistantResourceWithRawResponse", + "AsyncAssistantResourceWithRawResponse", + "AssistantResourceWithStreamingResponse", + "AsyncAssistantResourceWithStreamingResponse", +] diff --git a/src/mobilerun_sdk/resources/assistant/assistant.py b/src/mobilerun_sdk/resources/assistant/assistant.py new file mode 100644 index 00000000..b97d67bd --- /dev/null +++ b/src/mobilerun_sdk/resources/assistant/assistant.py @@ -0,0 +1,102 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from .conversations import ( + ConversationsResource, + AsyncConversationsResource, + ConversationsResourceWithRawResponse, + AsyncConversationsResourceWithRawResponse, + ConversationsResourceWithStreamingResponse, + AsyncConversationsResourceWithStreamingResponse, +) + +__all__ = ["AssistantResource", "AsyncAssistantResource"] + + +class AssistantResource(SyncAPIResource): + @cached_property + def conversations(self) -> ConversationsResource: + return ConversationsResource(self._client) + + @cached_property + def with_raw_response(self) -> AssistantResourceWithRawResponse: + """ + 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/droidrun/mobilerun-sdk-python#accessing-raw-response-data-eg-headers + """ + return AssistantResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AssistantResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/droidrun/mobilerun-sdk-python#with_streaming_response + """ + return AssistantResourceWithStreamingResponse(self) + + +class AsyncAssistantResource(AsyncAPIResource): + @cached_property + def conversations(self) -> AsyncConversationsResource: + return AsyncConversationsResource(self._client) + + @cached_property + def with_raw_response(self) -> AsyncAssistantResourceWithRawResponse: + """ + 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/droidrun/mobilerun-sdk-python#accessing-raw-response-data-eg-headers + """ + return AsyncAssistantResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncAssistantResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/droidrun/mobilerun-sdk-python#with_streaming_response + """ + return AsyncAssistantResourceWithStreamingResponse(self) + + +class AssistantResourceWithRawResponse: + def __init__(self, assistant: AssistantResource) -> None: + self._assistant = assistant + + @cached_property + def conversations(self) -> ConversationsResourceWithRawResponse: + return ConversationsResourceWithRawResponse(self._assistant.conversations) + + +class AsyncAssistantResourceWithRawResponse: + def __init__(self, assistant: AsyncAssistantResource) -> None: + self._assistant = assistant + + @cached_property + def conversations(self) -> AsyncConversationsResourceWithRawResponse: + return AsyncConversationsResourceWithRawResponse(self._assistant.conversations) + + +class AssistantResourceWithStreamingResponse: + def __init__(self, assistant: AssistantResource) -> None: + self._assistant = assistant + + @cached_property + def conversations(self) -> ConversationsResourceWithStreamingResponse: + return ConversationsResourceWithStreamingResponse(self._assistant.conversations) + + +class AsyncAssistantResourceWithStreamingResponse: + def __init__(self, assistant: AsyncAssistantResource) -> None: + self._assistant = assistant + + @cached_property + def conversations(self) -> AsyncConversationsResourceWithStreamingResponse: + return AsyncConversationsResourceWithStreamingResponse(self._assistant.conversations) diff --git a/src/mobilerun_sdk/resources/assistant/conversations.py b/src/mobilerun_sdk/resources/assistant/conversations.py new file mode 100644 index 00000000..536d58be --- /dev/null +++ b/src/mobilerun_sdk/resources/assistant/conversations.py @@ -0,0 +1,1111 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable, Optional +from typing_extensions import Literal + +import httpx + +from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ..._utils import path_template, maybe_transform, strip_not_given, 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.assistant import ( + conversation_list_params, + conversation_send_params, + conversation_abort_params, + conversation_create_params, + conversation_stream_params, + conversation_update_params, + conversation_history_params, + conversation_answer_question_params, + conversation_reject_question_params, + conversation_answer_permission_params, +) +from ...types.assistant.conversation_list_response import ConversationListResponse +from ...types.assistant.conversation_send_response import ConversationSendResponse +from ...types.assistant.conversation_abort_response import ConversationAbortResponse +from ...types.assistant.conversation_create_response import ConversationCreateResponse +from ...types.assistant.conversation_stream_response import ConversationStreamResponse +from ...types.assistant.conversation_update_response import ConversationUpdateResponse +from ...types.assistant.conversation_history_response import ConversationHistoryResponse +from ...types.assistant.conversation_answer_question_response import ConversationAnswerQuestionResponse +from ...types.assistant.conversation_reject_question_response import ConversationRejectQuestionResponse +from ...types.assistant.conversation_answer_permission_response import ConversationAnswerPermissionResponse + +__all__ = ["ConversationsResource", "AsyncConversationsResource"] + + +class ConversationsResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> ConversationsResourceWithRawResponse: + """ + 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/droidrun/mobilerun-sdk-python#accessing-raw-response-data-eg-headers + """ + return ConversationsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ConversationsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/droidrun/mobilerun-sdk-python#with_streaming_response + """ + return ConversationsResourceWithStreamingResponse(self) + + def create( + self, + *, + title: str, + agent: str | Omit = omit, + description: str | Omit = omit, + idempotency_key: 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, + ) -> ConversationCreateResponse: + """Creates a titled agent session. + + Setup may occur on the first prompt. Idempotent + via the `Idempotency-Key` header — a duplicate submit by the same authenticated + caller within the 24-hour idempotency window returns the already-created session + instead of a second one. + + Args: + idempotency_key: Optional client key. Reusing the same key with the same request body by the same + authenticated caller within 24 hours returns the already-created session instead + of a second one. + + 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 + """ + extra_headers = {**strip_not_given({"Idempotency-Key": idempotency_key}), **(extra_headers or {})} + return self._post( + "/assistant/chat/sessions", + body=maybe_transform( + { + "title": title, + "agent": agent, + "description": description, + }, + conversation_create_params.ConversationCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ConversationCreateResponse, + ) + + def update( + self, + id: str, + *, + description: Optional[str] | Omit = omit, + pinned: bool | Omit = omit, + status: Literal["active", "archived"] | Omit = omit, + title: 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, + ) -> ConversationUpdateResponse: + """Rename, change status, and/or pin. + + Title updates apply best-effort. Archiving + always clears the pinned flag. `title` is rejected with 409 + `code: "session_title_managed_by_workflow"` when this chat is bound to a + workflow — a bound chat's title is managed by the workflow. Other fields + (description, status, pinned) remain updateable; archiving a bound chat stays + allowed. + + 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._patch( + path_template("/assistant/chat/sessions/{id}", id=id), + body=maybe_transform( + { + "description": description, + "pinned": pinned, + "status": status, + "title": title, + }, + conversation_update_params.ConversationUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ConversationUpdateResponse, + ) + + def list( + self, + *, + kind: Literal["chat", "agent_workflow"] | Omit = omit, + mine: Literal["true", "false"] | Omit = omit, + workflow_id: 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, + ) -> ConversationListResponse: + """ + Default (`kind` absent or `chat`): active named chat sessions, pinned first then + most recent activity — `workflowId` must be absent, or the request 400s. + `mine=true` (only valid with `kind=chat`) narrows to sessions the caller + created. `kind=agent_workflow`: workflow-linked sessions for one workflow + (`workflowId` required, or the request 400s), no status filter, newest episode + first. + + 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 + """ + return self._get( + "/assistant/chat/sessions", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "kind": kind, + "mine": mine, + "workflow_id": workflow_id, + }, + conversation_list_params.ConversationListParams, + ), + ), + cast_to=ConversationListResponse, + ) + + def abort( + self, + *, + session_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, + ) -> ConversationAbortResponse: + """Abort the in-flight chat turn owned by `sessionId`. + + Idempotent. A turn owned by + a different session is left untouched (204). + + 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 + """ + return self._post( + "/assistant/chat/abort", + body=maybe_transform({"session_id": session_id}, conversation_abort_params.ConversationAbortParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ConversationAbortResponse, + ) + + def answer_permission( + self, + *, + permission_id: str, + response: Literal["once", "always", "reject"], + # 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, + ) -> ConversationAnswerPermissionResponse: + """ + Deliver a permission approval or rejection for an in-flight turn. + + 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 + """ + return self._post( + "/assistant/chat/permission", + body=maybe_transform( + { + "permission_id": permission_id, + "response": response, + }, + conversation_answer_permission_params.ConversationAnswerPermissionParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ConversationAnswerPermissionResponse, + ) + + def answer_question( + self, + *, + answers: Iterable[Iterable[conversation_answer_question_params.Answer]], + question_id: str, + idempotency_key: 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, + ) -> ConversationAnswerQuestionResponse: + """ + Deliver the user's answers to the pending question for an in-flight turn. + Idempotent via the `Idempotency-Key` header. + + Args: + idempotency_key: Optional client key. Reusing the same key with the same question answers + coalesces duplicate submits. + + 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 + """ + extra_headers = {**strip_not_given({"Idempotency-Key": idempotency_key}), **(extra_headers or {})} + return self._post( + "/assistant/chat/question", + body=maybe_transform( + { + "answers": answers, + "question_id": question_id, + }, + conversation_answer_question_params.ConversationAnswerQuestionParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ConversationAnswerQuestionResponse, + ) + + def history( + self, + *, + session_id: str, + limit: int | 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, + ) -> ConversationHistoryResponse: + """Return the user's chat history for the given session. + + History remains readable + after the session is no longer active. + + 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 + """ + return self._get( + "/assistant/chat/messages", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "session_id": session_id, + "limit": limit, + }, + conversation_history_params.ConversationHistoryParams, + ), + ), + cast_to=ConversationHistoryResponse, + ) + + def reject_question( + self, + *, + question_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, + ) -> ConversationRejectQuestionResponse: + """Dismiss the pending question. + + Already-resolved questions return 200 (no-op) so + multi-tab dismiss stays idempotent. + + 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 + """ + return self._post( + "/assistant/chat/question/reject", + body=maybe_transform( + {"question_id": question_id}, conversation_reject_question_params.ConversationRejectQuestionParams + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ConversationRejectQuestionResponse, + ) + + def send( + self, + *, + message: str, + session_id: str, + agent: 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, + ) -> ConversationSendResponse: + """Send a single user message. + + The response format follows the Accept header: + `text/event-stream` for SSE, `application/json` for a buffered assistant reply. + `sessionId` targets a concrete active chat. The resolved chat session ID is + returned as `chatSessionId` in the JSON body and as the `X-Chat-Session-Id` + response header on the SSE response. + + 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 + """ + return self._post( + "/assistant/chat/message", + body=maybe_transform( + { + "message": message, + "session_id": session_id, + "agent": agent, + }, + conversation_send_params.ConversationSendParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ConversationSendResponse, + ) + + def stream( + self, + *, + session_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[ConversationStreamResponse]: + """Reconnect to the in-flight turn stream. + + Replays buffered events from the start + of the active turn, then continues live until the turn finishes. Responds 204 + when no turn is active for the requested session. Resume is best-effort. Does + not start an inactive session. + + 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 + """ + extra_headers = {"Accept": "text/event-stream", **(extra_headers or {})} + return self._get( + "/assistant/chat/stream", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"session_id": session_id}, conversation_stream_params.ConversationStreamParams), + ), + cast_to=str, + stream=True, + stream_cls=Stream[ConversationStreamResponse], + ) + + +class AsyncConversationsResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncConversationsResourceWithRawResponse: + """ + 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/droidrun/mobilerun-sdk-python#accessing-raw-response-data-eg-headers + """ + return AsyncConversationsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncConversationsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/droidrun/mobilerun-sdk-python#with_streaming_response + """ + return AsyncConversationsResourceWithStreamingResponse(self) + + async def create( + self, + *, + title: str, + agent: str | Omit = omit, + description: str | Omit = omit, + idempotency_key: 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, + ) -> ConversationCreateResponse: + """Creates a titled agent session. + + Setup may occur on the first prompt. Idempotent + via the `Idempotency-Key` header — a duplicate submit by the same authenticated + caller within the 24-hour idempotency window returns the already-created session + instead of a second one. + + Args: + idempotency_key: Optional client key. Reusing the same key with the same request body by the same + authenticated caller within 24 hours returns the already-created session instead + of a second one. + + 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 + """ + extra_headers = {**strip_not_given({"Idempotency-Key": idempotency_key}), **(extra_headers or {})} + return await self._post( + "/assistant/chat/sessions", + body=await async_maybe_transform( + { + "title": title, + "agent": agent, + "description": description, + }, + conversation_create_params.ConversationCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ConversationCreateResponse, + ) + + async def update( + self, + id: str, + *, + description: Optional[str] | Omit = omit, + pinned: bool | Omit = omit, + status: Literal["active", "archived"] | Omit = omit, + title: 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, + ) -> ConversationUpdateResponse: + """Rename, change status, and/or pin. + + Title updates apply best-effort. Archiving + always clears the pinned flag. `title` is rejected with 409 + `code: "session_title_managed_by_workflow"` when this chat is bound to a + workflow — a bound chat's title is managed by the workflow. Other fields + (description, status, pinned) remain updateable; archiving a bound chat stays + allowed. + + 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._patch( + path_template("/assistant/chat/sessions/{id}", id=id), + body=await async_maybe_transform( + { + "description": description, + "pinned": pinned, + "status": status, + "title": title, + }, + conversation_update_params.ConversationUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ConversationUpdateResponse, + ) + + async def list( + self, + *, + kind: Literal["chat", "agent_workflow"] | Omit = omit, + mine: Literal["true", "false"] | Omit = omit, + workflow_id: 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, + ) -> ConversationListResponse: + """ + Default (`kind` absent or `chat`): active named chat sessions, pinned first then + most recent activity — `workflowId` must be absent, or the request 400s. + `mine=true` (only valid with `kind=chat`) narrows to sessions the caller + created. `kind=agent_workflow`: workflow-linked sessions for one workflow + (`workflowId` required, or the request 400s), no status filter, newest episode + first. + + 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 + """ + return await self._get( + "/assistant/chat/sessions", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "kind": kind, + "mine": mine, + "workflow_id": workflow_id, + }, + conversation_list_params.ConversationListParams, + ), + ), + cast_to=ConversationListResponse, + ) + + async def abort( + self, + *, + session_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, + ) -> ConversationAbortResponse: + """Abort the in-flight chat turn owned by `sessionId`. + + Idempotent. A turn owned by + a different session is left untouched (204). + + 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 + """ + return await self._post( + "/assistant/chat/abort", + body=await async_maybe_transform( + {"session_id": session_id}, conversation_abort_params.ConversationAbortParams + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ConversationAbortResponse, + ) + + async def answer_permission( + self, + *, + permission_id: str, + response: Literal["once", "always", "reject"], + # 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, + ) -> ConversationAnswerPermissionResponse: + """ + Deliver a permission approval or rejection for an in-flight turn. + + 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 + """ + return await self._post( + "/assistant/chat/permission", + body=await async_maybe_transform( + { + "permission_id": permission_id, + "response": response, + }, + conversation_answer_permission_params.ConversationAnswerPermissionParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ConversationAnswerPermissionResponse, + ) + + async def answer_question( + self, + *, + answers: Iterable[Iterable[conversation_answer_question_params.Answer]], + question_id: str, + idempotency_key: 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, + ) -> ConversationAnswerQuestionResponse: + """ + Deliver the user's answers to the pending question for an in-flight turn. + Idempotent via the `Idempotency-Key` header. + + Args: + idempotency_key: Optional client key. Reusing the same key with the same question answers + coalesces duplicate submits. + + 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 + """ + extra_headers = {**strip_not_given({"Idempotency-Key": idempotency_key}), **(extra_headers or {})} + return await self._post( + "/assistant/chat/question", + body=await async_maybe_transform( + { + "answers": answers, + "question_id": question_id, + }, + conversation_answer_question_params.ConversationAnswerQuestionParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ConversationAnswerQuestionResponse, + ) + + async def history( + self, + *, + session_id: str, + limit: int | 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, + ) -> ConversationHistoryResponse: + """Return the user's chat history for the given session. + + History remains readable + after the session is no longer active. + + 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 + """ + return await self._get( + "/assistant/chat/messages", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "session_id": session_id, + "limit": limit, + }, + conversation_history_params.ConversationHistoryParams, + ), + ), + cast_to=ConversationHistoryResponse, + ) + + async def reject_question( + self, + *, + question_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, + ) -> ConversationRejectQuestionResponse: + """Dismiss the pending question. + + Already-resolved questions return 200 (no-op) so + multi-tab dismiss stays idempotent. + + 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 + """ + return await self._post( + "/assistant/chat/question/reject", + body=await async_maybe_transform( + {"question_id": question_id}, conversation_reject_question_params.ConversationRejectQuestionParams + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ConversationRejectQuestionResponse, + ) + + async def send( + self, + *, + message: str, + session_id: str, + agent: 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, + ) -> ConversationSendResponse: + """Send a single user message. + + The response format follows the Accept header: + `text/event-stream` for SSE, `application/json` for a buffered assistant reply. + `sessionId` targets a concrete active chat. The resolved chat session ID is + returned as `chatSessionId` in the JSON body and as the `X-Chat-Session-Id` + response header on the SSE response. + + 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 + """ + return await self._post( + "/assistant/chat/message", + body=await async_maybe_transform( + { + "message": message, + "session_id": session_id, + "agent": agent, + }, + conversation_send_params.ConversationSendParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ConversationSendResponse, + ) + + async def stream( + self, + *, + session_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[ConversationStreamResponse]: + """Reconnect to the in-flight turn stream. + + Replays buffered events from the start + of the active turn, then continues live until the turn finishes. Responds 204 + when no turn is active for the requested session. Resume is best-effort. Does + not start an inactive session. + + 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 + """ + extra_headers = {"Accept": "text/event-stream", **(extra_headers or {})} + return await self._get( + "/assistant/chat/stream", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + {"session_id": session_id}, conversation_stream_params.ConversationStreamParams + ), + ), + cast_to=str, + stream=True, + stream_cls=AsyncStream[ConversationStreamResponse], + ) + + +class ConversationsResourceWithRawResponse: + def __init__(self, conversations: ConversationsResource) -> None: + self._conversations = conversations + + self.create = to_raw_response_wrapper( + conversations.create, + ) + self.update = to_raw_response_wrapper( + conversations.update, + ) + self.list = to_raw_response_wrapper( + conversations.list, + ) + self.abort = to_raw_response_wrapper( + conversations.abort, + ) + self.answer_permission = to_raw_response_wrapper( + conversations.answer_permission, + ) + self.answer_question = to_raw_response_wrapper( + conversations.answer_question, + ) + self.history = to_raw_response_wrapper( + conversations.history, + ) + self.reject_question = to_raw_response_wrapper( + conversations.reject_question, + ) + self.send = to_raw_response_wrapper( + conversations.send, + ) + self.stream = to_raw_response_wrapper( + conversations.stream, + ) + + +class AsyncConversationsResourceWithRawResponse: + def __init__(self, conversations: AsyncConversationsResource) -> None: + self._conversations = conversations + + self.create = async_to_raw_response_wrapper( + conversations.create, + ) + self.update = async_to_raw_response_wrapper( + conversations.update, + ) + self.list = async_to_raw_response_wrapper( + conversations.list, + ) + self.abort = async_to_raw_response_wrapper( + conversations.abort, + ) + self.answer_permission = async_to_raw_response_wrapper( + conversations.answer_permission, + ) + self.answer_question = async_to_raw_response_wrapper( + conversations.answer_question, + ) + self.history = async_to_raw_response_wrapper( + conversations.history, + ) + self.reject_question = async_to_raw_response_wrapper( + conversations.reject_question, + ) + self.send = async_to_raw_response_wrapper( + conversations.send, + ) + self.stream = async_to_raw_response_wrapper( + conversations.stream, + ) + + +class ConversationsResourceWithStreamingResponse: + def __init__(self, conversations: ConversationsResource) -> None: + self._conversations = conversations + + self.create = to_streamed_response_wrapper( + conversations.create, + ) + self.update = to_streamed_response_wrapper( + conversations.update, + ) + self.list = to_streamed_response_wrapper( + conversations.list, + ) + self.abort = to_streamed_response_wrapper( + conversations.abort, + ) + self.answer_permission = to_streamed_response_wrapper( + conversations.answer_permission, + ) + self.answer_question = to_streamed_response_wrapper( + conversations.answer_question, + ) + self.history = to_streamed_response_wrapper( + conversations.history, + ) + self.reject_question = to_streamed_response_wrapper( + conversations.reject_question, + ) + self.send = to_streamed_response_wrapper( + conversations.send, + ) + self.stream = to_streamed_response_wrapper( + conversations.stream, + ) + + +class AsyncConversationsResourceWithStreamingResponse: + def __init__(self, conversations: AsyncConversationsResource) -> None: + self._conversations = conversations + + self.create = async_to_streamed_response_wrapper( + conversations.create, + ) + self.update = async_to_streamed_response_wrapper( + conversations.update, + ) + self.list = async_to_streamed_response_wrapper( + conversations.list, + ) + self.abort = async_to_streamed_response_wrapper( + conversations.abort, + ) + self.answer_permission = async_to_streamed_response_wrapper( + conversations.answer_permission, + ) + self.answer_question = async_to_streamed_response_wrapper( + conversations.answer_question, + ) + self.history = async_to_streamed_response_wrapper( + conversations.history, + ) + self.reject_question = async_to_streamed_response_wrapper( + conversations.reject_question, + ) + self.send = async_to_streamed_response_wrapper( + conversations.send, + ) + self.stream = async_to_streamed_response_wrapper( + conversations.stream, + ) diff --git a/src/mobilerun_sdk/types/assistant/__init__.py b/src/mobilerun_sdk/types/assistant/__init__.py new file mode 100644 index 00000000..292b0d5d --- /dev/null +++ b/src/mobilerun_sdk/types/assistant/__init__.py @@ -0,0 +1,32 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .conversation_list_params import ConversationListParams as ConversationListParams +from .conversation_send_params import ConversationSendParams as ConversationSendParams +from .conversation_abort_params import ConversationAbortParams as ConversationAbortParams +from .conversation_create_params import ConversationCreateParams as ConversationCreateParams +from .conversation_list_response import ConversationListResponse as ConversationListResponse +from .conversation_send_response import ConversationSendResponse as ConversationSendResponse +from .conversation_stream_params import ConversationStreamParams as ConversationStreamParams +from .conversation_update_params import ConversationUpdateParams as ConversationUpdateParams +from .conversation_abort_response import ConversationAbortResponse as ConversationAbortResponse +from .conversation_history_params import ConversationHistoryParams as ConversationHistoryParams +from .conversation_create_response import ConversationCreateResponse as ConversationCreateResponse +from .conversation_stream_response import ConversationStreamResponse as ConversationStreamResponse +from .conversation_update_response import ConversationUpdateResponse as ConversationUpdateResponse +from .conversation_history_response import ConversationHistoryResponse as ConversationHistoryResponse +from .conversation_answer_question_params import ConversationAnswerQuestionParams as ConversationAnswerQuestionParams +from .conversation_reject_question_params import ConversationRejectQuestionParams as ConversationRejectQuestionParams +from .conversation_answer_permission_params import ( + ConversationAnswerPermissionParams as ConversationAnswerPermissionParams, +) +from .conversation_answer_question_response import ( + ConversationAnswerQuestionResponse as ConversationAnswerQuestionResponse, +) +from .conversation_reject_question_response import ( + ConversationRejectQuestionResponse as ConversationRejectQuestionResponse, +) +from .conversation_answer_permission_response import ( + ConversationAnswerPermissionResponse as ConversationAnswerPermissionResponse, +) diff --git a/src/mobilerun_sdk/types/assistant/conversation_abort_params.py b/src/mobilerun_sdk/types/assistant/conversation_abort_params.py new file mode 100644 index 00000000..ced9a891 --- /dev/null +++ b/src/mobilerun_sdk/types/assistant/conversation_abort_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_extensions import Required, Annotated, TypedDict + +from ..._utils import PropertyInfo + +__all__ = ["ConversationAbortParams"] + + +class ConversationAbortParams(TypedDict, total=False): + session_id: Required[Annotated[str, PropertyInfo(alias="sessionId")]] diff --git a/src/mobilerun_sdk/types/assistant/conversation_abort_response.py b/src/mobilerun_sdk/types/assistant/conversation_abort_response.py new file mode 100644 index 00000000..179518b5 --- /dev/null +++ b/src/mobilerun_sdk/types/assistant/conversation_abort_response.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ConversationAbortResponse"] + + +class ConversationAbortResponse(BaseModel): + ok: Literal[True] diff --git a/src/mobilerun_sdk/types/assistant/conversation_answer_permission_params.py b/src/mobilerun_sdk/types/assistant/conversation_answer_permission_params.py new file mode 100644 index 00000000..038e0123 --- /dev/null +++ b/src/mobilerun_sdk/types/assistant/conversation_answer_permission_params.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, Annotated, TypedDict + +from ..._utils import PropertyInfo + +__all__ = ["ConversationAnswerPermissionParams"] + + +class ConversationAnswerPermissionParams(TypedDict, total=False): + permission_id: Required[Annotated[str, PropertyInfo(alias="permissionId")]] + + response: Required[Literal["once", "always", "reject"]] diff --git a/src/mobilerun_sdk/types/assistant/conversation_answer_permission_response.py b/src/mobilerun_sdk/types/assistant/conversation_answer_permission_response.py new file mode 100644 index 00000000..7830fdac --- /dev/null +++ b/src/mobilerun_sdk/types/assistant/conversation_answer_permission_response.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ConversationAnswerPermissionResponse"] + + +class ConversationAnswerPermissionResponse(BaseModel): + ok: Literal[True] diff --git a/src/mobilerun_sdk/types/assistant/conversation_answer_question_params.py b/src/mobilerun_sdk/types/assistant/conversation_answer_question_params.py new file mode 100644 index 00000000..854a99b4 --- /dev/null +++ b/src/mobilerun_sdk/types/assistant/conversation_answer_question_params.py @@ -0,0 +1,39 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable +from typing_extensions import Required, Annotated, TypeAlias, TypedDict + +from ..._utils import PropertyInfo + +__all__ = ["ConversationAnswerQuestionParams", "Answer", "AnswerLabel", "AnswerCustom", "AnswerUnionMember2"] + + +class ConversationAnswerQuestionParams(TypedDict, total=False): + answers: Required[Iterable[Iterable[Answer]]] + + question_id: Required[Annotated[str, PropertyInfo(alias="questionId")]] + + idempotency_key: Annotated[str, PropertyInfo(alias="Idempotency-Key")] + """Optional client key. + + Reusing the same key with the same question answers coalesces duplicate submits. + """ + + +class AnswerLabel(TypedDict, total=False): + label: Required[str] + + +class AnswerCustom(TypedDict, total=False): + custom: Required[str] + + +class AnswerUnionMember2(TypedDict, total=False): + custom: Required[str] + + label: Required[str] + + +Answer: TypeAlias = Union[AnswerLabel, AnswerCustom, AnswerUnionMember2] diff --git a/src/mobilerun_sdk/types/assistant/conversation_answer_question_response.py b/src/mobilerun_sdk/types/assistant/conversation_answer_question_response.py new file mode 100644 index 00000000..404041c9 --- /dev/null +++ b/src/mobilerun_sdk/types/assistant/conversation_answer_question_response.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ConversationAnswerQuestionResponse"] + + +class ConversationAnswerQuestionResponse(BaseModel): + ok: Literal[True] diff --git a/src/mobilerun_sdk/types/assistant/conversation_create_params.py b/src/mobilerun_sdk/types/assistant/conversation_create_params.py new file mode 100644 index 00000000..96755aa4 --- /dev/null +++ b/src/mobilerun_sdk/types/assistant/conversation_create_params.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, Annotated, TypedDict + +from ..._utils import PropertyInfo + +__all__ = ["ConversationCreateParams"] + + +class ConversationCreateParams(TypedDict, total=False): + title: Required[str] + + agent: str + + description: str + + idempotency_key: Annotated[str, PropertyInfo(alias="Idempotency-Key")] + """Optional client key. + + Reusing the same key with the same request body by the same authenticated caller + within 24 hours returns the already-created session instead of a second one. + """ diff --git a/src/mobilerun_sdk/types/assistant/conversation_create_response.py b/src/mobilerun_sdk/types/assistant/conversation_create_response.py new file mode 100644 index 00000000..34341512 --- /dev/null +++ b/src/mobilerun_sdk/types/assistant/conversation_create_response.py @@ -0,0 +1,50 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from ..._models import BaseModel + +__all__ = ["ConversationCreateResponse", "Session"] + + +class Session(BaseModel): + id: str + + agent: Optional[str] = None + + base_prompt_tokens: Optional[float] = FieldInfo(alias="basePromptTokens", default=None) + + context_exhausted_at: Optional[str] = FieldInfo(alias="contextExhaustedAt", default=None) + + cost_cents: float = FieldInfo(alias="costCents") + + cost_usd: float = FieldInfo(alias="costUsd") + + created_at: str = FieldInfo(alias="createdAt") + + description: Optional[str] = None + + last_active_at: str = FieldInfo(alias="lastActiveAt") + + peak_prompt_tokens: Optional[float] = FieldInfo(alias="peakPromptTokens", default=None) + + pinned: bool + + prompt_status: Literal["ready", "machine_replaced"] = FieldInfo(alias="promptStatus") + + status: str + + title: str + + turn_active: bool = FieldInfo(alias="turnActive") + + turn_started_at: Optional[str] = FieldInfo(alias="turnStartedAt", default=None) + + created_by: Optional[str] = FieldInfo(alias="createdBy", default=None) + + +class ConversationCreateResponse(BaseModel): + session: Session diff --git a/src/mobilerun_sdk/types/assistant/conversation_history_params.py b/src/mobilerun_sdk/types/assistant/conversation_history_params.py new file mode 100644 index 00000000..ecf0dbe0 --- /dev/null +++ b/src/mobilerun_sdk/types/assistant/conversation_history_params.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, Annotated, TypedDict + +from ..._utils import PropertyInfo + +__all__ = ["ConversationHistoryParams"] + + +class ConversationHistoryParams(TypedDict, total=False): + session_id: Required[Annotated[str, PropertyInfo(alias="sessionId")]] + + limit: int diff --git a/src/mobilerun_sdk/types/assistant/conversation_history_response.py b/src/mobilerun_sdk/types/assistant/conversation_history_response.py new file mode 100644 index 00000000..56c14a09 --- /dev/null +++ b/src/mobilerun_sdk/types/assistant/conversation_history_response.py @@ -0,0 +1,68 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import TYPE_CHECKING, Dict, List, Optional +from datetime import datetime +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from ..._models import BaseModel + +__all__ = ["ConversationHistoryResponse", "Message", "MessagePart", "MessageMetadata"] + + +class MessagePart(BaseModel): + type: str + + if TYPE_CHECKING: + # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a + # value to this field, so for compatibility we avoid doing it at runtime. + __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride] + + # Stub to indicate that arbitrary properties are accepted. + # To access properties that are not valid identifiers you can use `getattr`, e.g. + # `getattr(obj, '$type')` + def __getattr__(self, attr: str) -> object: ... + else: + __pydantic_extra__: Dict[str, object] + + +class MessageMetadata(BaseModel): + agent: Optional[str] = None + + agent_message_id: Optional[str] = FieldInfo(alias="agentMessageId", default=None) + + agent_session_id: Optional[str] = FieldInfo(alias="agentSessionId", default=None) + + turn_anchor_message_id: Optional[str] = FieldInfo(alias="turnAnchorMessageId", default=None) + + +class Message(BaseModel): + id: str + + parts: List[MessagePart] + + role: Literal["user", "assistant", "system"] + + created_at: Optional[datetime] = FieldInfo(alias="createdAt", default=None) + + created_by: Optional[str] = FieldInfo(alias="createdBy", default=None) + + feedback: Optional[bool] = None + + metadata: Optional[MessageMetadata] = None + + source: Optional[Literal["cloud", "telegram", "api", "workflow"]] = None + + synthetic: Optional[bool] = None + + user_id: Optional[str] = FieldInfo(alias="userId", default=None) + """Deprecated: use createdBy.""" + + +class ConversationHistoryResponse(BaseModel): + messages: List[Message] + + turn_active: bool = FieldInfo(alias="turnActive") + + truncated: Optional[bool] = None diff --git a/src/mobilerun_sdk/types/assistant/conversation_list_params.py b/src/mobilerun_sdk/types/assistant/conversation_list_params.py new file mode 100644 index 00000000..616be183 --- /dev/null +++ b/src/mobilerun_sdk/types/assistant/conversation_list_params.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Annotated, TypedDict + +from ..._utils import PropertyInfo + +__all__ = ["ConversationListParams"] + + +class ConversationListParams(TypedDict, total=False): + kind: Literal["chat", "agent_workflow"] + + mine: Literal["true", "false"] + + workflow_id: Annotated[str, PropertyInfo(alias="workflowId")] diff --git a/src/mobilerun_sdk/types/assistant/conversation_list_response.py b/src/mobilerun_sdk/types/assistant/conversation_list_response.py new file mode 100644 index 00000000..3f3ba16a --- /dev/null +++ b/src/mobilerun_sdk/types/assistant/conversation_list_response.py @@ -0,0 +1,105 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, TypeAlias + +from pydantic import Field as FieldInfo + +from ..._models import BaseModel + +__all__ = ["ConversationListResponse", "Session", "SessionUnionMember0", "SessionUnionMember1"] + + +class SessionUnionMember0(BaseModel): + id: str + + agent: Optional[str] = None + + base_prompt_tokens: Optional[float] = FieldInfo(alias="basePromptTokens", default=None) + + context_exhausted_at: Optional[str] = FieldInfo(alias="contextExhaustedAt", default=None) + + cost_cents: float = FieldInfo(alias="costCents") + + cost_usd: float = FieldInfo(alias="costUsd") + + created_at: str = FieldInfo(alias="createdAt") + + description: Optional[str] = None + + episode: int + + kind: Literal["chat"] + + last_active_at: str = FieldInfo(alias="lastActiveAt") + + peak_prompt_tokens: Optional[float] = FieldInfo(alias="peakPromptTokens", default=None) + + pinned: bool + + prompt_status: Literal["ready", "machine_replaced"] = FieldInfo(alias="promptStatus") + + source_execution_id: None = FieldInfo(alias="sourceExecutionId", default=None) + + status: str + + title: str + + turn_active: bool = FieldInfo(alias="turnActive") + + turn_started_at: Optional[str] = FieldInfo(alias="turnStartedAt", default=None) + + workflow_id: None = FieldInfo(alias="workflowId", default=None) + + created_by: Optional[str] = FieldInfo(alias="createdBy", default=None) + + +class SessionUnionMember1(BaseModel): + id: str + + agent: Optional[str] = None + + base_prompt_tokens: Optional[float] = FieldInfo(alias="basePromptTokens", default=None) + + context_exhausted_at: Optional[str] = FieldInfo(alias="contextExhaustedAt", default=None) + + cost_cents: float = FieldInfo(alias="costCents") + + cost_usd: float = FieldInfo(alias="costUsd") + + created_at: str = FieldInfo(alias="createdAt") + + description: Optional[str] = None + + episode: int + + kind: Literal["agent_workflow"] + + last_active_at: str = FieldInfo(alias="lastActiveAt") + + peak_prompt_tokens: Optional[float] = FieldInfo(alias="peakPromptTokens", default=None) + + pinned: bool + + prompt_status: Literal["ready", "machine_replaced"] = FieldInfo(alias="promptStatus") + + source_execution_id: str = FieldInfo(alias="sourceExecutionId") + + status: str + + title: str + + turn_active: bool = FieldInfo(alias="turnActive") + + turn_started_at: Optional[str] = FieldInfo(alias="turnStartedAt", default=None) + + workflow_id: str = FieldInfo(alias="workflowId") + + created_by: Optional[str] = FieldInfo(alias="createdBy", default=None) + + +Session: TypeAlias = Union[SessionUnionMember0, SessionUnionMember1] + + +class ConversationListResponse(BaseModel): + sessions: List[Session] diff --git a/src/mobilerun_sdk/types/assistant/conversation_reject_question_params.py b/src/mobilerun_sdk/types/assistant/conversation_reject_question_params.py new file mode 100644 index 00000000..32db68e7 --- /dev/null +++ b/src/mobilerun_sdk/types/assistant/conversation_reject_question_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_extensions import Required, Annotated, TypedDict + +from ..._utils import PropertyInfo + +__all__ = ["ConversationRejectQuestionParams"] + + +class ConversationRejectQuestionParams(TypedDict, total=False): + question_id: Required[Annotated[str, PropertyInfo(alias="questionId")]] diff --git a/src/mobilerun_sdk/types/assistant/conversation_reject_question_response.py b/src/mobilerun_sdk/types/assistant/conversation_reject_question_response.py new file mode 100644 index 00000000..b6e30890 --- /dev/null +++ b/src/mobilerun_sdk/types/assistant/conversation_reject_question_response.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ConversationRejectQuestionResponse"] + + +class ConversationRejectQuestionResponse(BaseModel): + ok: Literal[True] diff --git a/src/mobilerun_sdk/types/assistant/conversation_send_params.py b/src/mobilerun_sdk/types/assistant/conversation_send_params.py new file mode 100644 index 00000000..60413764 --- /dev/null +++ b/src/mobilerun_sdk/types/assistant/conversation_send_params.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, Annotated, TypedDict + +from ..._utils import PropertyInfo + +__all__ = ["ConversationSendParams"] + + +class ConversationSendParams(TypedDict, total=False): + message: Required[str] + + session_id: Required[Annotated[str, PropertyInfo(alias="sessionId")]] + + agent: str diff --git a/src/mobilerun_sdk/types/assistant/conversation_send_response.py b/src/mobilerun_sdk/types/assistant/conversation_send_response.py new file mode 100644 index 00000000..7b558a5f --- /dev/null +++ b/src/mobilerun_sdk/types/assistant/conversation_send_response.py @@ -0,0 +1,17 @@ +# 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__ = ["ConversationSendResponse"] + + +class ConversationSendResponse(BaseModel): + assistant_text: str = FieldInfo(alias="assistantText") + + chat_session_id: str = FieldInfo(alias="chatSessionId") + + error_text: Optional[str] = FieldInfo(alias="errorText", default=None) diff --git a/src/mobilerun_sdk/types/assistant/conversation_stream_params.py b/src/mobilerun_sdk/types/assistant/conversation_stream_params.py new file mode 100644 index 00000000..ccd2c6c6 --- /dev/null +++ b/src/mobilerun_sdk/types/assistant/conversation_stream_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_extensions import Required, Annotated, TypedDict + +from ..._utils import PropertyInfo + +__all__ = ["ConversationStreamParams"] + + +class ConversationStreamParams(TypedDict, total=False): + session_id: Required[Annotated[str, PropertyInfo(alias="sessionId")]] diff --git a/src/mobilerun_sdk/types/assistant/conversation_stream_response.py b/src/mobilerun_sdk/types/assistant/conversation_stream_response.py new file mode 100644 index 00000000..8e8f2698 --- /dev/null +++ b/src/mobilerun_sdk/types/assistant/conversation_stream_response.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import TypeAlias + +__all__ = ["ConversationStreamResponse"] + +ConversationStreamResponse: TypeAlias = str diff --git a/src/mobilerun_sdk/types/assistant/conversation_update_params.py b/src/mobilerun_sdk/types/assistant/conversation_update_params.py new file mode 100644 index 00000000..3acb4ee9 --- /dev/null +++ b/src/mobilerun_sdk/types/assistant/conversation_update_params.py @@ -0,0 +1,18 @@ +# 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, TypedDict + +__all__ = ["ConversationUpdateParams"] + + +class ConversationUpdateParams(TypedDict, total=False): + description: Optional[str] + + pinned: bool + + status: Literal["active", "archived"] + + title: str diff --git a/src/mobilerun_sdk/types/assistant/conversation_update_response.py b/src/mobilerun_sdk/types/assistant/conversation_update_response.py new file mode 100644 index 00000000..514fad09 --- /dev/null +++ b/src/mobilerun_sdk/types/assistant/conversation_update_response.py @@ -0,0 +1,50 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from ..._models import BaseModel + +__all__ = ["ConversationUpdateResponse", "Session"] + + +class Session(BaseModel): + id: str + + agent: Optional[str] = None + + base_prompt_tokens: Optional[float] = FieldInfo(alias="basePromptTokens", default=None) + + context_exhausted_at: Optional[str] = FieldInfo(alias="contextExhaustedAt", default=None) + + cost_cents: float = FieldInfo(alias="costCents") + + cost_usd: float = FieldInfo(alias="costUsd") + + created_at: str = FieldInfo(alias="createdAt") + + description: Optional[str] = None + + last_active_at: str = FieldInfo(alias="lastActiveAt") + + peak_prompt_tokens: Optional[float] = FieldInfo(alias="peakPromptTokens", default=None) + + pinned: bool + + prompt_status: Literal["ready", "machine_replaced"] = FieldInfo(alias="promptStatus") + + status: str + + title: str + + turn_active: bool = FieldInfo(alias="turnActive") + + turn_started_at: Optional[str] = FieldInfo(alias="turnStartedAt", default=None) + + created_by: Optional[str] = FieldInfo(alias="createdBy", default=None) + + +class ConversationUpdateResponse(BaseModel): + session: Session diff --git a/tests/api_resources/assistant/__init__.py b/tests/api_resources/assistant/__init__.py new file mode 100644 index 00000000..fd8019a9 --- /dev/null +++ b/tests/api_resources/assistant/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/assistant/test_conversations.py b/tests/api_resources/assistant/test_conversations.py new file mode 100644 index 00000000..56def693 --- /dev/null +++ b/tests/api_resources/assistant/test_conversations.py @@ -0,0 +1,858 @@ +# 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 mobilerun_sdk import Mobilerun, AsyncMobilerun +from mobilerun_sdk.types.assistant import ( + ConversationListResponse, + ConversationSendResponse, + ConversationAbortResponse, + ConversationCreateResponse, + ConversationUpdateResponse, + ConversationHistoryResponse, + ConversationAnswerQuestionResponse, + ConversationRejectQuestionResponse, + ConversationAnswerPermissionResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestConversations: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_create(self, client: Mobilerun) -> None: + conversation = client.assistant.conversations.create( + title="x", + ) + assert_matches_type(ConversationCreateResponse, conversation, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_create_with_all_params(self, client: Mobilerun) -> None: + conversation = client.assistant.conversations.create( + title="x", + agent="x", + description="description", + idempotency_key="Idempotency-Key", + ) + assert_matches_type(ConversationCreateResponse, conversation, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_raw_response_create(self, client: Mobilerun) -> None: + response = client.assistant.conversations.with_raw_response.create( + title="x", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + conversation = response.parse() + assert_matches_type(ConversationCreateResponse, conversation, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_streaming_response_create(self, client: Mobilerun) -> None: + with client.assistant.conversations.with_streaming_response.create( + title="x", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + conversation = response.parse() + assert_matches_type(ConversationCreateResponse, conversation, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_update(self, client: Mobilerun) -> None: + conversation = client.assistant.conversations.update( + id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(ConversationUpdateResponse, conversation, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_update_with_all_params(self, client: Mobilerun) -> None: + conversation = client.assistant.conversations.update( + id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + description="description", + pinned=True, + status="active", + title="x", + ) + assert_matches_type(ConversationUpdateResponse, conversation, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_raw_response_update(self, client: Mobilerun) -> None: + response = client.assistant.conversations.with_raw_response.update( + id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + conversation = response.parse() + assert_matches_type(ConversationUpdateResponse, conversation, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_streaming_response_update(self, client: Mobilerun) -> None: + with client.assistant.conversations.with_streaming_response.update( + id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + conversation = response.parse() + assert_matches_type(ConversationUpdateResponse, conversation, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_path_params_update(self, client: Mobilerun) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.assistant.conversations.with_raw_response.update( + id="", + ) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_list(self, client: Mobilerun) -> None: + conversation = client.assistant.conversations.list() + assert_matches_type(ConversationListResponse, conversation, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_list_with_all_params(self, client: Mobilerun) -> None: + conversation = client.assistant.conversations.list( + kind="chat", + mine="true", + workflow_id="x", + ) + assert_matches_type(ConversationListResponse, conversation, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_raw_response_list(self, client: Mobilerun) -> None: + response = client.assistant.conversations.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + conversation = response.parse() + assert_matches_type(ConversationListResponse, conversation, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_streaming_response_list(self, client: Mobilerun) -> None: + with client.assistant.conversations.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + conversation = response.parse() + assert_matches_type(ConversationListResponse, conversation, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_abort(self, client: Mobilerun) -> None: + conversation = client.assistant.conversations.abort( + session_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(ConversationAbortResponse, conversation, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_raw_response_abort(self, client: Mobilerun) -> None: + response = client.assistant.conversations.with_raw_response.abort( + session_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + conversation = response.parse() + assert_matches_type(ConversationAbortResponse, conversation, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_streaming_response_abort(self, client: Mobilerun) -> None: + with client.assistant.conversations.with_streaming_response.abort( + session_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + conversation = response.parse() + assert_matches_type(ConversationAbortResponse, conversation, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_answer_permission(self, client: Mobilerun) -> None: + conversation = client.assistant.conversations.answer_permission( + permission_id="x", + response="once", + ) + assert_matches_type(ConversationAnswerPermissionResponse, conversation, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_raw_response_answer_permission(self, client: Mobilerun) -> None: + response = client.assistant.conversations.with_raw_response.answer_permission( + permission_id="x", + response="once", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + conversation = response.parse() + assert_matches_type(ConversationAnswerPermissionResponse, conversation, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_streaming_response_answer_permission(self, client: Mobilerun) -> None: + with client.assistant.conversations.with_streaming_response.answer_permission( + permission_id="x", + response="once", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + conversation = response.parse() + assert_matches_type(ConversationAnswerPermissionResponse, conversation, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_answer_question(self, client: Mobilerun) -> None: + conversation = client.assistant.conversations.answer_question( + answers=[[{"label": "x"}]], + question_id="x", + ) + assert_matches_type(ConversationAnswerQuestionResponse, conversation, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_answer_question_with_all_params(self, client: Mobilerun) -> None: + conversation = client.assistant.conversations.answer_question( + answers=[[{"label": "x"}]], + question_id="x", + idempotency_key="Idempotency-Key", + ) + assert_matches_type(ConversationAnswerQuestionResponse, conversation, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_raw_response_answer_question(self, client: Mobilerun) -> None: + response = client.assistant.conversations.with_raw_response.answer_question( + answers=[[{"label": "x"}]], + question_id="x", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + conversation = response.parse() + assert_matches_type(ConversationAnswerQuestionResponse, conversation, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_streaming_response_answer_question(self, client: Mobilerun) -> None: + with client.assistant.conversations.with_streaming_response.answer_question( + answers=[[{"label": "x"}]], + question_id="x", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + conversation = response.parse() + assert_matches_type(ConversationAnswerQuestionResponse, conversation, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_history(self, client: Mobilerun) -> None: + conversation = client.assistant.conversations.history( + session_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(ConversationHistoryResponse, conversation, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_history_with_all_params(self, client: Mobilerun) -> None: + conversation = client.assistant.conversations.history( + session_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + limit=1, + ) + assert_matches_type(ConversationHistoryResponse, conversation, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_raw_response_history(self, client: Mobilerun) -> None: + response = client.assistant.conversations.with_raw_response.history( + session_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + conversation = response.parse() + assert_matches_type(ConversationHistoryResponse, conversation, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_streaming_response_history(self, client: Mobilerun) -> None: + with client.assistant.conversations.with_streaming_response.history( + session_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + conversation = response.parse() + assert_matches_type(ConversationHistoryResponse, conversation, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_reject_question(self, client: Mobilerun) -> None: + conversation = client.assistant.conversations.reject_question( + question_id="x", + ) + assert_matches_type(ConversationRejectQuestionResponse, conversation, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_raw_response_reject_question(self, client: Mobilerun) -> None: + response = client.assistant.conversations.with_raw_response.reject_question( + question_id="x", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + conversation = response.parse() + assert_matches_type(ConversationRejectQuestionResponse, conversation, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_streaming_response_reject_question(self, client: Mobilerun) -> None: + with client.assistant.conversations.with_streaming_response.reject_question( + question_id="x", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + conversation = response.parse() + assert_matches_type(ConversationRejectQuestionResponse, conversation, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_send(self, client: Mobilerun) -> None: + conversation = client.assistant.conversations.send( + message="x", + session_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(ConversationSendResponse, conversation, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_send_with_all_params(self, client: Mobilerun) -> None: + conversation = client.assistant.conversations.send( + message="x", + session_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + agent="agent", + ) + assert_matches_type(ConversationSendResponse, conversation, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_raw_response_send(self, client: Mobilerun) -> None: + response = client.assistant.conversations.with_raw_response.send( + message="x", + session_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + conversation = response.parse() + assert_matches_type(ConversationSendResponse, conversation, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_streaming_response_send(self, client: Mobilerun) -> None: + with client.assistant.conversations.with_streaming_response.send( + message="x", + session_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + conversation = response.parse() + assert_matches_type(ConversationSendResponse, conversation, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_stream(self, client: Mobilerun) -> None: + conversation_stream = client.assistant.conversations.stream( + session_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + conversation_stream.response.close() + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_raw_response_stream(self, client: Mobilerun) -> None: + response = client.assistant.conversations.with_raw_response.stream( + session_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + stream = response.parse() + stream.close() + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_streaming_response_stream(self, client: Mobilerun) -> None: + with client.assistant.conversations.with_streaming_response.stream( + session_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) 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 + + +class TestAsyncConversations: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_create(self, async_client: AsyncMobilerun) -> None: + conversation = await async_client.assistant.conversations.create( + title="x", + ) + assert_matches_type(ConversationCreateResponse, conversation, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncMobilerun) -> None: + conversation = await async_client.assistant.conversations.create( + title="x", + agent="x", + description="description", + idempotency_key="Idempotency-Key", + ) + assert_matches_type(ConversationCreateResponse, conversation, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_raw_response_create(self, async_client: AsyncMobilerun) -> None: + response = await async_client.assistant.conversations.with_raw_response.create( + title="x", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + conversation = await response.parse() + assert_matches_type(ConversationCreateResponse, conversation, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_streaming_response_create(self, async_client: AsyncMobilerun) -> None: + async with async_client.assistant.conversations.with_streaming_response.create( + title="x", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + conversation = await response.parse() + assert_matches_type(ConversationCreateResponse, conversation, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_update(self, async_client: AsyncMobilerun) -> None: + conversation = await async_client.assistant.conversations.update( + id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(ConversationUpdateResponse, conversation, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_update_with_all_params(self, async_client: AsyncMobilerun) -> None: + conversation = await async_client.assistant.conversations.update( + id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + description="description", + pinned=True, + status="active", + title="x", + ) + assert_matches_type(ConversationUpdateResponse, conversation, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_raw_response_update(self, async_client: AsyncMobilerun) -> None: + response = await async_client.assistant.conversations.with_raw_response.update( + id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + conversation = await response.parse() + assert_matches_type(ConversationUpdateResponse, conversation, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_streaming_response_update(self, async_client: AsyncMobilerun) -> None: + async with async_client.assistant.conversations.with_streaming_response.update( + id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + conversation = await response.parse() + assert_matches_type(ConversationUpdateResponse, conversation, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_path_params_update(self, async_client: AsyncMobilerun) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.assistant.conversations.with_raw_response.update( + id="", + ) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_list(self, async_client: AsyncMobilerun) -> None: + conversation = await async_client.assistant.conversations.list() + assert_matches_type(ConversationListResponse, conversation, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncMobilerun) -> None: + conversation = await async_client.assistant.conversations.list( + kind="chat", + mine="true", + workflow_id="x", + ) + assert_matches_type(ConversationListResponse, conversation, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_raw_response_list(self, async_client: AsyncMobilerun) -> None: + response = await async_client.assistant.conversations.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + conversation = await response.parse() + assert_matches_type(ConversationListResponse, conversation, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_streaming_response_list(self, async_client: AsyncMobilerun) -> None: + async with async_client.assistant.conversations.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + conversation = await response.parse() + assert_matches_type(ConversationListResponse, conversation, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_abort(self, async_client: AsyncMobilerun) -> None: + conversation = await async_client.assistant.conversations.abort( + session_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(ConversationAbortResponse, conversation, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_raw_response_abort(self, async_client: AsyncMobilerun) -> None: + response = await async_client.assistant.conversations.with_raw_response.abort( + session_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + conversation = await response.parse() + assert_matches_type(ConversationAbortResponse, conversation, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_streaming_response_abort(self, async_client: AsyncMobilerun) -> None: + async with async_client.assistant.conversations.with_streaming_response.abort( + session_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + conversation = await response.parse() + assert_matches_type(ConversationAbortResponse, conversation, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_answer_permission(self, async_client: AsyncMobilerun) -> None: + conversation = await async_client.assistant.conversations.answer_permission( + permission_id="x", + response="once", + ) + assert_matches_type(ConversationAnswerPermissionResponse, conversation, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_raw_response_answer_permission(self, async_client: AsyncMobilerun) -> None: + response = await async_client.assistant.conversations.with_raw_response.answer_permission( + permission_id="x", + response="once", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + conversation = await response.parse() + assert_matches_type(ConversationAnswerPermissionResponse, conversation, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_streaming_response_answer_permission(self, async_client: AsyncMobilerun) -> None: + async with async_client.assistant.conversations.with_streaming_response.answer_permission( + permission_id="x", + response="once", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + conversation = await response.parse() + assert_matches_type(ConversationAnswerPermissionResponse, conversation, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_answer_question(self, async_client: AsyncMobilerun) -> None: + conversation = await async_client.assistant.conversations.answer_question( + answers=[[{"label": "x"}]], + question_id="x", + ) + assert_matches_type(ConversationAnswerQuestionResponse, conversation, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_answer_question_with_all_params(self, async_client: AsyncMobilerun) -> None: + conversation = await async_client.assistant.conversations.answer_question( + answers=[[{"label": "x"}]], + question_id="x", + idempotency_key="Idempotency-Key", + ) + assert_matches_type(ConversationAnswerQuestionResponse, conversation, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_raw_response_answer_question(self, async_client: AsyncMobilerun) -> None: + response = await async_client.assistant.conversations.with_raw_response.answer_question( + answers=[[{"label": "x"}]], + question_id="x", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + conversation = await response.parse() + assert_matches_type(ConversationAnswerQuestionResponse, conversation, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_streaming_response_answer_question(self, async_client: AsyncMobilerun) -> None: + async with async_client.assistant.conversations.with_streaming_response.answer_question( + answers=[[{"label": "x"}]], + question_id="x", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + conversation = await response.parse() + assert_matches_type(ConversationAnswerQuestionResponse, conversation, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_history(self, async_client: AsyncMobilerun) -> None: + conversation = await async_client.assistant.conversations.history( + session_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(ConversationHistoryResponse, conversation, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_history_with_all_params(self, async_client: AsyncMobilerun) -> None: + conversation = await async_client.assistant.conversations.history( + session_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + limit=1, + ) + assert_matches_type(ConversationHistoryResponse, conversation, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_raw_response_history(self, async_client: AsyncMobilerun) -> None: + response = await async_client.assistant.conversations.with_raw_response.history( + session_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + conversation = await response.parse() + assert_matches_type(ConversationHistoryResponse, conversation, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_streaming_response_history(self, async_client: AsyncMobilerun) -> None: + async with async_client.assistant.conversations.with_streaming_response.history( + session_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + conversation = await response.parse() + assert_matches_type(ConversationHistoryResponse, conversation, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_reject_question(self, async_client: AsyncMobilerun) -> None: + conversation = await async_client.assistant.conversations.reject_question( + question_id="x", + ) + assert_matches_type(ConversationRejectQuestionResponse, conversation, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_raw_response_reject_question(self, async_client: AsyncMobilerun) -> None: + response = await async_client.assistant.conversations.with_raw_response.reject_question( + question_id="x", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + conversation = await response.parse() + assert_matches_type(ConversationRejectQuestionResponse, conversation, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_streaming_response_reject_question(self, async_client: AsyncMobilerun) -> None: + async with async_client.assistant.conversations.with_streaming_response.reject_question( + question_id="x", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + conversation = await response.parse() + assert_matches_type(ConversationRejectQuestionResponse, conversation, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_send(self, async_client: AsyncMobilerun) -> None: + conversation = await async_client.assistant.conversations.send( + message="x", + session_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(ConversationSendResponse, conversation, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_send_with_all_params(self, async_client: AsyncMobilerun) -> None: + conversation = await async_client.assistant.conversations.send( + message="x", + session_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + agent="agent", + ) + assert_matches_type(ConversationSendResponse, conversation, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_raw_response_send(self, async_client: AsyncMobilerun) -> None: + response = await async_client.assistant.conversations.with_raw_response.send( + message="x", + session_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + conversation = await response.parse() + assert_matches_type(ConversationSendResponse, conversation, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_streaming_response_send(self, async_client: AsyncMobilerun) -> None: + async with async_client.assistant.conversations.with_streaming_response.send( + message="x", + session_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + conversation = await response.parse() + assert_matches_type(ConversationSendResponse, conversation, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_stream(self, async_client: AsyncMobilerun) -> None: + conversation_stream = await async_client.assistant.conversations.stream( + session_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + await conversation_stream.response.aclose() + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_raw_response_stream(self, async_client: AsyncMobilerun) -> None: + response = await async_client.assistant.conversations.with_raw_response.stream( + session_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + stream = await response.parse() + await stream.close() + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_streaming_response_stream(self, async_client: AsyncMobilerun) -> None: + async with async_client.assistant.conversations.with_streaming_response.stream( + session_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) 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 From 10907d90830eda6b87b3d78a46c0b526eee81acf Mon Sep 17 00:00:00 2001 From: stlc-bot Date: Fri, 21 Aug 2026 20:42:04 +0000 Subject: [PATCH 2/3] Build SDK Stainless-Generated-From: fa43e8a5630c4c47356efee3c721ce3da1a4a3f3 --- .stats.yml | 2 +- api.md | 25 + src/mobilerun_sdk/_client.py | 38 + src/mobilerun_sdk/resources/__init__.py | 14 + src/mobilerun_sdk/resources/files.py | 703 ++++++++++++++++++ src/mobilerun_sdk/types/__init__.py | 9 + .../types/file_cancel_pending_response.py | 9 + .../types/file_confirm_response.py | 35 + .../types/file_delete_response.py | 11 + src/mobilerun_sdk/types/file_list_params.py | 11 + src/mobilerun_sdk/types/file_list_response.py | 43 ++ src/mobilerun_sdk/types/file_update_params.py | 16 + .../types/file_update_response.py | 31 + .../types/file_upload_url_params.py | 21 + .../types/file_upload_url_response.py | 17 + tests/api_resources/test_files.py | 647 ++++++++++++++++ 16 files changed, 1631 insertions(+), 1 deletion(-) create mode 100644 src/mobilerun_sdk/resources/files.py create mode 100644 src/mobilerun_sdk/types/file_cancel_pending_response.py create mode 100644 src/mobilerun_sdk/types/file_confirm_response.py create mode 100644 src/mobilerun_sdk/types/file_delete_response.py create mode 100644 src/mobilerun_sdk/types/file_list_params.py create mode 100644 src/mobilerun_sdk/types/file_list_response.py create mode 100644 src/mobilerun_sdk/types/file_update_params.py create mode 100644 src/mobilerun_sdk/types/file_update_response.py create mode 100644 src/mobilerun_sdk/types/file_upload_url_params.py create mode 100644 src/mobilerun_sdk/types/file_upload_url_response.py create mode 100644 tests/api_resources/test_files.py diff --git a/.stats.yml b/.stats.yml index bd40b4e1..5893a877 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1 +1 @@ -configured_endpoints: 227 +configured_endpoints: 234 diff --git a/api.md b/api.md index b366e4de..dbe5ee9b 100644 --- a/api.md +++ b/api.md @@ -818,6 +818,31 @@ Methods: - client.agents.list() -> AgentListResponse +# Files + +Types: + +```python +from mobilerun_sdk.types import ( + FileUpdateResponse, + FileListResponse, + FileDeleteResponse, + FileCancelPendingResponse, + FileConfirmResponse, + FileUploadURLResponse, +) +``` + +Methods: + +- client.files.update(file_id, \*\*params) -> FileUpdateResponse +- client.files.list(\*\*params) -> FileListResponse +- client.files.delete(file_id) -> FileDeleteResponse +- client.files.cancel_pending(file_id) -> FileCancelPendingResponse +- client.files.confirm(file_id) -> FileConfirmResponse +- client.files.download(file_id) -> None +- client.files.upload_url(\*\*params) -> FileUploadURLResponse + # Assistant ## Conversations diff --git a/src/mobilerun_sdk/_client.py b/src/mobilerun_sdk/_client.py index 6fccbe0d..eb9941c5 100644 --- a/src/mobilerun_sdk/_client.py +++ b/src/mobilerun_sdk/_client.py @@ -39,6 +39,7 @@ from .resources import ( apps, esims, + files, store, tasks, agents, @@ -58,6 +59,7 @@ notifications, ) from .resources.apps import AppsResource, AsyncAppsResource + from .resources.files import FilesResource, AsyncFilesResource from .resources.agents import AgentsResource, AsyncAgentsResource from .resources.models import ModelsResource, AsyncModelsResource from .resources.proxies import ProxiesResource, AsyncProxiesResource @@ -223,6 +225,12 @@ def agents(self) -> AgentsResource: return AgentsResource(self) + @cached_property + def files(self) -> FilesResource: + from .resources.files import FilesResource + + return FilesResource(self) + @cached_property def assistant(self) -> AssistantResource: from .resources.assistant import AssistantResource @@ -523,6 +531,12 @@ def agents(self) -> AsyncAgentsResource: return AsyncAgentsResource(self) + @cached_property + def files(self) -> AsyncFilesResource: + from .resources.files import AsyncFilesResource + + return AsyncFilesResource(self) + @cached_property def assistant(self) -> AsyncAssistantResource: from .resources.assistant import AsyncAssistantResource @@ -769,6 +783,12 @@ def agents(self) -> agents.AgentsResourceWithRawResponse: return AgentsResourceWithRawResponse(self._client.agents) + @cached_property + def files(self) -> files.FilesResourceWithRawResponse: + from .resources.files import FilesResourceWithRawResponse + + return FilesResourceWithRawResponse(self._client.files) + @cached_property def assistant(self) -> assistant.AssistantResourceWithRawResponse: from .resources.assistant import AssistantResourceWithRawResponse @@ -892,6 +912,12 @@ def agents(self) -> agents.AsyncAgentsResourceWithRawResponse: return AsyncAgentsResourceWithRawResponse(self._client.agents) + @cached_property + def files(self) -> files.AsyncFilesResourceWithRawResponse: + from .resources.files import AsyncFilesResourceWithRawResponse + + return AsyncFilesResourceWithRawResponse(self._client.files) + @cached_property def assistant(self) -> assistant.AsyncAssistantResourceWithRawResponse: from .resources.assistant import AsyncAssistantResourceWithRawResponse @@ -1015,6 +1041,12 @@ def agents(self) -> agents.AgentsResourceWithStreamingResponse: return AgentsResourceWithStreamingResponse(self._client.agents) + @cached_property + def files(self) -> files.FilesResourceWithStreamingResponse: + from .resources.files import FilesResourceWithStreamingResponse + + return FilesResourceWithStreamingResponse(self._client.files) + @cached_property def assistant(self) -> assistant.AssistantResourceWithStreamingResponse: from .resources.assistant import AssistantResourceWithStreamingResponse @@ -1138,6 +1170,12 @@ def agents(self) -> agents.AsyncAgentsResourceWithStreamingResponse: return AsyncAgentsResourceWithStreamingResponse(self._client.agents) + @cached_property + def files(self) -> files.AsyncFilesResourceWithStreamingResponse: + from .resources.files import AsyncFilesResourceWithStreamingResponse + + return AsyncFilesResourceWithStreamingResponse(self._client.files) + @cached_property def assistant(self) -> assistant.AsyncAssistantResourceWithStreamingResponse: from .resources.assistant import AsyncAssistantResourceWithStreamingResponse diff --git a/src/mobilerun_sdk/resources/__init__.py b/src/mobilerun_sdk/resources/__init__.py index 15310aae..e4be9692 100644 --- a/src/mobilerun_sdk/resources/__init__.py +++ b/src/mobilerun_sdk/resources/__init__.py @@ -16,6 +16,14 @@ EsimsResourceWithStreamingResponse, AsyncEsimsResourceWithStreamingResponse, ) +from .files import ( + FilesResource, + AsyncFilesResource, + FilesResourceWithRawResponse, + AsyncFilesResourceWithRawResponse, + FilesResourceWithStreamingResponse, + AsyncFilesResourceWithStreamingResponse, +) from .store import ( StoreResource, AsyncStoreResource, @@ -226,6 +234,12 @@ "AsyncAgentsResourceWithRawResponse", "AgentsResourceWithStreamingResponse", "AsyncAgentsResourceWithStreamingResponse", + "FilesResource", + "AsyncFilesResource", + "FilesResourceWithRawResponse", + "AsyncFilesResourceWithRawResponse", + "FilesResourceWithStreamingResponse", + "AsyncFilesResourceWithStreamingResponse", "AssistantResource", "AsyncAssistantResource", "AssistantResourceWithRawResponse", diff --git a/src/mobilerun_sdk/resources/files.py b/src/mobilerun_sdk/resources/files.py new file mode 100644 index 00000000..739ba6f8 --- /dev/null +++ b/src/mobilerun_sdk/resources/files.py @@ -0,0 +1,703 @@ +# 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 file_list_params, file_update_params, file_upload_url_params +from .._types import Body, Omit, Query, Headers, NoneType, NotGiven, omit, not_given +from .._utils import path_template, maybe_transform, strip_not_given, 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 .._base_client import make_request_options +from ..types.file_list_response import FileListResponse +from ..types.file_delete_response import FileDeleteResponse +from ..types.file_update_response import FileUpdateResponse +from ..types.file_confirm_response import FileConfirmResponse +from ..types.file_upload_url_response import FileUploadURLResponse +from ..types.file_cancel_pending_response import FileCancelPendingResponse + +__all__ = ["FilesResource", "AsyncFilesResource"] + + +class FilesResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> FilesResourceWithRawResponse: + """ + 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/droidrun/mobilerun-sdk-python#accessing-raw-response-data-eg-headers + """ + return FilesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> FilesResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/droidrun/mobilerun-sdk-python#with_streaming_response + """ + return FilesResourceWithStreamingResponse(self) + + def update( + self, + file_id: str, + *, + display_name: Optional[str] | Omit = omit, + enabled: bool | 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, + ) -> FileUpdateResponse: + """Partial update of `displayName` and/or `enabled`. + + Only files with `zone=skills` + are mutable; other zones return 422 `unsupported_zone`. + + 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 file_id: + raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") + return self._patch( + path_template("/agents/files/{file_id}", file_id=file_id), + body=maybe_transform( + { + "display_name": display_name, + "enabled": enabled, + }, + file_update_params.FileUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=FileUpdateResponse, + ) + + def list( + self, + *, + zone: Literal["user", "agent", "workflow", "skills"] | 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, + ) -> FileListResponse: + """ + List the user's ready files, optionally filtered by zone + + 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 + """ + return self._get( + "/agents/files", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"zone": zone}, file_list_params.FileListParams), + ), + cast_to=FileListResponse, + ) + + def delete( + self, + file_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, + ) -> FileDeleteResponse: + """ + Hard-delete a file + + 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 file_id: + raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") + return self._delete( + path_template("/agents/files/{file_id}", file_id=file_id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=FileDeleteResponse, + ) + + def cancel_pending( + self, + file_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, + ) -> FileCancelPendingResponse: + """Soft-cancels an in-flight upload before confirm. + + Only acts on `pending` rows — + refuses to touch `ready` to avoid wiping confirmed files. Idempotent: + `{ cancelled: false }` if the row exists but is no longer pending. + + 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 file_id: + raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") + return self._delete( + path_template("/agents/files/{file_id}/pending", file_id=file_id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=FileCancelPendingResponse, + ) + + def confirm( + self, + file_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, + ) -> FileConfirmResponse: + """ + Confirm a file upload by server-side HEAD validation + + 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 file_id: + raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") + return self._post( + path_template("/agents/files/{file_id}/confirm", file_id=file_id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=FileConfirmResponse, + ) + + def download( + self, + file_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, + ) -> None: + """Redirects to a short-lived presigned download URL. + + Missing, non-ready, and files + owned by another tenant all return the same 404 response. + + 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 file_id: + raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return self._get( + path_template("/agents/files/{file_id}/download", file_id=file_id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + def upload_url( + self, + *, + filename: str, + mime_type: str, + size_bytes: int, + zone: Literal["user", "skills"] | Omit = omit, + idempotency_key: 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, + ) -> FileUploadURLResponse: + """ + Mint a presigned PUT URL for a user file upload + + 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 + """ + extra_headers = {**strip_not_given({"Idempotency-Key": idempotency_key}), **(extra_headers or {})} + return self._post( + "/agents/files/upload-url", + body=maybe_transform( + { + "filename": filename, + "mime_type": mime_type, + "size_bytes": size_bytes, + "zone": zone, + }, + file_upload_url_params.FileUploadURLParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=FileUploadURLResponse, + ) + + +class AsyncFilesResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncFilesResourceWithRawResponse: + """ + 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/droidrun/mobilerun-sdk-python#accessing-raw-response-data-eg-headers + """ + return AsyncFilesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncFilesResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/droidrun/mobilerun-sdk-python#with_streaming_response + """ + return AsyncFilesResourceWithStreamingResponse(self) + + async def update( + self, + file_id: str, + *, + display_name: Optional[str] | Omit = omit, + enabled: bool | 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, + ) -> FileUpdateResponse: + """Partial update of `displayName` and/or `enabled`. + + Only files with `zone=skills` + are mutable; other zones return 422 `unsupported_zone`. + + 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 file_id: + raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") + return await self._patch( + path_template("/agents/files/{file_id}", file_id=file_id), + body=await async_maybe_transform( + { + "display_name": display_name, + "enabled": enabled, + }, + file_update_params.FileUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=FileUpdateResponse, + ) + + async def list( + self, + *, + zone: Literal["user", "agent", "workflow", "skills"] | 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, + ) -> FileListResponse: + """ + List the user's ready files, optionally filtered by zone + + 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 + """ + return await self._get( + "/agents/files", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform({"zone": zone}, file_list_params.FileListParams), + ), + cast_to=FileListResponse, + ) + + async def delete( + self, + file_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, + ) -> FileDeleteResponse: + """ + Hard-delete a file + + 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 file_id: + raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") + return await self._delete( + path_template("/agents/files/{file_id}", file_id=file_id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=FileDeleteResponse, + ) + + async def cancel_pending( + self, + file_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, + ) -> FileCancelPendingResponse: + """Soft-cancels an in-flight upload before confirm. + + Only acts on `pending` rows — + refuses to touch `ready` to avoid wiping confirmed files. Idempotent: + `{ cancelled: false }` if the row exists but is no longer pending. + + 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 file_id: + raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") + return await self._delete( + path_template("/agents/files/{file_id}/pending", file_id=file_id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=FileCancelPendingResponse, + ) + + async def confirm( + self, + file_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, + ) -> FileConfirmResponse: + """ + Confirm a file upload by server-side HEAD validation + + 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 file_id: + raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") + return await self._post( + path_template("/agents/files/{file_id}/confirm", file_id=file_id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=FileConfirmResponse, + ) + + async def download( + self, + file_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, + ) -> None: + """Redirects to a short-lived presigned download URL. + + Missing, non-ready, and files + owned by another tenant all return the same 404 response. + + 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 file_id: + raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return await self._get( + path_template("/agents/files/{file_id}/download", file_id=file_id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + async def upload_url( + self, + *, + filename: str, + mime_type: str, + size_bytes: int, + zone: Literal["user", "skills"] | Omit = omit, + idempotency_key: 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, + ) -> FileUploadURLResponse: + """ + Mint a presigned PUT URL for a user file upload + + 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 + """ + extra_headers = {**strip_not_given({"Idempotency-Key": idempotency_key}), **(extra_headers or {})} + return await self._post( + "/agents/files/upload-url", + body=await async_maybe_transform( + { + "filename": filename, + "mime_type": mime_type, + "size_bytes": size_bytes, + "zone": zone, + }, + file_upload_url_params.FileUploadURLParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=FileUploadURLResponse, + ) + + +class FilesResourceWithRawResponse: + def __init__(self, files: FilesResource) -> None: + self._files = files + + self.update = to_raw_response_wrapper( + files.update, + ) + self.list = to_raw_response_wrapper( + files.list, + ) + self.delete = to_raw_response_wrapper( + files.delete, + ) + self.cancel_pending = to_raw_response_wrapper( + files.cancel_pending, + ) + self.confirm = to_raw_response_wrapper( + files.confirm, + ) + self.download = to_raw_response_wrapper( + files.download, + ) + self.upload_url = to_raw_response_wrapper( + files.upload_url, + ) + + +class AsyncFilesResourceWithRawResponse: + def __init__(self, files: AsyncFilesResource) -> None: + self._files = files + + self.update = async_to_raw_response_wrapper( + files.update, + ) + self.list = async_to_raw_response_wrapper( + files.list, + ) + self.delete = async_to_raw_response_wrapper( + files.delete, + ) + self.cancel_pending = async_to_raw_response_wrapper( + files.cancel_pending, + ) + self.confirm = async_to_raw_response_wrapper( + files.confirm, + ) + self.download = async_to_raw_response_wrapper( + files.download, + ) + self.upload_url = async_to_raw_response_wrapper( + files.upload_url, + ) + + +class FilesResourceWithStreamingResponse: + def __init__(self, files: FilesResource) -> None: + self._files = files + + self.update = to_streamed_response_wrapper( + files.update, + ) + self.list = to_streamed_response_wrapper( + files.list, + ) + self.delete = to_streamed_response_wrapper( + files.delete, + ) + self.cancel_pending = to_streamed_response_wrapper( + files.cancel_pending, + ) + self.confirm = to_streamed_response_wrapper( + files.confirm, + ) + self.download = to_streamed_response_wrapper( + files.download, + ) + self.upload_url = to_streamed_response_wrapper( + files.upload_url, + ) + + +class AsyncFilesResourceWithStreamingResponse: + def __init__(self, files: AsyncFilesResource) -> None: + self._files = files + + self.update = async_to_streamed_response_wrapper( + files.update, + ) + self.list = async_to_streamed_response_wrapper( + files.list, + ) + self.delete = async_to_streamed_response_wrapper( + files.delete, + ) + self.cancel_pending = async_to_streamed_response_wrapper( + files.cancel_pending, + ) + self.confirm = async_to_streamed_response_wrapper( + files.confirm, + ) + self.download = async_to_streamed_response_wrapper( + files.download, + ) + self.upload_url = async_to_streamed_response_wrapper( + files.upload_url, + ) diff --git a/src/mobilerun_sdk/types/__init__.py b/src/mobilerun_sdk/types/__init__.py index f634c2c1..8d7932f6 100644 --- a/src/mobilerun_sdk/types/__init__.py +++ b/src/mobilerun_sdk/types/__init__.py @@ -18,6 +18,7 @@ from .app_list_params import AppListParams as AppListParams from .task_run_params import TaskRunParams as TaskRunParams from .esim_list_params import EsimListParams as EsimListParams +from .file_list_params import FileListParams as FileListParams from .task_list_params import TaskListParams as TaskListParams from .app_list_response import AppListResponse as AppListResponse from .proxy_list_params import ProxyListParams as ProxyListParams @@ -27,6 +28,8 @@ from .esim_import_params import EsimImportParams as EsimImportParams from .esim_list_response import EsimListResponse as EsimListResponse from .esim_update_params import EsimUpdateParams as EsimUpdateParams +from .file_list_response import FileListResponse as FileListResponse +from .file_update_params import FileUpdateParams as FileUpdateParams from .number_list_params import NumberListParams as NumberListParams from .task_list_response import TaskListResponse as TaskListResponse from .task_stop_response import TaskStopResponse as TaskStopResponse @@ -47,6 +50,8 @@ from .esim_create_response import EsimCreateResponse as EsimCreateResponse from .esim_import_response import EsimImportResponse as EsimImportResponse from .esim_update_response import EsimUpdateResponse as EsimUpdateResponse +from .file_delete_response import FileDeleteResponse as FileDeleteResponse +from .file_update_response import FileUpdateResponse as FileUpdateResponse from .number_create_params import NumberCreateParams as NumberCreateParams from .number_list_response import NumberListResponse as NumberListResponse from .app_event_list_params import AppEventListParams as AppEventListParams @@ -57,6 +62,7 @@ from .carrier_update_params import CarrierUpdateParams as CarrierUpdateParams from .device_count_response import DeviceCountResponse as DeviceCountResponse from .esim_install_response import EsimInstallResponse as EsimInstallResponse +from .file_confirm_response import FileConfirmResponse as FileConfirmResponse from .message_list_response import MessageListResponse as MessageListResponse from .profile_create_params import ProfileCreateParams as ProfileCreateParams from .profile_list_response import ProfileListResponse as ProfileListResponse @@ -74,6 +80,7 @@ from .esim_capacity_response import EsimCapacityResponse as EsimCapacityResponse from .esim_retrieve_response import EsimRetrieveResponse as EsimRetrieveResponse from .esim_selector_response import EsimSelectorResponse as EsimSelectorResponse +from .file_upload_url_params import FileUploadURLParams as FileUploadURLParams from .number_create_response import NumberCreateResponse as NumberCreateResponse from .number_delete_response import NumberDeleteResponse as NumberDeleteResponse from .task_retrieve_response import TaskRetrieveResponse as TaskRetrieveResponse @@ -93,6 +100,7 @@ from .credential_list_response import CredentialListResponse as CredentialListResponse from .device_retrieve_response import DeviceRetrieveResponse as DeviceRetrieveResponse from .device_set_name_response import DeviceSetNameResponse as DeviceSetNameResponse +from .file_upload_url_response import FileUploadURLResponse as FileUploadURLResponse from .number_purposes_response import NumberPurposesResponse as NumberPurposesResponse from .number_retrieve_response import NumberRetrieveResponse as NumberRetrieveResponse from .task_get_status_response import TaskGetStatusResponse as TaskGetStatusResponse @@ -111,6 +119,7 @@ from .app_event_retrieve_response import AppEventRetrieveResponse as AppEventRetrieveResponse from .device_fingerprint_response import DeviceFingerprintResponse as DeviceFingerprintResponse from .esim_install_status_response import EsimInstallStatusResponse as EsimInstallStatusResponse +from .file_cancel_pending_response import FileCancelPendingResponse as FileCancelPendingResponse from .task_get_trajectory_response import TaskGetTrajectoryResponse as TaskGetTrajectoryResponse from .webhook_event_types_response import WebhookEventTypesResponse as WebhookEventTypesResponse from .esim_confirm_payment_response import EsimConfirmPaymentResponse as EsimConfirmPaymentResponse diff --git a/src/mobilerun_sdk/types/file_cancel_pending_response.py b/src/mobilerun_sdk/types/file_cancel_pending_response.py new file mode 100644 index 00000000..811c6b32 --- /dev/null +++ b/src/mobilerun_sdk/types/file_cancel_pending_response.py @@ -0,0 +1,9 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .._models import BaseModel + +__all__ = ["FileCancelPendingResponse"] + + +class FileCancelPendingResponse(BaseModel): + cancelled: bool diff --git a/src/mobilerun_sdk/types/file_confirm_response.py b/src/mobilerun_sdk/types/file_confirm_response.py new file mode 100644 index 00000000..7ee2a30c --- /dev/null +++ b/src/mobilerun_sdk/types/file_confirm_response.py @@ -0,0 +1,35 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from datetime import datetime +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = ["FileConfirmResponse"] + + +class FileConfirmResponse(BaseModel): + actual_size_bytes: float = FieldInfo(alias="actualSizeBytes") + + created_at: datetime = FieldInfo(alias="createdAt") + + created_by: Literal["user", "agent", "workflow"] = FieldInfo(alias="createdBy") + + display_name: Optional[str] = FieldInfo(alias="displayName", default=None) + + enabled: bool + + file_id: str = FieldInfo(alias="fileId") + + filename: str + + mime_type: str = FieldInfo(alias="mimeType") + + size_bytes: float = FieldInfo(alias="sizeBytes") + + state: Literal["ready"] + + zone: Literal["user", "agent", "workflow", "skills"] diff --git a/src/mobilerun_sdk/types/file_delete_response.py b/src/mobilerun_sdk/types/file_delete_response.py new file mode 100644 index 00000000..66dabbab --- /dev/null +++ b/src/mobilerun_sdk/types/file_delete_response.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from .._models import BaseModel + +__all__ = ["FileDeleteResponse"] + + +class FileDeleteResponse(BaseModel): + ok: Literal[True] diff --git a/src/mobilerun_sdk/types/file_list_params.py b/src/mobilerun_sdk/types/file_list_params.py new file mode 100644 index 00000000..fe1d5561 --- /dev/null +++ b/src/mobilerun_sdk/types/file_list_params.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["FileListParams"] + + +class FileListParams(TypedDict, total=False): + zone: Literal["user", "agent", "workflow", "skills"] diff --git a/src/mobilerun_sdk/types/file_list_response.py b/src/mobilerun_sdk/types/file_list_response.py new file mode 100644 index 00000000..014c11fb --- /dev/null +++ b/src/mobilerun_sdk/types/file_list_response.py @@ -0,0 +1,43 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from datetime import datetime +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = ["FileListResponse", "File", "Quota"] + + +class File(BaseModel): + created_at: datetime = FieldInfo(alias="createdAt") + + created_by: Literal["user", "agent", "workflow"] = FieldInfo(alias="createdBy") + + display_name: Optional[str] = FieldInfo(alias="displayName", default=None) + + enabled: bool + + file_id: str = FieldInfo(alias="fileId") + + filename: str + + mime_type: str = FieldInfo(alias="mimeType") + + size_bytes: float = FieldInfo(alias="sizeBytes") + + zone: Literal["user", "agent", "workflow", "skills"] + + +class Quota(BaseModel): + current_bytes: int = FieldInfo(alias="currentBytes") + + quota_bytes: int = FieldInfo(alias="quotaBytes") + + +class FileListResponse(BaseModel): + files: List[File] + + quota: Quota diff --git a/src/mobilerun_sdk/types/file_update_params.py b/src/mobilerun_sdk/types/file_update_params.py new file mode 100644 index 00000000..e6da4e5a --- /dev/null +++ b/src/mobilerun_sdk/types/file_update_params.py @@ -0,0 +1,16 @@ +# 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 Annotated, TypedDict + +from .._utils import PropertyInfo + +__all__ = ["FileUpdateParams"] + + +class FileUpdateParams(TypedDict, total=False): + display_name: Annotated[Optional[str], PropertyInfo(alias="displayName")] + + enabled: bool diff --git a/src/mobilerun_sdk/types/file_update_response.py b/src/mobilerun_sdk/types/file_update_response.py new file mode 100644 index 00000000..a3903256 --- /dev/null +++ b/src/mobilerun_sdk/types/file_update_response.py @@ -0,0 +1,31 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from datetime import datetime +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = ["FileUpdateResponse"] + + +class FileUpdateResponse(BaseModel): + created_at: datetime = FieldInfo(alias="createdAt") + + created_by: Literal["user", "agent", "workflow"] = FieldInfo(alias="createdBy") + + display_name: Optional[str] = FieldInfo(alias="displayName", default=None) + + enabled: bool + + file_id: str = FieldInfo(alias="fileId") + + filename: str + + mime_type: str = FieldInfo(alias="mimeType") + + size_bytes: float = FieldInfo(alias="sizeBytes") + + zone: Literal["user", "agent", "workflow", "skills"] diff --git a/src/mobilerun_sdk/types/file_upload_url_params.py b/src/mobilerun_sdk/types/file_upload_url_params.py new file mode 100644 index 00000000..98c7f0c0 --- /dev/null +++ b/src/mobilerun_sdk/types/file_upload_url_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, Annotated, TypedDict + +from .._utils import PropertyInfo + +__all__ = ["FileUploadURLParams"] + + +class FileUploadURLParams(TypedDict, total=False): + filename: Required[str] + + mime_type: Required[Annotated[str, PropertyInfo(alias="mimeType")]] + + size_bytes: Required[Annotated[int, PropertyInfo(alias="sizeBytes")]] + + zone: Literal["user", "skills"] + + idempotency_key: Annotated[str, PropertyInfo(alias="Idempotency-Key")] diff --git a/src/mobilerun_sdk/types/file_upload_url_response.py b/src/mobilerun_sdk/types/file_upload_url_response.py new file mode 100644 index 00000000..d376779a --- /dev/null +++ b/src/mobilerun_sdk/types/file_upload_url_response.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from datetime import datetime + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = ["FileUploadURLResponse"] + + +class FileUploadURLResponse(BaseModel): + expires_at: datetime = FieldInfo(alias="expiresAt") + + file_id: str = FieldInfo(alias="fileId") + + put_url: str = FieldInfo(alias="putUrl") diff --git a/tests/api_resources/test_files.py b/tests/api_resources/test_files.py new file mode 100644 index 00000000..47decef9 --- /dev/null +++ b/tests/api_resources/test_files.py @@ -0,0 +1,647 @@ +# 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 mobilerun_sdk import Mobilerun, AsyncMobilerun +from mobilerun_sdk.types import ( + FileListResponse, + FileDeleteResponse, + FileUpdateResponse, + FileConfirmResponse, + FileUploadURLResponse, + FileCancelPendingResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestFiles: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_update(self, client: Mobilerun) -> None: + file = client.files.update( + file_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(FileUpdateResponse, file, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_update_with_all_params(self, client: Mobilerun) -> None: + file = client.files.update( + file_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + display_name="x", + enabled=True, + ) + assert_matches_type(FileUpdateResponse, file, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_raw_response_update(self, client: Mobilerun) -> None: + response = client.files.with_raw_response.update( + file_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + file = response.parse() + assert_matches_type(FileUpdateResponse, file, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_streaming_response_update(self, client: Mobilerun) -> None: + with client.files.with_streaming_response.update( + file_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + file = response.parse() + assert_matches_type(FileUpdateResponse, file, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_path_params_update(self, client: Mobilerun) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_id` but received ''"): + client.files.with_raw_response.update( + file_id="", + ) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_list(self, client: Mobilerun) -> None: + file = client.files.list() + assert_matches_type(FileListResponse, file, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_list_with_all_params(self, client: Mobilerun) -> None: + file = client.files.list( + zone="user", + ) + assert_matches_type(FileListResponse, file, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_raw_response_list(self, client: Mobilerun) -> None: + response = client.files.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + file = response.parse() + assert_matches_type(FileListResponse, file, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_streaming_response_list(self, client: Mobilerun) -> None: + with client.files.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + file = response.parse() + assert_matches_type(FileListResponse, file, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_delete(self, client: Mobilerun) -> None: + file = client.files.delete( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(FileDeleteResponse, file, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_raw_response_delete(self, client: Mobilerun) -> None: + response = client.files.with_raw_response.delete( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + file = response.parse() + assert_matches_type(FileDeleteResponse, file, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_streaming_response_delete(self, client: Mobilerun) -> None: + with client.files.with_streaming_response.delete( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + file = response.parse() + assert_matches_type(FileDeleteResponse, file, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_path_params_delete(self, client: Mobilerun) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_id` but received ''"): + client.files.with_raw_response.delete( + "", + ) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_cancel_pending(self, client: Mobilerun) -> None: + file = client.files.cancel_pending( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(FileCancelPendingResponse, file, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_raw_response_cancel_pending(self, client: Mobilerun) -> None: + response = client.files.with_raw_response.cancel_pending( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + file = response.parse() + assert_matches_type(FileCancelPendingResponse, file, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_streaming_response_cancel_pending(self, client: Mobilerun) -> None: + with client.files.with_streaming_response.cancel_pending( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + file = response.parse() + assert_matches_type(FileCancelPendingResponse, file, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_path_params_cancel_pending(self, client: Mobilerun) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_id` but received ''"): + client.files.with_raw_response.cancel_pending( + "", + ) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_confirm(self, client: Mobilerun) -> None: + file = client.files.confirm( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(FileConfirmResponse, file, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_raw_response_confirm(self, client: Mobilerun) -> None: + response = client.files.with_raw_response.confirm( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + file = response.parse() + assert_matches_type(FileConfirmResponse, file, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_streaming_response_confirm(self, client: Mobilerun) -> None: + with client.files.with_streaming_response.confirm( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + file = response.parse() + assert_matches_type(FileConfirmResponse, file, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_path_params_confirm(self, client: Mobilerun) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_id` but received ''"): + client.files.with_raw_response.confirm( + "", + ) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_download(self, client: Mobilerun) -> None: + file = client.files.download( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert file is None + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_raw_response_download(self, client: Mobilerun) -> None: + response = client.files.with_raw_response.download( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + file = response.parse() + assert file is None + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_streaming_response_download(self, client: Mobilerun) -> None: + with client.files.with_streaming_response.download( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + file = response.parse() + assert file is None + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_path_params_download(self, client: Mobilerun) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_id` but received ''"): + client.files.with_raw_response.download( + "", + ) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_upload_url(self, client: Mobilerun) -> None: + file = client.files.upload_url( + filename="x", + mime_type="x", + size_bytes=1, + ) + assert_matches_type(FileUploadURLResponse, file, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_upload_url_with_all_params(self, client: Mobilerun) -> None: + file = client.files.upload_url( + filename="x", + mime_type="x", + size_bytes=1, + zone="user", + idempotency_key="x", + ) + assert_matches_type(FileUploadURLResponse, file, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_raw_response_upload_url(self, client: Mobilerun) -> None: + response = client.files.with_raw_response.upload_url( + filename="x", + mime_type="x", + size_bytes=1, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + file = response.parse() + assert_matches_type(FileUploadURLResponse, file, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_streaming_response_upload_url(self, client: Mobilerun) -> None: + with client.files.with_streaming_response.upload_url( + filename="x", + mime_type="x", + size_bytes=1, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + file = response.parse() + assert_matches_type(FileUploadURLResponse, file, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncFiles: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_update(self, async_client: AsyncMobilerun) -> None: + file = await async_client.files.update( + file_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(FileUpdateResponse, file, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_update_with_all_params(self, async_client: AsyncMobilerun) -> None: + file = await async_client.files.update( + file_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + display_name="x", + enabled=True, + ) + assert_matches_type(FileUpdateResponse, file, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_raw_response_update(self, async_client: AsyncMobilerun) -> None: + response = await async_client.files.with_raw_response.update( + file_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + file = await response.parse() + assert_matches_type(FileUpdateResponse, file, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_streaming_response_update(self, async_client: AsyncMobilerun) -> None: + async with async_client.files.with_streaming_response.update( + file_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + file = await response.parse() + assert_matches_type(FileUpdateResponse, file, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_path_params_update(self, async_client: AsyncMobilerun) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_id` but received ''"): + await async_client.files.with_raw_response.update( + file_id="", + ) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_list(self, async_client: AsyncMobilerun) -> None: + file = await async_client.files.list() + assert_matches_type(FileListResponse, file, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncMobilerun) -> None: + file = await async_client.files.list( + zone="user", + ) + assert_matches_type(FileListResponse, file, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_raw_response_list(self, async_client: AsyncMobilerun) -> None: + response = await async_client.files.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + file = await response.parse() + assert_matches_type(FileListResponse, file, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_streaming_response_list(self, async_client: AsyncMobilerun) -> None: + async with async_client.files.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + file = await response.parse() + assert_matches_type(FileListResponse, file, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_delete(self, async_client: AsyncMobilerun) -> None: + file = await async_client.files.delete( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(FileDeleteResponse, file, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_raw_response_delete(self, async_client: AsyncMobilerun) -> None: + response = await async_client.files.with_raw_response.delete( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + file = await response.parse() + assert_matches_type(FileDeleteResponse, file, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncMobilerun) -> None: + async with async_client.files.with_streaming_response.delete( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + file = await response.parse() + assert_matches_type(FileDeleteResponse, file, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_path_params_delete(self, async_client: AsyncMobilerun) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_id` but received ''"): + await async_client.files.with_raw_response.delete( + "", + ) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_cancel_pending(self, async_client: AsyncMobilerun) -> None: + file = await async_client.files.cancel_pending( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(FileCancelPendingResponse, file, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_raw_response_cancel_pending(self, async_client: AsyncMobilerun) -> None: + response = await async_client.files.with_raw_response.cancel_pending( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + file = await response.parse() + assert_matches_type(FileCancelPendingResponse, file, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_streaming_response_cancel_pending(self, async_client: AsyncMobilerun) -> None: + async with async_client.files.with_streaming_response.cancel_pending( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + file = await response.parse() + assert_matches_type(FileCancelPendingResponse, file, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_path_params_cancel_pending(self, async_client: AsyncMobilerun) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_id` but received ''"): + await async_client.files.with_raw_response.cancel_pending( + "", + ) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_confirm(self, async_client: AsyncMobilerun) -> None: + file = await async_client.files.confirm( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(FileConfirmResponse, file, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_raw_response_confirm(self, async_client: AsyncMobilerun) -> None: + response = await async_client.files.with_raw_response.confirm( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + file = await response.parse() + assert_matches_type(FileConfirmResponse, file, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_streaming_response_confirm(self, async_client: AsyncMobilerun) -> None: + async with async_client.files.with_streaming_response.confirm( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + file = await response.parse() + assert_matches_type(FileConfirmResponse, file, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_path_params_confirm(self, async_client: AsyncMobilerun) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_id` but received ''"): + await async_client.files.with_raw_response.confirm( + "", + ) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_download(self, async_client: AsyncMobilerun) -> None: + file = await async_client.files.download( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert file is None + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_raw_response_download(self, async_client: AsyncMobilerun) -> None: + response = await async_client.files.with_raw_response.download( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + file = await response.parse() + assert file is None + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_streaming_response_download(self, async_client: AsyncMobilerun) -> None: + async with async_client.files.with_streaming_response.download( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + file = await response.parse() + assert file is None + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_path_params_download(self, async_client: AsyncMobilerun) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_id` but received ''"): + await async_client.files.with_raw_response.download( + "", + ) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_upload_url(self, async_client: AsyncMobilerun) -> None: + file = await async_client.files.upload_url( + filename="x", + mime_type="x", + size_bytes=1, + ) + assert_matches_type(FileUploadURLResponse, file, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_upload_url_with_all_params(self, async_client: AsyncMobilerun) -> None: + file = await async_client.files.upload_url( + filename="x", + mime_type="x", + size_bytes=1, + zone="user", + idempotency_key="x", + ) + assert_matches_type(FileUploadURLResponse, file, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_raw_response_upload_url(self, async_client: AsyncMobilerun) -> None: + response = await async_client.files.with_raw_response.upload_url( + filename="x", + mime_type="x", + size_bytes=1, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + file = await response.parse() + assert_matches_type(FileUploadURLResponse, file, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_streaming_response_upload_url(self, async_client: AsyncMobilerun) -> None: + async with async_client.files.with_streaming_response.upload_url( + filename="x", + mime_type="x", + size_bytes=1, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + file = await response.parse() + assert_matches_type(FileUploadURLResponse, file, path=["response"]) + + assert cast(Any, response.is_closed) is True From 55fb15d2b4d1dc9ac149843473f6a87f92eea81d Mon Sep 17 00:00:00 2001 From: stlc-bot Date: Mon, 24 Aug 2026 15:28:00 +0000 Subject: [PATCH 3/3] Build SDK Stainless-Generated-From: 0e546a2f3af52dbaab05637caea11b4d2063dfd2 --- .stats.yml | 2 +- api.md | 29 +- src/mobilerun_sdk/_client.py | 38 --- src/mobilerun_sdk/resources/__init__.py | 14 - src/mobilerun_sdk/resources/agents.py | 135 --------- .../resources/assistant/conversations.py | 46 ++- .../resources/devices/__init__.py | 14 + .../resources/devices/clipboard.py | 285 ++++++++++++++++++ .../resources/devices/devices.py | 32 ++ src/mobilerun_sdk/resources/esims/esims.py | 83 ++++- src/mobilerun_sdk/resources/models.py | 4 +- .../resources/numbers/numbers.py | 116 ++++++- .../resources/workflows/flows/flows.py | 8 + src/mobilerun_sdk/types/__init__.py | 4 +- .../types/agent_list_response.py | 33 -- src/mobilerun_sdk/types/app_list_response.py | 2 +- .../types/app_list_versions_response.py | 2 +- .../conversation_answer_question_params.py | 6 - .../conversation_history_response.py | 2 +- .../device_retrieve_capabilities_response.py | 2 + src/mobilerun_sdk/types/devices/__init__.py | 2 + .../types/devices/clipboard_get_response.py | 16 + .../types/devices/clipboard_set_params.py | 15 + src/mobilerun_sdk/types/esim_create_params.py | 7 + src/mobilerun_sdk/types/esim_list_response.py | 12 +- .../types/esim_selector_params.py | 15 + .../types/esim_selector_response.py | 13 +- .../types/esims/message_list_response.py | 12 +- .../types/message_list_response.py | 12 +- .../messages/conversation_list_response.py | 20 +- .../types/model_list_response.py | 17 +- .../types/number_create_params.py | 8 + .../types/number_delete_response.py | 2 + .../types/number_list_response.py | 14 +- .../types/number_retrieve_response.py | 2 + .../types/number_update_params.py | 17 ++ .../types/number_update_response.py | 45 +++ .../types/numbers/message_list_response.py | 29 +- .../types/store/app_list_response.py | 2 +- .../types/store/app_retrieve_response.py | 2 +- .../types/workflows/event_dry_run_response.py | 2 + .../workflows/execution_abort_response.py | 9 + .../workflows/execution_list_response.py | 9 + .../workflows/execution_retrieve_response.py | 9 + .../types/workflows/flow_clone_response.py | 2 + .../types/workflows/flow_create_params.py | 2 + .../types/workflows/flow_create_response.py | 2 + .../types/workflows/flow_list_response.py | 2 + .../types/workflows/flow_retrieve_response.py | 2 + .../types/workflows/flow_unblock_response.py | 2 + .../types/workflows/flow_update_params.py | 2 + .../types/workflows/flow_update_response.py | 2 + .../assistant/test_conversations.py | 20 -- tests/api_resources/devices/test_clipboard.py | 238 +++++++++++++++ tests/api_resources/test_agents.py | 80 ----- tests/api_resources/test_esims.py | 20 ++ tests/api_resources/test_numbers.py | 105 +++++++ tests/api_resources/workflows/test_flows.py | 4 + 58 files changed, 1169 insertions(+), 462 deletions(-) delete mode 100644 src/mobilerun_sdk/resources/agents.py create mode 100644 src/mobilerun_sdk/resources/devices/clipboard.py delete mode 100644 src/mobilerun_sdk/types/agent_list_response.py create mode 100644 src/mobilerun_sdk/types/devices/clipboard_get_response.py create mode 100644 src/mobilerun_sdk/types/devices/clipboard_set_params.py create mode 100644 src/mobilerun_sdk/types/esim_selector_params.py create mode 100644 src/mobilerun_sdk/types/number_update_params.py create mode 100644 src/mobilerun_sdk/types/number_update_response.py create mode 100644 tests/api_resources/devices/test_clipboard.py delete mode 100644 tests/api_resources/test_agents.py diff --git a/.stats.yml b/.stats.yml index 5893a877..027e42a1 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1 +1 @@ -configured_endpoints: 234 +configured_endpoints: 236 diff --git a/api.md b/api.md index dbe5ee9b..3081e9c2 100644 --- a/api.md +++ b/api.md @@ -325,6 +325,19 @@ Methods: - client.devices.tasks.list(device_id, \*\*params) -> TaskListResponse +## Clipboard + +Types: + +```python +from mobilerun_sdk.types.devices import ClipboardGetResponse +``` + +Methods: + +- client.devices.clipboard.get(device_id) -> ClipboardGetResponse +- client.devices.clipboard.set(device_id, \*\*params) -> None + ## Timezone Types: @@ -806,18 +819,6 @@ Methods: - client.webhooks.deliveries.retrieve_attempts(delivery_id, \*, id) -> DeliveryRetrieveAttemptsResponse - client.webhooks.deliveries.stats(\*\*params) -> DeliveryStatsResponse -# Agents - -Types: - -```python -from mobilerun_sdk.types import AgentListResponse -``` - -Methods: - -- client.agents.list() -> AgentListResponse - # Files Types: @@ -952,7 +953,7 @@ Methods: - client.esims.import\_(\*\*params) -> EsimImportResponse - client.esims.install(id, \*\*params) -> EsimInstallResponse - client.esims.install_status(id) -> EsimInstallStatusResponse -- client.esims.selector() -> EsimSelectorResponse +- client.esims.selector(\*\*params) -> EsimSelectorResponse ## Messages @@ -1000,6 +1001,7 @@ Types: from mobilerun_sdk.types import ( NumberCreateResponse, NumberRetrieveResponse, + NumberUpdateResponse, NumberListResponse, NumberDeleteResponse, NumberCountriesResponse, @@ -1011,6 +1013,7 @@ Methods: - client.numbers.create(\*\*params) -> NumberCreateResponse - client.numbers.retrieve(id) -> NumberRetrieveResponse +- client.numbers.update(id, \*\*params) -> NumberUpdateResponse - client.numbers.list(\*\*params) -> NumberListResponse - client.numbers.delete(id) -> NumberDeleteResponse - client.numbers.countries() -> NumberCountriesResponse diff --git a/src/mobilerun_sdk/_client.py b/src/mobilerun_sdk/_client.py index eb9941c5..a8adddde 100644 --- a/src/mobilerun_sdk/_client.py +++ b/src/mobilerun_sdk/_client.py @@ -42,7 +42,6 @@ files, store, tasks, - agents, models, connect, devices, @@ -60,7 +59,6 @@ ) from .resources.apps import AppsResource, AsyncAppsResource from .resources.files import FilesResource, AsyncFilesResource - from .resources.agents import AgentsResource, AsyncAgentsResource from .resources.models import ModelsResource, AsyncModelsResource from .resources.proxies import ProxiesResource, AsyncProxiesResource from .resources.carriers import CarriersResource, AsyncCarriersResource @@ -219,12 +217,6 @@ def webhooks(self) -> WebhooksResource: return WebhooksResource(self) - @cached_property - def agents(self) -> AgentsResource: - from .resources.agents import AgentsResource - - return AgentsResource(self) - @cached_property def files(self) -> FilesResource: from .resources.files import FilesResource @@ -525,12 +517,6 @@ def webhooks(self) -> AsyncWebhooksResource: return AsyncWebhooksResource(self) - @cached_property - def agents(self) -> AsyncAgentsResource: - from .resources.agents import AsyncAgentsResource - - return AsyncAgentsResource(self) - @cached_property def files(self) -> AsyncFilesResource: from .resources.files import AsyncFilesResource @@ -777,12 +763,6 @@ def webhooks(self) -> webhooks.WebhooksResourceWithRawResponse: return WebhooksResourceWithRawResponse(self._client.webhooks) - @cached_property - def agents(self) -> agents.AgentsResourceWithRawResponse: - from .resources.agents import AgentsResourceWithRawResponse - - return AgentsResourceWithRawResponse(self._client.agents) - @cached_property def files(self) -> files.FilesResourceWithRawResponse: from .resources.files import FilesResourceWithRawResponse @@ -906,12 +886,6 @@ def webhooks(self) -> webhooks.AsyncWebhooksResourceWithRawResponse: return AsyncWebhooksResourceWithRawResponse(self._client.webhooks) - @cached_property - def agents(self) -> agents.AsyncAgentsResourceWithRawResponse: - from .resources.agents import AsyncAgentsResourceWithRawResponse - - return AsyncAgentsResourceWithRawResponse(self._client.agents) - @cached_property def files(self) -> files.AsyncFilesResourceWithRawResponse: from .resources.files import AsyncFilesResourceWithRawResponse @@ -1035,12 +1009,6 @@ def webhooks(self) -> webhooks.WebhooksResourceWithStreamingResponse: return WebhooksResourceWithStreamingResponse(self._client.webhooks) - @cached_property - def agents(self) -> agents.AgentsResourceWithStreamingResponse: - from .resources.agents import AgentsResourceWithStreamingResponse - - return AgentsResourceWithStreamingResponse(self._client.agents) - @cached_property def files(self) -> files.FilesResourceWithStreamingResponse: from .resources.files import FilesResourceWithStreamingResponse @@ -1164,12 +1132,6 @@ def webhooks(self) -> webhooks.AsyncWebhooksResourceWithStreamingResponse: return AsyncWebhooksResourceWithStreamingResponse(self._client.webhooks) - @cached_property - def agents(self) -> agents.AsyncAgentsResourceWithStreamingResponse: - from .resources.agents import AsyncAgentsResourceWithStreamingResponse - - return AsyncAgentsResourceWithStreamingResponse(self._client.agents) - @cached_property def files(self) -> files.AsyncFilesResourceWithStreamingResponse: from .resources.files import AsyncFilesResourceWithStreamingResponse diff --git a/src/mobilerun_sdk/resources/__init__.py b/src/mobilerun_sdk/resources/__init__.py index e4be9692..1181afd4 100644 --- a/src/mobilerun_sdk/resources/__init__.py +++ b/src/mobilerun_sdk/resources/__init__.py @@ -40,14 +40,6 @@ TasksResourceWithStreamingResponse, AsyncTasksResourceWithStreamingResponse, ) -from .agents import ( - AgentsResource, - AsyncAgentsResource, - AgentsResourceWithRawResponse, - AsyncAgentsResourceWithRawResponse, - AgentsResourceWithStreamingResponse, - AsyncAgentsResourceWithStreamingResponse, -) from .models import ( ModelsResource, AsyncModelsResource, @@ -228,12 +220,6 @@ "AsyncWebhooksResourceWithRawResponse", "WebhooksResourceWithStreamingResponse", "AsyncWebhooksResourceWithStreamingResponse", - "AgentsResource", - "AsyncAgentsResource", - "AgentsResourceWithRawResponse", - "AsyncAgentsResourceWithRawResponse", - "AgentsResourceWithStreamingResponse", - "AsyncAgentsResourceWithStreamingResponse", "FilesResource", "AsyncFilesResource", "FilesResourceWithRawResponse", diff --git a/src/mobilerun_sdk/resources/agents.py b/src/mobilerun_sdk/resources/agents.py deleted file mode 100644 index 7d7bb86f..00000000 --- a/src/mobilerun_sdk/resources/agents.py +++ /dev/null @@ -1,135 +0,0 @@ -# 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.agent_list_response import AgentListResponse - -__all__ = ["AgentsResource", "AsyncAgentsResource"] - - -class AgentsResource(SyncAPIResource): - @cached_property - def with_raw_response(self) -> AgentsResourceWithRawResponse: - """ - 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/droidrun/mobilerun-sdk-python#accessing-raw-response-data-eg-headers - """ - return AgentsResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AgentsResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/droidrun/mobilerun-sdk-python#with_streaming_response - """ - return AgentsResourceWithStreamingResponse(self) - - 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, - ) -> AgentListResponse: - """List all available agents with their default configurations.""" - return self._get( - "/agents", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=AgentListResponse, - ) - - -class AsyncAgentsResource(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncAgentsResourceWithRawResponse: - """ - 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/droidrun/mobilerun-sdk-python#accessing-raw-response-data-eg-headers - """ - return AsyncAgentsResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncAgentsResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/droidrun/mobilerun-sdk-python#with_streaming_response - """ - return AsyncAgentsResourceWithStreamingResponse(self) - - 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, - ) -> AgentListResponse: - """List all available agents with their default configurations.""" - return await self._get( - "/agents", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=AgentListResponse, - ) - - -class AgentsResourceWithRawResponse: - def __init__(self, agents: AgentsResource) -> None: - self._agents = agents - - self.list = to_raw_response_wrapper( - agents.list, - ) - - -class AsyncAgentsResourceWithRawResponse: - def __init__(self, agents: AsyncAgentsResource) -> None: - self._agents = agents - - self.list = async_to_raw_response_wrapper( - agents.list, - ) - - -class AgentsResourceWithStreamingResponse: - def __init__(self, agents: AgentsResource) -> None: - self._agents = agents - - self.list = to_streamed_response_wrapper( - agents.list, - ) - - -class AsyncAgentsResourceWithStreamingResponse: - def __init__(self, agents: AsyncAgentsResource) -> None: - self._agents = agents - - self.list = async_to_streamed_response_wrapper( - agents.list, - ) diff --git a/src/mobilerun_sdk/resources/assistant/conversations.py b/src/mobilerun_sdk/resources/assistant/conversations.py index 536d58be..04b2a56c 100644 --- a/src/mobilerun_sdk/resources/assistant/conversations.py +++ b/src/mobilerun_sdk/resources/assistant/conversations.py @@ -264,7 +264,7 @@ def answer_permission( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ConversationAnswerPermissionResponse: """ - Deliver a permission approval or rejection for an in-flight turn. + Deliver a HITL approval/rejection for an in-flight turn. Args: extra_headers: Send extra headers @@ -295,7 +295,6 @@ def answer_question( *, answers: Iterable[Iterable[conversation_answer_question_params.Answer]], question_id: str, - idempotency_key: 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, @@ -303,14 +302,12 @@ def answer_question( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ConversationAnswerQuestionResponse: - """ - Deliver the user's answers to the pending question for an in-flight turn. - Idempotent via the `Idempotency-Key` header. + """Deliver the user's answers to the agent's pending question for an in-flight + turn. - Args: - idempotency_key: Optional client key. Reusing the same key with the same question answers - coalesces duplicate submits. + Idempotent via the `idempotency-key` header. + Args: extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -319,7 +316,6 @@ def answer_question( timeout: Override the client-level default timeout for this request, in seconds """ - extra_headers = {**strip_not_given({"Idempotency-Key": idempotency_key}), **(extra_headers or {})} return self._post( "/assistant/chat/question", body=maybe_transform( @@ -390,10 +386,10 @@ def reject_question( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ConversationRejectQuestionResponse: - """Dismiss the pending question. + """Dismiss the agent's pending question. - Already-resolved questions return 200 (no-op) so - multi-tab dismiss stays idempotent. + Already-resolved questions return 200 + (no-op) so multi-tab dismiss stays idempotent. Args: extra_headers: Send extra headers @@ -476,7 +472,8 @@ def stream( Replays buffered events from the start of the active turn, then continues live until the turn finishes. Responds 204 - when no turn is active for the requested session. Resume is best-effort. Does + when no active turn exists for the requested session. Upstream streaming + failures return a retryable 503 with `Retry-After`. Resume is best-effort. Does not start an inactive session. Args: @@ -725,7 +722,7 @@ async def answer_permission( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ConversationAnswerPermissionResponse: """ - Deliver a permission approval or rejection for an in-flight turn. + Deliver a HITL approval/rejection for an in-flight turn. Args: extra_headers: Send extra headers @@ -756,7 +753,6 @@ async def answer_question( *, answers: Iterable[Iterable[conversation_answer_question_params.Answer]], question_id: str, - idempotency_key: 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, @@ -764,14 +760,12 @@ async def answer_question( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ConversationAnswerQuestionResponse: - """ - Deliver the user's answers to the pending question for an in-flight turn. - Idempotent via the `Idempotency-Key` header. + """Deliver the user's answers to the agent's pending question for an in-flight + turn. - Args: - idempotency_key: Optional client key. Reusing the same key with the same question answers - coalesces duplicate submits. + Idempotent via the `idempotency-key` header. + Args: extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -780,7 +774,6 @@ async def answer_question( timeout: Override the client-level default timeout for this request, in seconds """ - extra_headers = {**strip_not_given({"Idempotency-Key": idempotency_key}), **(extra_headers or {})} return await self._post( "/assistant/chat/question", body=await async_maybe_transform( @@ -851,10 +844,10 @@ async def reject_question( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ConversationRejectQuestionResponse: - """Dismiss the pending question. + """Dismiss the agent's pending question. - Already-resolved questions return 200 (no-op) so - multi-tab dismiss stays idempotent. + Already-resolved questions return 200 + (no-op) so multi-tab dismiss stays idempotent. Args: extra_headers: Send extra headers @@ -937,7 +930,8 @@ async def stream( Replays buffered events from the start of the active turn, then continues live until the turn finishes. Responds 204 - when no turn is active for the requested session. Resume is best-effort. Does + when no active turn exists for the requested session. Upstream streaming + failures return a retryable 503 with `Retry-After`. Resume is best-effort. Does not start an inactive session. Args: diff --git a/src/mobilerun_sdk/resources/devices/__init__.py b/src/mobilerun_sdk/resources/devices/__init__.py index 4f1a6d2b..7a429240 100644 --- a/src/mobilerun_sdk/resources/devices/__init__.py +++ b/src/mobilerun_sdk/resources/devices/__init__.py @@ -128,6 +128,14 @@ TimezoneResourceWithStreamingResponse, AsyncTimezoneResourceWithStreamingResponse, ) +from .clipboard import ( + ClipboardResource, + AsyncClipboardResource, + ClipboardResourceWithRawResponse, + AsyncClipboardResourceWithRawResponse, + ClipboardResourceWithStreamingResponse, + AsyncClipboardResourceWithStreamingResponse, +) from .deep_link import ( DeepLinkResource, AsyncDeepLinkResource, @@ -220,6 +228,12 @@ "AsyncTasksResourceWithRawResponse", "TasksResourceWithStreamingResponse", "AsyncTasksResourceWithStreamingResponse", + "ClipboardResource", + "AsyncClipboardResource", + "ClipboardResourceWithRawResponse", + "AsyncClipboardResourceWithRawResponse", + "ClipboardResourceWithStreamingResponse", + "AsyncClipboardResourceWithStreamingResponse", "TimezoneResource", "AsyncTimezoneResource", "TimezoneResourceWithRawResponse", diff --git a/src/mobilerun_sdk/resources/devices/clipboard.py b/src/mobilerun_sdk/resources/devices/clipboard.py new file mode 100644 index 00000000..7c641500 --- /dev/null +++ b/src/mobilerun_sdk/resources/devices/clipboard.py @@ -0,0 +1,285 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from ..._types import Body, Omit, Query, Headers, NoneType, NotGiven, omit, not_given +from ..._utils import is_given, path_template, maybe_transform, strip_not_given, 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 ..._base_client import make_request_options +from ...types.devices import clipboard_set_params +from ...types.devices.clipboard_get_response import ClipboardGetResponse + +__all__ = ["ClipboardResource", "AsyncClipboardResource"] + + +class ClipboardResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> ClipboardResourceWithRawResponse: + """ + 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/droidrun/mobilerun-sdk-python#accessing-raw-response-data-eg-headers + """ + return ClipboardResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ClipboardResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/droidrun/mobilerun-sdk-python#with_streaming_response + """ + return ClipboardResourceWithStreamingResponse(self) + + def get( + self, + device_id: str, + *, + x_device_display_id: int | 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, + ) -> ClipboardGetResponse: + """Returns the current text content of the device's clipboard. + + Devices without + clipboard support return an unsupported-feature error. + + 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 device_id: + raise ValueError(f"Expected a non-empty value for `device_id` but received {device_id!r}") + extra_headers = { + **strip_not_given( + {"X-Device-Display-ID": str(x_device_display_id) if is_given(x_device_display_id) else not_given} + ), + **(extra_headers or {}), + } + return self._get( + path_template("/devices/{device_id}/clipboard", device_id=device_id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ClipboardGetResponse, + ) + + def set( + self, + device_id: str, + *, + text: str, + x_device_display_id: int | 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, + ) -> None: + """ + Replaces the device's clipboard content with the text in the request body; an + empty text clears the clipboard. Devices without clipboard support return an + unsupported-feature error. + + 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 device_id: + raise ValueError(f"Expected a non-empty value for `device_id` but received {device_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + extra_headers = { + **strip_not_given( + {"X-Device-Display-ID": str(x_device_display_id) if is_given(x_device_display_id) else not_given} + ), + **(extra_headers or {}), + } + return self._post( + path_template("/devices/{device_id}/clipboard", device_id=device_id), + body=maybe_transform({"text": text}, clipboard_set_params.ClipboardSetParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + +class AsyncClipboardResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncClipboardResourceWithRawResponse: + """ + 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/droidrun/mobilerun-sdk-python#accessing-raw-response-data-eg-headers + """ + return AsyncClipboardResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncClipboardResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/droidrun/mobilerun-sdk-python#with_streaming_response + """ + return AsyncClipboardResourceWithStreamingResponse(self) + + async def get( + self, + device_id: str, + *, + x_device_display_id: int | 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, + ) -> ClipboardGetResponse: + """Returns the current text content of the device's clipboard. + + Devices without + clipboard support return an unsupported-feature error. + + 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 device_id: + raise ValueError(f"Expected a non-empty value for `device_id` but received {device_id!r}") + extra_headers = { + **strip_not_given( + {"X-Device-Display-ID": str(x_device_display_id) if is_given(x_device_display_id) else not_given} + ), + **(extra_headers or {}), + } + return await self._get( + path_template("/devices/{device_id}/clipboard", device_id=device_id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ClipboardGetResponse, + ) + + async def set( + self, + device_id: str, + *, + text: str, + x_device_display_id: int | 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, + ) -> None: + """ + Replaces the device's clipboard content with the text in the request body; an + empty text clears the clipboard. Devices without clipboard support return an + unsupported-feature error. + + 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 device_id: + raise ValueError(f"Expected a non-empty value for `device_id` but received {device_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + extra_headers = { + **strip_not_given( + {"X-Device-Display-ID": str(x_device_display_id) if is_given(x_device_display_id) else not_given} + ), + **(extra_headers or {}), + } + return await self._post( + path_template("/devices/{device_id}/clipboard", device_id=device_id), + body=await async_maybe_transform({"text": text}, clipboard_set_params.ClipboardSetParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + +class ClipboardResourceWithRawResponse: + def __init__(self, clipboard: ClipboardResource) -> None: + self._clipboard = clipboard + + self.get = to_raw_response_wrapper( + clipboard.get, + ) + self.set = to_raw_response_wrapper( + clipboard.set, + ) + + +class AsyncClipboardResourceWithRawResponse: + def __init__(self, clipboard: AsyncClipboardResource) -> None: + self._clipboard = clipboard + + self.get = async_to_raw_response_wrapper( + clipboard.get, + ) + self.set = async_to_raw_response_wrapper( + clipboard.set, + ) + + +class ClipboardResourceWithStreamingResponse: + def __init__(self, clipboard: ClipboardResource) -> None: + self._clipboard = clipboard + + self.get = to_streamed_response_wrapper( + clipboard.get, + ) + self.set = to_streamed_response_wrapper( + clipboard.set, + ) + + +class AsyncClipboardResourceWithStreamingResponse: + def __init__(self, clipboard: AsyncClipboardResource) -> None: + self._clipboard = clipboard + + self.get = async_to_streamed_response_wrapper( + clipboard.get, + ) + self.set = async_to_streamed_response_wrapper( + clipboard.set, + ) diff --git a/src/mobilerun_sdk/resources/devices/devices.py b/src/mobilerun_sdk/resources/devices/devices.py index b3cfb65a..ad34958e 100644 --- a/src/mobilerun_sdk/resources/devices/devices.py +++ b/src/mobilerun_sdk/resources/devices/devices.py @@ -124,6 +124,14 @@ AsyncTimezoneResourceWithStreamingResponse, ) from ..._compat import cached_property +from .clipboard import ( + ClipboardResource, + AsyncClipboardResource, + ClipboardResourceWithRawResponse, + AsyncClipboardResourceWithRawResponse, + ClipboardResourceWithStreamingResponse, + AsyncClipboardResourceWithStreamingResponse, +) from .deep_link import ( DeepLinkResource, AsyncDeepLinkResource, @@ -224,6 +232,10 @@ def state(self) -> StateResource: def tasks(self) -> TasksResource: return TasksResource(self._client) + @cached_property + def clipboard(self) -> ClipboardResource: + return ClipboardResource(self._client) + @cached_property def timezone(self) -> TimezoneResource: return TimezoneResource(self._client) @@ -907,6 +919,10 @@ def state(self) -> AsyncStateResource: def tasks(self) -> AsyncTasksResource: return AsyncTasksResource(self._client) + @cached_property + def clipboard(self) -> AsyncClipboardResource: + return AsyncClipboardResource(self._client) + @cached_property def timezone(self) -> AsyncTimezoneResource: return AsyncTimezoneResource(self._client) @@ -1633,6 +1649,10 @@ def state(self) -> StateResourceWithRawResponse: def tasks(self) -> TasksResourceWithRawResponse: return TasksResourceWithRawResponse(self._devices.tasks) + @cached_property + def clipboard(self) -> ClipboardResourceWithRawResponse: + return ClipboardResourceWithRawResponse(self._devices.clipboard) + @cached_property def timezone(self) -> TimezoneResourceWithRawResponse: return TimezoneResourceWithRawResponse(self._devices.timezone) @@ -1750,6 +1770,10 @@ def state(self) -> AsyncStateResourceWithRawResponse: def tasks(self) -> AsyncTasksResourceWithRawResponse: return AsyncTasksResourceWithRawResponse(self._devices.tasks) + @cached_property + def clipboard(self) -> AsyncClipboardResourceWithRawResponse: + return AsyncClipboardResourceWithRawResponse(self._devices.clipboard) + @cached_property def timezone(self) -> AsyncTimezoneResourceWithRawResponse: return AsyncTimezoneResourceWithRawResponse(self._devices.timezone) @@ -1867,6 +1891,10 @@ def state(self) -> StateResourceWithStreamingResponse: def tasks(self) -> TasksResourceWithStreamingResponse: return TasksResourceWithStreamingResponse(self._devices.tasks) + @cached_property + def clipboard(self) -> ClipboardResourceWithStreamingResponse: + return ClipboardResourceWithStreamingResponse(self._devices.clipboard) + @cached_property def timezone(self) -> TimezoneResourceWithStreamingResponse: return TimezoneResourceWithStreamingResponse(self._devices.timezone) @@ -1984,6 +2012,10 @@ def state(self) -> AsyncStateResourceWithStreamingResponse: def tasks(self) -> AsyncTasksResourceWithStreamingResponse: return AsyncTasksResourceWithStreamingResponse(self._devices.tasks) + @cached_property + def clipboard(self) -> AsyncClipboardResourceWithStreamingResponse: + return AsyncClipboardResourceWithStreamingResponse(self._devices.clipboard) + @cached_property def timezone(self) -> AsyncTimezoneResourceWithStreamingResponse: return AsyncTimezoneResourceWithStreamingResponse(self._devices.timezone) diff --git a/src/mobilerun_sdk/resources/esims/esims.py b/src/mobilerun_sdk/resources/esims/esims.py index 39132580..49347cbe 100644 --- a/src/mobilerun_sdk/resources/esims/esims.py +++ b/src/mobilerun_sdk/resources/esims/esims.py @@ -7,7 +7,14 @@ import httpx -from ...types import esim_list_params, esim_create_params, esim_import_params, esim_update_params, esim_install_params +from ...types import ( + esim_list_params, + esim_create_params, + esim_import_params, + esim_update_params, + esim_install_params, + esim_selector_params, +) from ..._types import Body, Omit, Query, Headers, NoneType, NotGiven, omit, not_given from ..._utils import path_template, maybe_transform, async_maybe_transform from .messages import ( @@ -69,6 +76,7 @@ def create( self, *, idempotency_key: str | Omit = omit, + 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, @@ -85,6 +93,9 @@ def create( idempotency_key: Client-supplied key; replaying the same key returns the original purchase instead of buying again + name: Optional user-defined display label — NFC-normalized, up to 15 GRAPHEMES. Omit + or null for no label. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -95,7 +106,13 @@ def create( """ return self._post( "/numbers/esims", - body=maybe_transform({"idempotency_key": idempotency_key}, esim_create_params.EsimCreateParams), + body=maybe_transform( + { + "idempotency_key": idempotency_key, + "name": name, + }, + esim_create_params.EsimCreateParams, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -522,6 +539,8 @@ def install_status( def selector( self, *, + page: int | Omit = omit, + page_size: int | 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, @@ -532,12 +551,31 @@ def selector( """ Returns a lightweight list (id, msisdn, carrierName, status, masked iccid) for use in a message filter dropdown. Unlike `GET /esims`, this includes all - statuses, including retired eSIMs, and is not paginated. + statuses, including retired eSIMs. + + 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 """ return self._get( "/numbers/esims/selector", options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "page": page, + "page_size": page_size, + }, + esim_selector_params.EsimSelectorParams, + ), ), cast_to=EsimSelectorResponse, ) @@ -571,6 +609,7 @@ async def create( self, *, idempotency_key: str | Omit = omit, + 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, @@ -587,6 +626,9 @@ async def create( idempotency_key: Client-supplied key; replaying the same key returns the original purchase instead of buying again + name: Optional user-defined display label — NFC-normalized, up to 15 GRAPHEMES. Omit + or null for no label. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -597,7 +639,13 @@ async def create( """ return await self._post( "/numbers/esims", - body=await async_maybe_transform({"idempotency_key": idempotency_key}, esim_create_params.EsimCreateParams), + body=await async_maybe_transform( + { + "idempotency_key": idempotency_key, + "name": name, + }, + esim_create_params.EsimCreateParams, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -1024,6 +1072,8 @@ async def install_status( async def selector( self, *, + page: int | Omit = omit, + page_size: int | 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, @@ -1034,12 +1084,31 @@ async def selector( """ Returns a lightweight list (id, msisdn, carrierName, status, masked iccid) for use in a message filter dropdown. Unlike `GET /esims`, this includes all - statuses, including retired eSIMs, and is not paginated. + statuses, including retired eSIMs. + + 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 """ return await self._get( "/numbers/esims/selector", options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "page": page, + "page_size": page_size, + }, + esim_selector_params.EsimSelectorParams, + ), ), cast_to=EsimSelectorResponse, ) diff --git a/src/mobilerun_sdk/resources/models.py b/src/mobilerun_sdk/resources/models.py index 4bb2764f..e32c8324 100644 --- a/src/mobilerun_sdk/resources/models.py +++ b/src/mobilerun_sdk/resources/models.py @@ -51,7 +51,7 @@ def list( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ModelListResponse: - """Get all LLM models""" + """List available LLM models.""" return self._get( "/models", options=make_request_options( @@ -93,7 +93,7 @@ async def list( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ModelListResponse: - """Get all LLM models""" + """List available LLM models.""" return await self._get( "/models", options=make_request_options( diff --git a/src/mobilerun_sdk/resources/numbers/numbers.py b/src/mobilerun_sdk/resources/numbers/numbers.py index 2ec46354..7b39353f 100644 --- a/src/mobilerun_sdk/resources/numbers/numbers.py +++ b/src/mobilerun_sdk/resources/numbers/numbers.py @@ -2,11 +2,12 @@ from __future__ import annotations +from typing import Optional from typing_extensions import Literal import httpx -from ...types import number_list_params, number_create_params +from ...types import number_list_params, number_create_params, number_update_params from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given from ..._utils import path_template, maybe_transform, strip_not_given, async_maybe_transform from .messages import ( @@ -29,6 +30,7 @@ from ...types.number_list_response import NumberListResponse from ...types.number_create_response import NumberCreateResponse from ...types.number_delete_response import NumberDeleteResponse +from ...types.number_update_response import NumberUpdateResponse from ...types.number_purposes_response import NumberPurposesResponse from ...types.number_retrieve_response import NumberRetrieveResponse from ...types.number_countries_response import NumberCountriesResponse @@ -65,6 +67,7 @@ def create( *, billing_preference: Literal["included", "rent"] | Omit = omit, country: str | Omit = omit, + label: Optional[str] | Omit = omit, purpose: str | Omit = omit, idempotency_key: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -87,6 +90,10 @@ def create( country: Optional ISO 3166-1 alpha-2 country code from GET /numbers/countries. Cannot be combined with `purpose`. + label: User-defined display label — NFC-normalized, up to 100 GRAPHEMES (not UTF-16 + code units; an emoji/flag may span several). Display-only, never used for + routing. Also seeds the billing entity name at purchase. + purpose: Optional Mobilerun Phone purpose slug from GET /numbers/purposes. idempotency_key: Optional request idempotency key. @@ -106,6 +113,7 @@ def create( { "billing_preference": billing_preference, "country": country, + "label": label, "purpose": purpose, }, number_create_params.NumberCreateParams, @@ -149,6 +157,50 @@ def retrieve( cast_to=NumberRetrieveResponse, ) + def update( + self, + id: str, + *, + label: 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, + ) -> NumberUpdateResponse: + """Updates the phone number's user-defined display label. + + Omitting `label` leaves + it unchanged; setting it to null or an empty string clears it. The label is + capped at 100 characters, is display-only, and never affects routing. It also + seeds the billing entity name when set at purchase time; a later change here + does not rename the already-created billing entity. + + Args: + label: User-defined display label — NFC-normalized, up to 100 GRAPHEMES (not UTF-16 + code units; an emoji/flag may span several). Display-only, never used for + routing. Also seeds the billing entity name at purchase. + + 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._patch( + path_template("/numbers/phones/{id}", id=id), + body=maybe_transform({"label": label}, number_update_params.NumberUpdateParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NumberUpdateResponse, + ) + def list( self, *, @@ -313,6 +365,7 @@ async def create( *, billing_preference: Literal["included", "rent"] | Omit = omit, country: str | Omit = omit, + label: Optional[str] | Omit = omit, purpose: str | Omit = omit, idempotency_key: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -335,6 +388,10 @@ async def create( country: Optional ISO 3166-1 alpha-2 country code from GET /numbers/countries. Cannot be combined with `purpose`. + label: User-defined display label — NFC-normalized, up to 100 GRAPHEMES (not UTF-16 + code units; an emoji/flag may span several). Display-only, never used for + routing. Also seeds the billing entity name at purchase. + purpose: Optional Mobilerun Phone purpose slug from GET /numbers/purposes. idempotency_key: Optional request idempotency key. @@ -354,6 +411,7 @@ async def create( { "billing_preference": billing_preference, "country": country, + "label": label, "purpose": purpose, }, number_create_params.NumberCreateParams, @@ -397,6 +455,50 @@ async def retrieve( cast_to=NumberRetrieveResponse, ) + async def update( + self, + id: str, + *, + label: 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, + ) -> NumberUpdateResponse: + """Updates the phone number's user-defined display label. + + Omitting `label` leaves + it unchanged; setting it to null or an empty string clears it. The label is + capped at 100 characters, is display-only, and never affects routing. It also + seeds the billing entity name when set at purchase time; a later change here + does not rename the already-created billing entity. + + Args: + label: User-defined display label — NFC-normalized, up to 100 GRAPHEMES (not UTF-16 + code units; an emoji/flag may span several). Display-only, never used for + routing. Also seeds the billing entity name at purchase. + + 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._patch( + path_template("/numbers/phones/{id}", id=id), + body=await async_maybe_transform({"label": label}, number_update_params.NumberUpdateParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NumberUpdateResponse, + ) + async def list( self, *, @@ -542,6 +644,9 @@ def __init__(self, numbers: NumbersResource) -> None: self.retrieve = to_raw_response_wrapper( numbers.retrieve, ) + self.update = to_raw_response_wrapper( + numbers.update, + ) self.list = to_raw_response_wrapper( numbers.list, ) @@ -570,6 +675,9 @@ def __init__(self, numbers: AsyncNumbersResource) -> None: self.retrieve = async_to_raw_response_wrapper( numbers.retrieve, ) + self.update = async_to_raw_response_wrapper( + numbers.update, + ) self.list = async_to_raw_response_wrapper( numbers.list, ) @@ -598,6 +706,9 @@ def __init__(self, numbers: NumbersResource) -> None: self.retrieve = to_streamed_response_wrapper( numbers.retrieve, ) + self.update = to_streamed_response_wrapper( + numbers.update, + ) self.list = to_streamed_response_wrapper( numbers.list, ) @@ -626,6 +737,9 @@ def __init__(self, numbers: AsyncNumbersResource) -> None: self.retrieve = async_to_streamed_response_wrapper( numbers.retrieve, ) + self.update = async_to_streamed_response_wrapper( + numbers.update, + ) self.list = async_to_streamed_response_wrapper( numbers.list, ) diff --git a/src/mobilerun_sdk/resources/workflows/flows/flows.py b/src/mobilerun_sdk/resources/workflows/flows/flows.py index 79277a29..db390fcc 100644 --- a/src/mobilerun_sdk/resources/workflows/flows/flows.py +++ b/src/mobilerun_sdk/resources/workflows/flows/flows.py @@ -85,6 +85,7 @@ def create( notify_on_failure: bool | Omit = omit, notify_on_success: bool | Omit = omit, notify_webhook_id: Optional[str] | Omit = omit, + recording_enabled: bool | Omit = omit, self_healing_enabled: bool | Omit = omit, self_healing_max_attempts: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -125,6 +126,7 @@ def create( "notify_on_failure": notify_on_failure, "notify_on_success": notify_on_success, "notify_webhook_id": notify_webhook_id, + "recording_enabled": recording_enabled, "self_healing_enabled": self_healing_enabled, "self_healing_max_attempts": self_healing_max_attempts, }, @@ -184,6 +186,7 @@ def update( notify_on_failure: bool | Omit = omit, notify_on_success: bool | Omit = omit, notify_webhook_id: Optional[str] | Omit = omit, + recording_enabled: bool | Omit = omit, self_healing_enabled: bool | Omit = omit, self_healing_max_attempts: int | Omit = omit, trigger_id: str | Omit = omit, @@ -225,6 +228,7 @@ def update( "notify_on_failure": notify_on_failure, "notify_on_success": notify_on_success, "notify_webhook_id": notify_webhook_id, + "recording_enabled": recording_enabled, "self_healing_enabled": self_healing_enabled, "self_healing_max_attempts": self_healing_max_attempts, "trigger_id": trigger_id, @@ -538,6 +542,7 @@ async def create( notify_on_failure: bool | Omit = omit, notify_on_success: bool | Omit = omit, notify_webhook_id: Optional[str] | Omit = omit, + recording_enabled: bool | Omit = omit, self_healing_enabled: bool | Omit = omit, self_healing_max_attempts: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -578,6 +583,7 @@ async def create( "notify_on_failure": notify_on_failure, "notify_on_success": notify_on_success, "notify_webhook_id": notify_webhook_id, + "recording_enabled": recording_enabled, "self_healing_enabled": self_healing_enabled, "self_healing_max_attempts": self_healing_max_attempts, }, @@ -637,6 +643,7 @@ async def update( notify_on_failure: bool | Omit = omit, notify_on_success: bool | Omit = omit, notify_webhook_id: Optional[str] | Omit = omit, + recording_enabled: bool | Omit = omit, self_healing_enabled: bool | Omit = omit, self_healing_max_attempts: int | Omit = omit, trigger_id: str | Omit = omit, @@ -678,6 +685,7 @@ async def update( "notify_on_failure": notify_on_failure, "notify_on_success": notify_on_success, "notify_webhook_id": notify_webhook_id, + "recording_enabled": recording_enabled, "self_healing_enabled": self_healing_enabled, "self_healing_max_attempts": self_healing_max_attempts, "trigger_id": trigger_id, diff --git a/src/mobilerun_sdk/types/__init__.py b/src/mobilerun_sdk/types/__init__.py index 8d7932f6..92c43f3d 100644 --- a/src/mobilerun_sdk/types/__init__.py +++ b/src/mobilerun_sdk/types/__init__.py @@ -33,7 +33,6 @@ from .number_list_params import NumberListParams as NumberListParams from .task_list_response import TaskListResponse as TaskListResponse from .task_stop_response import TaskStopResponse as TaskStopResponse -from .agent_list_response import AgentListResponse as AgentListResponse from .app_delete_response import AppDeleteResponse as AppDeleteResponse from .carrier_list_params import CarrierListParams as CarrierListParams from .esim_install_params import EsimInstallParams as EsimInstallParams @@ -49,11 +48,13 @@ from .device_list_response import DeviceListResponse as DeviceListResponse from .esim_create_response import EsimCreateResponse as EsimCreateResponse from .esim_import_response import EsimImportResponse as EsimImportResponse +from .esim_selector_params import EsimSelectorParams as EsimSelectorParams from .esim_update_response import EsimUpdateResponse as EsimUpdateResponse from .file_delete_response import FileDeleteResponse as FileDeleteResponse from .file_update_response import FileUpdateResponse as FileUpdateResponse from .number_create_params import NumberCreateParams as NumberCreateParams from .number_list_response import NumberListResponse as NumberListResponse +from .number_update_params import NumberUpdateParams as NumberUpdateParams from .app_event_list_params import AppEventListParams as AppEventListParams from .app_retrieve_response import AppRetrieveResponse as AppRetrieveResponse from .carrier_create_params import CarrierCreateParams as CarrierCreateParams @@ -83,6 +84,7 @@ from .file_upload_url_params import FileUploadURLParams as FileUploadURLParams from .number_create_response import NumberCreateResponse as NumberCreateResponse from .number_delete_response import NumberDeleteResponse as NumberDeleteResponse +from .number_update_response import NumberUpdateResponse as NumberUpdateResponse from .task_retrieve_response import TaskRetrieveResponse as TaskRetrieveResponse from .app_event_list_response import AppEventListResponse as AppEventListResponse from .carrier_create_response import CarrierCreateResponse as CarrierCreateResponse diff --git a/src/mobilerun_sdk/types/agent_list_response.py b/src/mobilerun_sdk/types/agent_list_response.py deleted file mode 100644 index fc366d60..00000000 --- a/src/mobilerun_sdk/types/agent_list_response.py +++ /dev/null @@ -1,33 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import List, Optional -from typing_extensions import TypeAlias - -from pydantic import Field as FieldInfo - -from .._models import BaseModel - -__all__ = ["AgentListResponse", "AgentListResponseItem"] - - -class AgentListResponseItem(BaseModel): - id: int - - description: Optional[str] = None - - icon: str - - llm_model: str = FieldInfo(alias="llmModel") - - max_steps: int = FieldInfo(alias="maxSteps") - - name: str - - reasoning: bool - - subagent_model: Optional[str] = FieldInfo(alias="subagentModel", default=None) - - vision: bool - - -AgentListResponse: TypeAlias = List[AgentListResponseItem] diff --git a/src/mobilerun_sdk/types/app_list_response.py b/src/mobilerun_sdk/types/app_list_response.py index 5d85ac9c..c7088c0c 100644 --- a/src/mobilerun_sdk/types/app_list_response.py +++ b/src/mobilerun_sdk/types/app_list_response.py @@ -292,7 +292,7 @@ class ItemVersion(BaseModel): size_bytes: Optional[int] = FieldInfo(alias="sizeBytes", default=None) - source: Literal["user", "system", "portal", "store"] + source: Literal["user", "system", "portal", "store", "catalog"] status: Literal["queued", "available", "failed"] diff --git a/src/mobilerun_sdk/types/app_list_versions_response.py b/src/mobilerun_sdk/types/app_list_versions_response.py index a2bfedbd..a4181ed7 100644 --- a/src/mobilerun_sdk/types/app_list_versions_response.py +++ b/src/mobilerun_sdk/types/app_list_versions_response.py @@ -281,7 +281,7 @@ class Data(BaseModel): size_bytes: Optional[int] = FieldInfo(alias="sizeBytes", default=None) - source: Literal["user", "system", "portal", "store"] + source: Literal["user", "system", "portal", "store", "catalog"] status: Literal["queued", "available", "failed"] diff --git a/src/mobilerun_sdk/types/assistant/conversation_answer_question_params.py b/src/mobilerun_sdk/types/assistant/conversation_answer_question_params.py index 854a99b4..3a45efec 100644 --- a/src/mobilerun_sdk/types/assistant/conversation_answer_question_params.py +++ b/src/mobilerun_sdk/types/assistant/conversation_answer_question_params.py @@ -15,12 +15,6 @@ class ConversationAnswerQuestionParams(TypedDict, total=False): question_id: Required[Annotated[str, PropertyInfo(alias="questionId")]] - idempotency_key: Annotated[str, PropertyInfo(alias="Idempotency-Key")] - """Optional client key. - - Reusing the same key with the same question answers coalesces duplicate submits. - """ - class AnswerLabel(TypedDict, total=False): label: Required[str] diff --git a/src/mobilerun_sdk/types/assistant/conversation_history_response.py b/src/mobilerun_sdk/types/assistant/conversation_history_response.py index 56c14a09..8288033a 100644 --- a/src/mobilerun_sdk/types/assistant/conversation_history_response.py +++ b/src/mobilerun_sdk/types/assistant/conversation_history_response.py @@ -52,7 +52,7 @@ class Message(BaseModel): metadata: Optional[MessageMetadata] = None - source: Optional[Literal["cloud", "telegram", "api", "workflow"]] = None + source: Optional[Literal["cloud", "telegram", "api", "workflow", "notification"]] = None synthetic: Optional[bool] = None diff --git a/src/mobilerun_sdk/types/device_retrieve_capabilities_response.py b/src/mobilerun_sdk/types/device_retrieve_capabilities_response.py index d8b598b2..2e02c5ea 100644 --- a/src/mobilerun_sdk/types/device_retrieve_capabilities_response.py +++ b/src/mobilerun_sdk/types/device_retrieve_capabilities_response.py @@ -20,6 +20,8 @@ class Capabilities(BaseModel): camera_injection: bool = FieldInfo(alias="cameraInjection") + clipboard: bool + esim: bool files: bool diff --git a/src/mobilerun_sdk/types/devices/__init__.py b/src/mobilerun_sdk/types/devices/__init__.py index 18dfbe66..6b5d3494 100644 --- a/src/mobilerun_sdk/types/devices/__init__.py +++ b/src/mobilerun_sdk/types/devices/__init__.py @@ -29,6 +29,7 @@ from .state_time_response import StateTimeResponse as StateTimeResponse from .timezone_set_params import TimezoneSetParams as TimezoneSetParams from .action_global_params import ActionGlobalParams as ActionGlobalParams +from .clipboard_set_params import ClipboardSetParams as ClipboardSetParams from .esim_activate_params import EsimActivateParams as EsimActivateParams from .esim_status_response import EsimStatusResponse as EsimStatusResponse from .file_download_params import FileDownloadParams as FileDownloadParams @@ -40,6 +41,7 @@ from .proxy_status_response import ProxyStatusResponse as ProxyStatusResponse from .recording_list_params import RecordingListParams as RecordingListParams from .timezone_get_response import TimezoneGetResponse as TimezoneGetResponse +from .clipboard_get_response import ClipboardGetResponse as ClipboardGetResponse from .esim_activate_response import EsimActivateResponse as EsimActivateResponse from .file_download_response import FileDownloadResponse as FileDownloadResponse from .recording_start_params import RecordingStartParams as RecordingStartParams diff --git a/src/mobilerun_sdk/types/devices/clipboard_get_response.py b/src/mobilerun_sdk/types/devices/clipboard_get_response.py new file mode 100644 index 00000000..f5b53a04 --- /dev/null +++ b/src/mobilerun_sdk/types/devices/clipboard_get_response.py @@ -0,0 +1,16 @@ +# 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__ = ["ClipboardGetResponse"] + + +class ClipboardGetResponse(BaseModel): + text: str + + schema_: Optional[str] = FieldInfo(alias="$schema", default=None) + """A URL to the JSON Schema for this object.""" diff --git a/src/mobilerun_sdk/types/devices/clipboard_set_params.py b/src/mobilerun_sdk/types/devices/clipboard_set_params.py new file mode 100644 index 00000000..0d665423 --- /dev/null +++ b/src/mobilerun_sdk/types/devices/clipboard_set_params.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, Annotated, TypedDict + +from ..._utils import PropertyInfo + +__all__ = ["ClipboardSetParams"] + + +class ClipboardSetParams(TypedDict, total=False): + text: Required[str] + + x_device_display_id: Annotated[int, PropertyInfo(alias="X-Device-Display-ID")] diff --git a/src/mobilerun_sdk/types/esim_create_params.py b/src/mobilerun_sdk/types/esim_create_params.py index 1e76c2c9..973cbd1d 100644 --- a/src/mobilerun_sdk/types/esim_create_params.py +++ b/src/mobilerun_sdk/types/esim_create_params.py @@ -2,6 +2,7 @@ from __future__ import annotations +from typing import Optional from typing_extensions import Annotated, TypedDict from .._utils import PropertyInfo @@ -15,3 +16,9 @@ class EsimCreateParams(TypedDict, total=False): Client-supplied key; replaying the same key returns the original purchase instead of buying again """ + + name: Optional[str] + """Optional user-defined display label — NFC-normalized, up to 15 GRAPHEMES. + + Omit or null for no label. + """ diff --git a/src/mobilerun_sdk/types/esim_list_response.py b/src/mobilerun_sdk/types/esim_list_response.py index a0440fe0..5c236e21 100644 --- a/src/mobilerun_sdk/types/esim_list_response.py +++ b/src/mobilerun_sdk/types/esim_list_response.py @@ -9,10 +9,10 @@ from .._models import BaseModel from .shared.pagination import Pagination -__all__ = ["EsimListResponse", "Data", "DataItem"] +__all__ = ["EsimListResponse", "Item"] -class DataItem(BaseModel): +class Item(BaseModel): id: str carrier_name: Optional[str] = FieldInfo(alias="carrierName", default=None) @@ -66,11 +66,7 @@ class DataItem(BaseModel): ] = FieldInfo(alias="rentStatus", default=None) -class Data(BaseModel): - items: List[DataItem] +class EsimListResponse(BaseModel): + items: List[Item] pagination: Pagination - - -class EsimListResponse(BaseModel): - data: Data diff --git a/src/mobilerun_sdk/types/esim_selector_params.py b/src/mobilerun_sdk/types/esim_selector_params.py new file mode 100644 index 00000000..d5cd46c1 --- /dev/null +++ b/src/mobilerun_sdk/types/esim_selector_params.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Annotated, TypedDict + +from .._utils import PropertyInfo + +__all__ = ["EsimSelectorParams"] + + +class EsimSelectorParams(TypedDict, total=False): + page: int + + page_size: Annotated[int, PropertyInfo(alias="pageSize")] diff --git a/src/mobilerun_sdk/types/esim_selector_response.py b/src/mobilerun_sdk/types/esim_selector_response.py index 7eedbf93..4288bcf9 100644 --- a/src/mobilerun_sdk/types/esim_selector_response.py +++ b/src/mobilerun_sdk/types/esim_selector_response.py @@ -6,11 +6,12 @@ from pydantic import Field as FieldInfo from .._models import BaseModel +from .shared.pagination import Pagination -__all__ = ["EsimSelectorResponse", "Data", "DataItem"] +__all__ = ["EsimSelectorResponse", "Item"] -class DataItem(BaseModel): +class Item(BaseModel): id: str carrier_name: Optional[str] = FieldInfo(alias="carrierName", default=None) @@ -26,9 +27,7 @@ class DataItem(BaseModel): status: Literal["in_stock", "owned", "installing", "installed", "install_failed", "retired"] -class Data(BaseModel): - items: List[DataItem] - - class EsimSelectorResponse(BaseModel): - data: Data + items: List[Item] + + pagination: Pagination diff --git a/src/mobilerun_sdk/types/esims/message_list_response.py b/src/mobilerun_sdk/types/esims/message_list_response.py index 077b2c78..c04b3d46 100644 --- a/src/mobilerun_sdk/types/esims/message_list_response.py +++ b/src/mobilerun_sdk/types/esims/message_list_response.py @@ -9,10 +9,10 @@ from ..._models import BaseModel from ..shared.pagination import Pagination -__all__ = ["MessageListResponse", "Data", "DataItem"] +__all__ = ["MessageListResponse", "Item"] -class DataItem(BaseModel): +class Item(BaseModel): id: str body: Optional[str] = None @@ -38,11 +38,7 @@ class DataItem(BaseModel): status: Literal["received", "queued", "claimed", "sending", "sent", "sent_unconfirmed", "delivered", "failed"] -class Data(BaseModel): - items: List[DataItem] +class MessageListResponse(BaseModel): + items: List[Item] pagination: Pagination - - -class MessageListResponse(BaseModel): - data: Data diff --git a/src/mobilerun_sdk/types/message_list_response.py b/src/mobilerun_sdk/types/message_list_response.py index aaaa20b6..9bce1d48 100644 --- a/src/mobilerun_sdk/types/message_list_response.py +++ b/src/mobilerun_sdk/types/message_list_response.py @@ -9,10 +9,10 @@ from .._models import BaseModel from .shared.pagination import Pagination -__all__ = ["MessageListResponse", "Data", "DataItem"] +__all__ = ["MessageListResponse", "Item"] -class DataItem(BaseModel): +class Item(BaseModel): id: str body: Optional[str] = None @@ -38,11 +38,7 @@ class DataItem(BaseModel): status: Literal["received", "queued", "claimed", "sending", "sent", "sent_unconfirmed", "delivered", "failed"] -class Data(BaseModel): - items: List[DataItem] +class MessageListResponse(BaseModel): + items: List[Item] pagination: Pagination - - -class MessageListResponse(BaseModel): - data: Data diff --git a/src/mobilerun_sdk/types/messages/conversation_list_response.py b/src/mobilerun_sdk/types/messages/conversation_list_response.py index da1e4585..6e3e622e 100644 --- a/src/mobilerun_sdk/types/messages/conversation_list_response.py +++ b/src/mobilerun_sdk/types/messages/conversation_list_response.py @@ -8,10 +8,10 @@ from ..._models import BaseModel -__all__ = ["ConversationListResponse", "Data", "DataItem", "DataItemLastMessage", "DataNextCursor"] +__all__ = ["ConversationListResponse", "Item", "ItemLastMessage", "NextCursor"] -class DataItemLastMessage(BaseModel): +class ItemLastMessage(BaseModel): id: str body: Optional[str] = None @@ -23,27 +23,23 @@ class DataItemLastMessage(BaseModel): status: Literal["received", "queued", "claimed", "sending", "sent", "sent_unconfirmed", "delivered", "failed"] -class DataItem(BaseModel): +class Item(BaseModel): esim_ids: List[str] = FieldInfo(alias="esimIds") - last_message: DataItemLastMessage = FieldInfo(alias="lastMessage") + last_message: ItemLastMessage = FieldInfo(alias="lastMessage") peer_key: str = FieldInfo(alias="peerKey") unread_count: int = FieldInfo(alias="unreadCount") -class DataNextCursor(BaseModel): +class NextCursor(BaseModel): last_message_id: str = FieldInfo(alias="lastMessageId") last_occurred_at: datetime = FieldInfo(alias="lastOccurredAt") -class Data(BaseModel): - items: List[DataItem] - - next_cursor: Optional[DataNextCursor] = FieldInfo(alias="nextCursor", default=None) - - class ConversationListResponse(BaseModel): - data: Data + items: List[Item] + + next_cursor: Optional[NextCursor] = FieldInfo(alias="nextCursor", default=None) diff --git a/src/mobilerun_sdk/types/model_list_response.py b/src/mobilerun_sdk/types/model_list_response.py index 81d802a5..8a1931c6 100644 --- a/src/mobilerun_sdk/types/model_list_response.py +++ b/src/mobilerun_sdk/types/model_list_response.py @@ -8,17 +8,22 @@ class Data(BaseModel): - id: Optional[str] = None + id: str + """Model identifier""" - created: Optional[float] = None + owned_by: str + """Model owner/provider""" - object: Optional[str] = None + created: Optional[int] = None + """Creation timestamp""" - owned_by: Optional[str] = None + object: Optional[str] = None + """Object type""" class ModelListResponse(BaseModel): - data: Optional[List[Data]] = None + data: List[Data] + """Available models""" object: Optional[str] = None - """Always "list" for list responses""" + """Object type""" diff --git a/src/mobilerun_sdk/types/number_create_params.py b/src/mobilerun_sdk/types/number_create_params.py index 5083087f..6098fc62 100644 --- a/src/mobilerun_sdk/types/number_create_params.py +++ b/src/mobilerun_sdk/types/number_create_params.py @@ -2,6 +2,7 @@ from __future__ import annotations +from typing import Optional from typing_extensions import Literal, Annotated, TypedDict from .._utils import PropertyInfo @@ -22,6 +23,13 @@ class NumberCreateParams(TypedDict, total=False): Cannot be combined with `purpose`. """ + label: Optional[str] + """ + User-defined display label — NFC-normalized, up to 100 GRAPHEMES (not UTF-16 + code units; an emoji/flag may span several). Display-only, never used for + routing. Also seeds the billing entity name at purchase. + """ + purpose: str """Optional Mobilerun Phone purpose slug from GET /numbers/purposes.""" diff --git a/src/mobilerun_sdk/types/number_delete_response.py b/src/mobilerun_sdk/types/number_delete_response.py index 4049d3d8..4e9351eb 100644 --- a/src/mobilerun_sdk/types/number_delete_response.py +++ b/src/mobilerun_sdk/types/number_delete_response.py @@ -30,6 +30,8 @@ class Data(BaseModel): current_period_end: Optional[datetime] = FieldInfo(alias="currentPeriodEnd", default=None) + label: Optional[str] = None + phone_number: Optional[str] = FieldInfo(alias="phoneNumber", default=None) purpose: Optional[str] = None diff --git a/src/mobilerun_sdk/types/number_list_response.py b/src/mobilerun_sdk/types/number_list_response.py index 2ada07db..59a3eccd 100644 --- a/src/mobilerun_sdk/types/number_list_response.py +++ b/src/mobilerun_sdk/types/number_list_response.py @@ -9,10 +9,10 @@ from .._models import BaseModel from .shared.pagination import Pagination -__all__ = ["NumberListResponse", "Data", "DataItem"] +__all__ = ["NumberListResponse", "Item"] -class DataItem(BaseModel): +class Item(BaseModel): id: str cancel_at_period_end: bool = FieldInfo(alias="cancelAtPeriodEnd") @@ -31,6 +31,8 @@ class DataItem(BaseModel): current_period_end: Optional[datetime] = FieldInfo(alias="currentPeriodEnd", default=None) + label: Optional[str] = None + phone_number: Optional[str] = FieldInfo(alias="phoneNumber", default=None) purpose: Optional[str] = None @@ -40,11 +42,7 @@ class DataItem(BaseModel): updated_at: Optional[datetime] = FieldInfo(alias="updatedAt", default=None) -class Data(BaseModel): - items: List[DataItem] +class NumberListResponse(BaseModel): + items: List[Item] pagination: Pagination - - -class NumberListResponse(BaseModel): - data: Data diff --git a/src/mobilerun_sdk/types/number_retrieve_response.py b/src/mobilerun_sdk/types/number_retrieve_response.py index 70a88c99..dcd5843f 100644 --- a/src/mobilerun_sdk/types/number_retrieve_response.py +++ b/src/mobilerun_sdk/types/number_retrieve_response.py @@ -30,6 +30,8 @@ class Data(BaseModel): current_period_end: Optional[datetime] = FieldInfo(alias="currentPeriodEnd", default=None) + label: Optional[str] = None + phone_number: Optional[str] = FieldInfo(alias="phoneNumber", default=None) purpose: Optional[str] = None diff --git a/src/mobilerun_sdk/types/number_update_params.py b/src/mobilerun_sdk/types/number_update_params.py new file mode 100644 index 00000000..0c4372ba --- /dev/null +++ b/src/mobilerun_sdk/types/number_update_params.py @@ -0,0 +1,17 @@ +# 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__ = ["NumberUpdateParams"] + + +class NumberUpdateParams(TypedDict, total=False): + label: Optional[str] + """ + User-defined display label — NFC-normalized, up to 100 GRAPHEMES (not UTF-16 + code units; an emoji/flag may span several). Display-only, never used for + routing. Also seeds the billing entity name at purchase. + """ diff --git a/src/mobilerun_sdk/types/number_update_response.py b/src/mobilerun_sdk/types/number_update_response.py new file mode 100644 index 00000000..e6166cb1 --- /dev/null +++ b/src/mobilerun_sdk/types/number_update_response.py @@ -0,0 +1,45 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from datetime import datetime +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = ["NumberUpdateResponse", "Data"] + + +class Data(BaseModel): + id: str + + cancel_at_period_end: bool = FieldInfo(alias="cancelAtPeriodEnd") + + cancellable: bool + + can_send: bool = FieldInfo(alias="canSend") + + capabilities: Optional[List[Literal["sms", "voice"]]] = None + + checkout_url: Optional[str] = FieldInfo(alias="checkoutUrl", default=None) + + country_code: Optional[str] = FieldInfo(alias="countryCode", default=None) + + created_at: Optional[datetime] = FieldInfo(alias="createdAt", default=None) + + current_period_end: Optional[datetime] = FieldInfo(alias="currentPeriodEnd", default=None) + + label: Optional[str] = None + + phone_number: Optional[str] = FieldInfo(alias="phoneNumber", default=None) + + purpose: Optional[str] = None + + state: Literal["awaiting_payment", "provisioning", "active", "cancel_scheduled", "expired", "failed"] + + updated_at: Optional[datetime] = FieldInfo(alias="updatedAt", default=None) + + +class NumberUpdateResponse(BaseModel): + data: Data diff --git a/src/mobilerun_sdk/types/numbers/message_list_response.py b/src/mobilerun_sdk/types/numbers/message_list_response.py index 3d9b3c3d..62ad7986 100644 --- a/src/mobilerun_sdk/types/numbers/message_list_response.py +++ b/src/mobilerun_sdk/types/numbers/message_list_response.py @@ -7,11 +7,12 @@ from pydantic import Field as FieldInfo from ..._models import BaseModel +from ..shared.pagination import Pagination -__all__ = ["MessageListResponse", "Data", "DataItem", "DataPagination"] +__all__ = ["MessageListResponse", "Item"] -class DataItem(BaseModel): +class Item(BaseModel): id: str body: Optional[str] = None @@ -35,25 +36,7 @@ class DataItem(BaseModel): status: Literal["received", "queued", "claimed", "sending", "sent", "sent_unconfirmed", "delivered", "failed"] -class DataPagination(BaseModel): - has_next: bool = FieldInfo(alias="hasNext") - - has_prev: bool = FieldInfo(alias="hasPrev") - - page: int - - pages: int - - page_size: int = FieldInfo(alias="pageSize") - - total: int - - -class Data(BaseModel): - items: List[DataItem] - - pagination: DataPagination - - class MessageListResponse(BaseModel): - data: Data + items: List[Item] + + pagination: Pagination diff --git a/src/mobilerun_sdk/types/store/app_list_response.py b/src/mobilerun_sdk/types/store/app_list_response.py index 7ee43700..c8dce76d 100644 --- a/src/mobilerun_sdk/types/store/app_list_response.py +++ b/src/mobilerun_sdk/types/store/app_list_response.py @@ -282,7 +282,7 @@ class ItemVersion(BaseModel): size_bytes: Optional[int] = FieldInfo(alias="sizeBytes", default=None) - source: Literal["user", "system", "portal", "store"] + source: Literal["user", "system", "portal", "store", "catalog"] status: Literal["queued", "available", "failed"] diff --git a/src/mobilerun_sdk/types/store/app_retrieve_response.py b/src/mobilerun_sdk/types/store/app_retrieve_response.py index a8cff78c..c9461110 100644 --- a/src/mobilerun_sdk/types/store/app_retrieve_response.py +++ b/src/mobilerun_sdk/types/store/app_retrieve_response.py @@ -281,7 +281,7 @@ class DataVersion(BaseModel): size_bytes: Optional[int] = FieldInfo(alias="sizeBytes", default=None) - source: Literal["user", "system", "portal", "store"] + source: Literal["user", "system", "portal", "store", "catalog"] status: Literal["queued", "available", "failed"] diff --git a/src/mobilerun_sdk/types/workflows/event_dry_run_response.py b/src/mobilerun_sdk/types/workflows/event_dry_run_response.py index c3509640..b04779e3 100644 --- a/src/mobilerun_sdk/types/workflows/event_dry_run_response.py +++ b/src/mobilerun_sdk/types/workflows/event_dry_run_response.py @@ -79,6 +79,8 @@ class DataMatchedFlowFlow(BaseModel): owner_id: str = FieldInfo(alias="ownerId") + recording_enabled: bool = FieldInfo(alias="recordingEnabled") + self_healing_enabled: bool = FieldInfo(alias="selfHealingEnabled") self_healing_max_attempts: int = FieldInfo(alias="selfHealingMaxAttempts") diff --git a/src/mobilerun_sdk/types/workflows/execution_abort_response.py b/src/mobilerun_sdk/types/workflows/execution_abort_response.py index 853122f0..0d9468bf 100644 --- a/src/mobilerun_sdk/types/workflows/execution_abort_response.py +++ b/src/mobilerun_sdk/types/workflows/execution_abort_response.py @@ -25,6 +25,15 @@ class Data(BaseModel): kind: Literal["live", "dry_run"] + recording_device_id: Optional[str] = FieldInfo(alias="recordingDeviceId", default=None) + + recording_id: Optional[str] = FieldInfo(alias="recordingId", default=None) + """ + Device-recording id (devices-api) for this execution, set once the worker starts + a recording. Null when the flow has recording disabled, no device is bound, or + the recording failed to start. + """ + started_at: Optional[str] = FieldInfo(alias="startedAt", default=None) status: Optional[Literal["pending", "running", "success", "failed", "cancelled", "skipped", "invalid"]] = None diff --git a/src/mobilerun_sdk/types/workflows/execution_list_response.py b/src/mobilerun_sdk/types/workflows/execution_list_response.py index 93c5a45c..b7c4993f 100644 --- a/src/mobilerun_sdk/types/workflows/execution_list_response.py +++ b/src/mobilerun_sdk/types/workflows/execution_list_response.py @@ -28,6 +28,15 @@ class Item(BaseModel): kind: Literal["live", "dry_run"] + recording_device_id: Optional[str] = FieldInfo(alias="recordingDeviceId", default=None) + + recording_id: Optional[str] = FieldInfo(alias="recordingId", default=None) + """ + Device-recording id (devices-api) for this execution, set once the worker starts + a recording. Null when the flow has recording disabled, no device is bound, or + the recording failed to start. + """ + started_at: Optional[str] = FieldInfo(alias="startedAt", default=None) status: Optional[Literal["pending", "running", "success", "failed", "cancelled", "skipped", "invalid"]] = None diff --git a/src/mobilerun_sdk/types/workflows/execution_retrieve_response.py b/src/mobilerun_sdk/types/workflows/execution_retrieve_response.py index d67a8f4f..de6c2d19 100644 --- a/src/mobilerun_sdk/types/workflows/execution_retrieve_response.py +++ b/src/mobilerun_sdk/types/workflows/execution_retrieve_response.py @@ -44,6 +44,15 @@ class Data(BaseModel): kind: Literal["live", "dry_run"] + recording_device_id: Optional[str] = FieldInfo(alias="recordingDeviceId", default=None) + + recording_id: Optional[str] = FieldInfo(alias="recordingId", default=None) + """ + Device-recording id (devices-api) for this execution, set once the worker starts + a recording. Null when the flow has recording disabled, no device is bound, or + the recording failed to start. + """ + started_at: Optional[str] = FieldInfo(alias="startedAt", default=None) status: Optional[Literal["pending", "running", "success", "failed", "cancelled", "skipped", "invalid"]] = None diff --git a/src/mobilerun_sdk/types/workflows/flow_clone_response.py b/src/mobilerun_sdk/types/workflows/flow_clone_response.py index 0688cdc4..14ed8d1f 100644 --- a/src/mobilerun_sdk/types/workflows/flow_clone_response.py +++ b/src/mobilerun_sdk/types/workflows/flow_clone_response.py @@ -51,6 +51,8 @@ class Data(BaseModel): owner_id: str = FieldInfo(alias="ownerId") + recording_enabled: bool = FieldInfo(alias="recordingEnabled") + self_healing_enabled: bool = FieldInfo(alias="selfHealingEnabled") self_healing_max_attempts: int = FieldInfo(alias="selfHealingMaxAttempts") diff --git a/src/mobilerun_sdk/types/workflows/flow_create_params.py b/src/mobilerun_sdk/types/workflows/flow_create_params.py index 002ee9d7..cde7b202 100644 --- a/src/mobilerun_sdk/types/workflows/flow_create_params.py +++ b/src/mobilerun_sdk/types/workflows/flow_create_params.py @@ -36,6 +36,8 @@ class FlowCreateParams(TypedDict, total=False): notify_webhook_id: Annotated[Optional[str], PropertyInfo(alias="notifyWebhookId")] + recording_enabled: Annotated[bool, PropertyInfo(alias="recordingEnabled")] + self_healing_enabled: Annotated[bool, PropertyInfo(alias="selfHealingEnabled")] self_healing_max_attempts: Annotated[int, PropertyInfo(alias="selfHealingMaxAttempts")] diff --git a/src/mobilerun_sdk/types/workflows/flow_create_response.py b/src/mobilerun_sdk/types/workflows/flow_create_response.py index d35319d3..a840cf45 100644 --- a/src/mobilerun_sdk/types/workflows/flow_create_response.py +++ b/src/mobilerun_sdk/types/workflows/flow_create_response.py @@ -51,6 +51,8 @@ class Data(BaseModel): owner_id: str = FieldInfo(alias="ownerId") + recording_enabled: bool = FieldInfo(alias="recordingEnabled") + self_healing_enabled: bool = FieldInfo(alias="selfHealingEnabled") self_healing_max_attempts: int = FieldInfo(alias="selfHealingMaxAttempts") diff --git a/src/mobilerun_sdk/types/workflows/flow_list_response.py b/src/mobilerun_sdk/types/workflows/flow_list_response.py index 36819ffe..5cd546ab 100644 --- a/src/mobilerun_sdk/types/workflows/flow_list_response.py +++ b/src/mobilerun_sdk/types/workflows/flow_list_response.py @@ -52,6 +52,8 @@ class Item(BaseModel): owner_id: str = FieldInfo(alias="ownerId") + recording_enabled: bool = FieldInfo(alias="recordingEnabled") + self_healing_enabled: bool = FieldInfo(alias="selfHealingEnabled") self_healing_max_attempts: int = FieldInfo(alias="selfHealingMaxAttempts") diff --git a/src/mobilerun_sdk/types/workflows/flow_retrieve_response.py b/src/mobilerun_sdk/types/workflows/flow_retrieve_response.py index 3c017332..dab4caa9 100644 --- a/src/mobilerun_sdk/types/workflows/flow_retrieve_response.py +++ b/src/mobilerun_sdk/types/workflows/flow_retrieve_response.py @@ -51,6 +51,8 @@ class Data(BaseModel): owner_id: str = FieldInfo(alias="ownerId") + recording_enabled: bool = FieldInfo(alias="recordingEnabled") + self_healing_enabled: bool = FieldInfo(alias="selfHealingEnabled") self_healing_max_attempts: int = FieldInfo(alias="selfHealingMaxAttempts") diff --git a/src/mobilerun_sdk/types/workflows/flow_unblock_response.py b/src/mobilerun_sdk/types/workflows/flow_unblock_response.py index f36de0cd..0429bba4 100644 --- a/src/mobilerun_sdk/types/workflows/flow_unblock_response.py +++ b/src/mobilerun_sdk/types/workflows/flow_unblock_response.py @@ -51,6 +51,8 @@ class Data(BaseModel): owner_id: str = FieldInfo(alias="ownerId") + recording_enabled: bool = FieldInfo(alias="recordingEnabled") + self_healing_enabled: bool = FieldInfo(alias="selfHealingEnabled") self_healing_max_attempts: int = FieldInfo(alias="selfHealingMaxAttempts") diff --git a/src/mobilerun_sdk/types/workflows/flow_update_params.py b/src/mobilerun_sdk/types/workflows/flow_update_params.py index d7f9e64a..8db31b2e 100644 --- a/src/mobilerun_sdk/types/workflows/flow_update_params.py +++ b/src/mobilerun_sdk/types/workflows/flow_update_params.py @@ -32,6 +32,8 @@ class FlowUpdateParams(TypedDict, total=False): notify_webhook_id: Annotated[Optional[str], PropertyInfo(alias="notifyWebhookId")] + recording_enabled: Annotated[bool, PropertyInfo(alias="recordingEnabled")] + self_healing_enabled: Annotated[bool, PropertyInfo(alias="selfHealingEnabled")] self_healing_max_attempts: Annotated[int, PropertyInfo(alias="selfHealingMaxAttempts")] diff --git a/src/mobilerun_sdk/types/workflows/flow_update_response.py b/src/mobilerun_sdk/types/workflows/flow_update_response.py index b33c1b3e..ad10348f 100644 --- a/src/mobilerun_sdk/types/workflows/flow_update_response.py +++ b/src/mobilerun_sdk/types/workflows/flow_update_response.py @@ -51,6 +51,8 @@ class Data(BaseModel): owner_id: str = FieldInfo(alias="ownerId") + recording_enabled: bool = FieldInfo(alias="recordingEnabled") + self_healing_enabled: bool = FieldInfo(alias="selfHealingEnabled") self_healing_max_attempts: int = FieldInfo(alias="selfHealingMaxAttempts") diff --git a/tests/api_resources/assistant/test_conversations.py b/tests/api_resources/assistant/test_conversations.py index 56def693..32ef5823 100644 --- a/tests/api_resources/assistant/test_conversations.py +++ b/tests/api_resources/assistant/test_conversations.py @@ -244,16 +244,6 @@ def test_method_answer_question(self, client: Mobilerun) -> None: ) assert_matches_type(ConversationAnswerQuestionResponse, conversation, path=["response"]) - @pytest.mark.skip(reason="Mock server tests are disabled") - @parametrize - def test_method_answer_question_with_all_params(self, client: Mobilerun) -> None: - conversation = client.assistant.conversations.answer_question( - answers=[[{"label": "x"}]], - question_id="x", - idempotency_key="Idempotency-Key", - ) - assert_matches_type(ConversationAnswerQuestionResponse, conversation, path=["response"]) - @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize def test_raw_response_answer_question(self, client: Mobilerun) -> None: @@ -662,16 +652,6 @@ async def test_method_answer_question(self, async_client: AsyncMobilerun) -> Non ) assert_matches_type(ConversationAnswerQuestionResponse, conversation, path=["response"]) - @pytest.mark.skip(reason="Mock server tests are disabled") - @parametrize - async def test_method_answer_question_with_all_params(self, async_client: AsyncMobilerun) -> None: - conversation = await async_client.assistant.conversations.answer_question( - answers=[[{"label": "x"}]], - question_id="x", - idempotency_key="Idempotency-Key", - ) - assert_matches_type(ConversationAnswerQuestionResponse, conversation, path=["response"]) - @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize async def test_raw_response_answer_question(self, async_client: AsyncMobilerun) -> None: diff --git a/tests/api_resources/devices/test_clipboard.py b/tests/api_resources/devices/test_clipboard.py new file mode 100644 index 00000000..aef973ba --- /dev/null +++ b/tests/api_resources/devices/test_clipboard.py @@ -0,0 +1,238 @@ +# 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 mobilerun_sdk import Mobilerun, AsyncMobilerun +from mobilerun_sdk.types.devices import ClipboardGetResponse + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestClipboard: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_get(self, client: Mobilerun) -> None: + clipboard = client.devices.clipboard.get( + device_id="deviceId", + ) + assert_matches_type(ClipboardGetResponse, clipboard, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_get_with_all_params(self, client: Mobilerun) -> None: + clipboard = client.devices.clipboard.get( + device_id="deviceId", + x_device_display_id=0, + ) + assert_matches_type(ClipboardGetResponse, clipboard, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_raw_response_get(self, client: Mobilerun) -> None: + response = client.devices.clipboard.with_raw_response.get( + device_id="deviceId", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + clipboard = response.parse() + assert_matches_type(ClipboardGetResponse, clipboard, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_streaming_response_get(self, client: Mobilerun) -> None: + with client.devices.clipboard.with_streaming_response.get( + device_id="deviceId", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + clipboard = response.parse() + assert_matches_type(ClipboardGetResponse, clipboard, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_path_params_get(self, client: Mobilerun) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `device_id` but received ''"): + client.devices.clipboard.with_raw_response.get( + device_id="", + ) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_set(self, client: Mobilerun) -> None: + clipboard = client.devices.clipboard.set( + device_id="deviceId", + text="text", + ) + assert clipboard is None + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_set_with_all_params(self, client: Mobilerun) -> None: + clipboard = client.devices.clipboard.set( + device_id="deviceId", + text="text", + x_device_display_id=0, + ) + assert clipboard is None + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_raw_response_set(self, client: Mobilerun) -> None: + response = client.devices.clipboard.with_raw_response.set( + device_id="deviceId", + text="text", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + clipboard = response.parse() + assert clipboard is None + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_streaming_response_set(self, client: Mobilerun) -> None: + with client.devices.clipboard.with_streaming_response.set( + device_id="deviceId", + text="text", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + clipboard = response.parse() + assert clipboard is None + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_path_params_set(self, client: Mobilerun) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `device_id` but received ''"): + client.devices.clipboard.with_raw_response.set( + device_id="", + text="text", + ) + + +class TestAsyncClipboard: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_get(self, async_client: AsyncMobilerun) -> None: + clipboard = await async_client.devices.clipboard.get( + device_id="deviceId", + ) + assert_matches_type(ClipboardGetResponse, clipboard, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_get_with_all_params(self, async_client: AsyncMobilerun) -> None: + clipboard = await async_client.devices.clipboard.get( + device_id="deviceId", + x_device_display_id=0, + ) + assert_matches_type(ClipboardGetResponse, clipboard, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_raw_response_get(self, async_client: AsyncMobilerun) -> None: + response = await async_client.devices.clipboard.with_raw_response.get( + device_id="deviceId", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + clipboard = await response.parse() + assert_matches_type(ClipboardGetResponse, clipboard, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_streaming_response_get(self, async_client: AsyncMobilerun) -> None: + async with async_client.devices.clipboard.with_streaming_response.get( + device_id="deviceId", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + clipboard = await response.parse() + assert_matches_type(ClipboardGetResponse, clipboard, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_path_params_get(self, async_client: AsyncMobilerun) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `device_id` but received ''"): + await async_client.devices.clipboard.with_raw_response.get( + device_id="", + ) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_set(self, async_client: AsyncMobilerun) -> None: + clipboard = await async_client.devices.clipboard.set( + device_id="deviceId", + text="text", + ) + assert clipboard is None + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_set_with_all_params(self, async_client: AsyncMobilerun) -> None: + clipboard = await async_client.devices.clipboard.set( + device_id="deviceId", + text="text", + x_device_display_id=0, + ) + assert clipboard is None + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_raw_response_set(self, async_client: AsyncMobilerun) -> None: + response = await async_client.devices.clipboard.with_raw_response.set( + device_id="deviceId", + text="text", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + clipboard = await response.parse() + assert clipboard is None + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_streaming_response_set(self, async_client: AsyncMobilerun) -> None: + async with async_client.devices.clipboard.with_streaming_response.set( + device_id="deviceId", + text="text", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + clipboard = await response.parse() + assert clipboard is None + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_path_params_set(self, async_client: AsyncMobilerun) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `device_id` but received ''"): + await async_client.devices.clipboard.with_raw_response.set( + device_id="", + text="text", + ) diff --git a/tests/api_resources/test_agents.py b/tests/api_resources/test_agents.py deleted file mode 100644 index 8087dead..00000000 --- a/tests/api_resources/test_agents.py +++ /dev/null @@ -1,80 +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 mobilerun_sdk import Mobilerun, AsyncMobilerun -from mobilerun_sdk.types import AgentListResponse - -base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") - - -class TestAgents: - parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - - @pytest.mark.skip(reason="Mock server tests are disabled") - @parametrize - def test_method_list(self, client: Mobilerun) -> None: - agent = client.agents.list() - assert_matches_type(AgentListResponse, agent, path=["response"]) - - @pytest.mark.skip(reason="Mock server tests are disabled") - @parametrize - def test_raw_response_list(self, client: Mobilerun) -> None: - response = client.agents.with_raw_response.list() - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - agent = response.parse() - assert_matches_type(AgentListResponse, agent, path=["response"]) - - @pytest.mark.skip(reason="Mock server tests are disabled") - @parametrize - def test_streaming_response_list(self, client: Mobilerun) -> None: - with client.agents.with_streaming_response.list() as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - agent = response.parse() - assert_matches_type(AgentListResponse, agent, path=["response"]) - - assert cast(Any, response.is_closed) is True - - -class TestAsyncAgents: - parametrize = pytest.mark.parametrize( - "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] - ) - - @pytest.mark.skip(reason="Mock server tests are disabled") - @parametrize - async def test_method_list(self, async_client: AsyncMobilerun) -> None: - agent = await async_client.agents.list() - assert_matches_type(AgentListResponse, agent, path=["response"]) - - @pytest.mark.skip(reason="Mock server tests are disabled") - @parametrize - async def test_raw_response_list(self, async_client: AsyncMobilerun) -> None: - response = await async_client.agents.with_raw_response.list() - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - agent = await response.parse() - assert_matches_type(AgentListResponse, agent, path=["response"]) - - @pytest.mark.skip(reason="Mock server tests are disabled") - @parametrize - async def test_streaming_response_list(self, async_client: AsyncMobilerun) -> None: - async with async_client.agents.with_streaming_response.list() as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - agent = await response.parse() - assert_matches_type(AgentListResponse, agent, path=["response"]) - - assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/test_esims.py b/tests/api_resources/test_esims.py index 4040193e..a47275d3 100644 --- a/tests/api_resources/test_esims.py +++ b/tests/api_resources/test_esims.py @@ -39,6 +39,7 @@ def test_method_create(self, client: Mobilerun) -> None: def test_method_create_with_all_params(self, client: Mobilerun) -> None: esim = client.esims.create( idempotency_key="idempotencyKey", + name="Mom's phone", ) assert_matches_type(EsimCreateResponse, esim, path=["response"]) @@ -455,6 +456,15 @@ def test_method_selector(self, client: Mobilerun) -> None: esim = client.esims.selector() assert_matches_type(EsimSelectorResponse, esim, path=["response"]) + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_selector_with_all_params(self, client: Mobilerun) -> None: + esim = client.esims.selector( + page=1, + page_size=1, + ) + assert_matches_type(EsimSelectorResponse, esim, path=["response"]) + @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize def test_raw_response_selector(self, client: Mobilerun) -> None: @@ -494,6 +504,7 @@ async def test_method_create(self, async_client: AsyncMobilerun) -> None: async def test_method_create_with_all_params(self, async_client: AsyncMobilerun) -> None: esim = await async_client.esims.create( idempotency_key="idempotencyKey", + name="Mom's phone", ) assert_matches_type(EsimCreateResponse, esim, path=["response"]) @@ -910,6 +921,15 @@ async def test_method_selector(self, async_client: AsyncMobilerun) -> None: esim = await async_client.esims.selector() assert_matches_type(EsimSelectorResponse, esim, path=["response"]) + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_selector_with_all_params(self, async_client: AsyncMobilerun) -> None: + esim = await async_client.esims.selector( + page=1, + page_size=1, + ) + assert_matches_type(EsimSelectorResponse, esim, path=["response"]) + @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize async def test_raw_response_selector(self, async_client: AsyncMobilerun) -> None: diff --git a/tests/api_resources/test_numbers.py b/tests/api_resources/test_numbers.py index bf4d09de..44193174 100644 --- a/tests/api_resources/test_numbers.py +++ b/tests/api_resources/test_numbers.py @@ -13,6 +13,7 @@ NumberListResponse, NumberCreateResponse, NumberDeleteResponse, + NumberUpdateResponse, NumberPurposesResponse, NumberRetrieveResponse, NumberCountriesResponse, @@ -36,6 +37,7 @@ def test_method_create_with_all_params(self, client: Mobilerun) -> None: number = client.numbers.create( billing_preference="included", country="de", + label="Support line", purpose="telegram", idempotency_key="x", ) @@ -105,6 +107,57 @@ def test_path_params_retrieve(self, client: Mobilerun) -> None: "", ) + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_update(self, client: Mobilerun) -> None: + number = client.numbers.update( + id="550e8400-e29b-41d4-a716-446655440000", + ) + assert_matches_type(NumberUpdateResponse, number, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_update_with_all_params(self, client: Mobilerun) -> None: + number = client.numbers.update( + id="550e8400-e29b-41d4-a716-446655440000", + label="Support line", + ) + assert_matches_type(NumberUpdateResponse, number, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_raw_response_update(self, client: Mobilerun) -> None: + response = client.numbers.with_raw_response.update( + id="550e8400-e29b-41d4-a716-446655440000", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + number = response.parse() + assert_matches_type(NumberUpdateResponse, number, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_streaming_response_update(self, client: Mobilerun) -> None: + with client.numbers.with_streaming_response.update( + id="550e8400-e29b-41d4-a716-446655440000", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + number = response.parse() + assert_matches_type(NumberUpdateResponse, number, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_path_params_update(self, client: Mobilerun) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.numbers.with_raw_response.update( + id="", + ) + @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize def test_method_list(self, client: Mobilerun) -> None: @@ -258,6 +311,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncMobilerun) number = await async_client.numbers.create( billing_preference="included", country="de", + label="Support line", purpose="telegram", idempotency_key="x", ) @@ -327,6 +381,57 @@ async def test_path_params_retrieve(self, async_client: AsyncMobilerun) -> None: "", ) + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_update(self, async_client: AsyncMobilerun) -> None: + number = await async_client.numbers.update( + id="550e8400-e29b-41d4-a716-446655440000", + ) + assert_matches_type(NumberUpdateResponse, number, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_update_with_all_params(self, async_client: AsyncMobilerun) -> None: + number = await async_client.numbers.update( + id="550e8400-e29b-41d4-a716-446655440000", + label="Support line", + ) + assert_matches_type(NumberUpdateResponse, number, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_raw_response_update(self, async_client: AsyncMobilerun) -> None: + response = await async_client.numbers.with_raw_response.update( + id="550e8400-e29b-41d4-a716-446655440000", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + number = await response.parse() + assert_matches_type(NumberUpdateResponse, number, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_streaming_response_update(self, async_client: AsyncMobilerun) -> None: + async with async_client.numbers.with_streaming_response.update( + id="550e8400-e29b-41d4-a716-446655440000", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + number = await response.parse() + assert_matches_type(NumberUpdateResponse, number, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_path_params_update(self, async_client: AsyncMobilerun) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.numbers.with_raw_response.update( + id="", + ) + @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize async def test_method_list(self, async_client: AsyncMobilerun) -> None: diff --git a/tests/api_resources/workflows/test_flows.py b/tests/api_resources/workflows/test_flows.py index 0cbbda1c..845d0f4f 100644 --- a/tests/api_resources/workflows/test_flows.py +++ b/tests/api_resources/workflows/test_flows.py @@ -75,6 +75,7 @@ def test_method_create_with_all_params(self, client: Mobilerun) -> None: notify_on_failure=True, notify_on_success=True, notify_webhook_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + recording_enabled=True, self_healing_enabled=True, self_healing_max_attempts=1, ) @@ -185,6 +186,7 @@ def test_method_update_with_all_params(self, client: Mobilerun) -> None: notify_on_failure=True, notify_on_success=True, notify_webhook_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + recording_enabled=True, self_healing_enabled=True, self_healing_max_attempts=1, trigger_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", @@ -553,6 +555,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncMobilerun) notify_on_failure=True, notify_on_success=True, notify_webhook_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + recording_enabled=True, self_healing_enabled=True, self_healing_max_attempts=1, ) @@ -663,6 +666,7 @@ async def test_method_update_with_all_params(self, async_client: AsyncMobilerun) notify_on_failure=True, notify_on_success=True, notify_webhook_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + recording_enabled=True, self_healing_enabled=True, self_healing_max_attempts=1, trigger_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",