allternit-platform 0.2.0__tar.gz → 0.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {allternit_platform-0.2.0 → allternit_platform-0.3.0}/PKG-INFO +1 -1
- {allternit_platform-0.2.0 → allternit_platform-0.3.0}/allternit_platform/_generated.py +461 -7
- {allternit_platform-0.2.0 → allternit_platform-0.3.0}/allternit_platform.egg-info/PKG-INFO +1 -1
- {allternit_platform-0.2.0 → allternit_platform-0.3.0}/pyproject.toml +1 -1
- {allternit_platform-0.2.0 → allternit_platform-0.3.0}/README.md +0 -0
- {allternit_platform-0.2.0 → allternit_platform-0.3.0}/allternit_platform/__init__.py +0 -0
- {allternit_platform-0.2.0 → allternit_platform-0.3.0}/allternit_platform/_client.py +0 -0
- {allternit_platform-0.2.0 → allternit_platform-0.3.0}/allternit_platform/_core.py +0 -0
- {allternit_platform-0.2.0 → allternit_platform-0.3.0}/allternit_platform/_errors.py +0 -0
- {allternit_platform-0.2.0 → allternit_platform-0.3.0}/allternit_platform/py.typed +0 -0
- {allternit_platform-0.2.0 → allternit_platform-0.3.0}/allternit_platform.egg-info/SOURCES.txt +0 -0
- {allternit_platform-0.2.0 → allternit_platform-0.3.0}/allternit_platform.egg-info/dependency_links.txt +0 -0
- {allternit_platform-0.2.0 → allternit_platform-0.3.0}/allternit_platform.egg-info/top_level.txt +0 -0
- {allternit_platform-0.2.0 → allternit_platform-0.3.0}/setup.cfg +0 -0
- {allternit_platform-0.2.0 → allternit_platform-0.3.0}/tests/test_client.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: allternit-platform
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Python SDK for the Allternit Platform API (agents, conversations, numbers, messaging, webhooks, usage).
|
|
5
5
|
License: Apache-2.0
|
|
6
6
|
Project-URL: Documentation, https://docs.allternit.com/api/platform/sdks/python
|
|
@@ -33,21 +33,37 @@ StockVoice = Literal['af_alloy', 'af_aoede', 'af_bella', 'af_heart', 'af_jessica
|
|
|
33
33
|
Conversation = TypedDict('Conversation', {'id': str, 'object': Literal['conversation'], 'agent_id': str, 'account_id': str, 'metadata': Dict[str, Any], 'created_at': str, 'messages': List["ConversationMessage"]}, total=False)
|
|
34
34
|
ConversationMessage = TypedDict('ConversationMessage', {'id': str, 'object': Literal['conversation.message'], 'conversation_id': str, 'role': Literal['user', 'assistant'], 'content': str, 'status': Literal['completed', 'failed'], 'error': Optional[str], 'created_at': str}, total=False)
|
|
35
35
|
ModelKey = TypedDict('ModelKey', {'provider': Literal['anthropic', 'openai', 'xai'], 'object': Literal['model_key'], 'masked': str, 'created_at': str, 'updated_at': str}, total=False)
|
|
36
|
-
|
|
36
|
+
ProjectTracing = TypedDict('ProjectTracing', {'object': Literal['project.tracing'], 'project_id': str, 'endpoint': str, 'header_names': List[str], 'service_name': str, 'enabled': bool, 'last_export_at': Optional[str], 'last_error': Optional[str], 'created_at': str, 'updated_at': str}, total=False)
|
|
37
|
+
UsageRow = TypedDict('UsageRow', {'group': Optional[str], 'meter': Literal['voice_min_allternit', 'voice_min_byok', 'agent_month', 'tokens_in', 'tokens_out', 'number_local_month', 'number_tollfree_month', 'sms_segment', 'mms', 'registration_passthrough_cents', 'recording_min_month', 'computer_minute', 'computer_action', 'mailbox_month', 'identity_month'], 'unit': Optional[str], 'quantity': float, 'events': int}, total=False)
|
|
37
38
|
PhoneNumber = TypedDict('PhoneNumber', {'id': str, 'object': Literal['phone_number'], 'account_id': Optional[str], 'e164': str, 'type': Literal['local', 'toll_free'], 'sms_state': Literal['pending_registration', 'active', 'rejected', 'blocked'], 'simulated': bool, 'created_at': str}, total=False)
|
|
38
39
|
PhoneNumberList = TypedDict('PhoneNumberList', {'data': List["PhoneNumber"], 'has_more': bool, 'next_cursor': Optional[str]}, total=False)
|
|
39
40
|
Message = TypedDict('Message', {'id': str, 'object': Literal['message'], 'account_id': Optional[str], 'number_id': str, 'direction': Literal['inbound', 'outbound'], 'from': str, 'to': str, 'body': str, 'segments': int, 'status': Literal['sent', 'simulated', 'received', 'failed'], 'error_code': Optional[str], 'media': List[Dict[str, Any]], 'created_at': str}, total=False)
|
|
40
41
|
MessageList = TypedDict('MessageList', {'data': List["Message"], 'has_more': bool, 'next_cursor': Optional[str]}, total=False)
|
|
41
42
|
WebhookEndpoint = TypedDict('WebhookEndpoint', {'id': str, 'object': Literal['webhook_endpoint'], 'url': str, 'events': List[str], 'description': Optional[str], 'created_at': str}, total=False)
|
|
42
|
-
Event = TypedDict('Event', {'id': str, 'object': Literal['event'], 'type': Literal['approval.requested', 'message.received', 'message.status', 'registration.updated', 'inbox.item.created', 'call.started', 'call.ended', 'call.transcript.ready', 'usage.threshold', 'webhook.test'], 'created': int, 'project_id': str, 'account_id': Optional[str], 'data': Dict[str, Any]}, total=False)
|
|
43
|
+
Event = TypedDict('Event', {'id': str, 'object': Literal['event'], 'type': Literal['approval.requested', 'message.received', 'message.status', 'registration.updated', 'inbox.item.created', 'call.started', 'call.ended', 'call.transcript.ready', 'usage.threshold', 'phone.step', 'phone.run.completed', 'phone.fleet_run.completed', 'mail.received', 'webhook.test'], 'created': int, 'project_id': str, 'account_id': Optional[str], 'data': Dict[str, Any]}, total=False)
|
|
43
44
|
Computer = TypedDict('Computer', {'id': str, 'object': Literal['computer'], 'name': str, 'status': Literal['provisioning', 'starting', 'running', 'stopping', 'stopped', 'error', 'deleted'], 'account_id': Optional[str], 'key_id': str, 'created_at': str, 'started_at': Optional[str], 'metadata': Dict[str, Any]}, total=False)
|
|
44
45
|
ComputerList = TypedDict('ComputerList', {'data': List["Computer"], 'has_more': bool, 'next_cursor': Optional[str]}, total=False)
|
|
45
46
|
ComputerEvent = TypedDict('ComputerEvent', {'id': str, 'type': Literal['computer.action'], 'ts': str, 'data': Dict[str, Any]}, total=False)
|
|
46
47
|
ComputerEventList = TypedDict('ComputerEventList', {'data': List["ComputerEvent"], 'has_more': bool, 'next_cursor': Optional[str]}, total=False)
|
|
47
48
|
ComputerSettings = TypedDict('ComputerSettings', {'hosted_driver_enabled': bool, 'approval_mode': Literal['owner', 'api_key'], 'per_key_concurrency': int, 'browser_toolset': bool}, total=False)
|
|
48
|
-
ToolsetCall = TypedDict('ToolsetCall', {'toolset': Literal['computer', 'browser'], 'member': str, 'input': Dict[str, Any], 'run_id': str, 'turn_id': str, 'call_index': int, 'model_frame': Dict[str, Any], 'coordinate_space': Literal['pixels', 'normalized_1000'], 'approval_grant': str, 'browser_session_id': str, 'enable': List[str]}, total=False)
|
|
49
|
+
ToolsetCall = TypedDict('ToolsetCall', {'toolset': Literal['computer', 'browser'], 'member': str, 'input': Dict[str, Any], 'run_id': str, 'turn_id': str, 'call_index': int, 'model_frame': Dict[str, Any], 'coordinate_space': Literal['pixels', 'normalized_1000'], 'approval_grant': str, 'browser_session_id': str, 'enable': List[str], 'agent_id': str, 'agent_name': str, 'step': str}, total=False)
|
|
49
50
|
ToolsetResult = TypedDict('ToolsetResult', {'is_error': bool, 'content': List[Dict[str, Any]], 'browser_state': Dict[str, Any], 'screen': Dict[str, Any], 'error': str}, total=False)
|
|
50
51
|
ToolsetApprovalRequired = TypedDict('ToolsetApprovalRequired', {'error': Dict[str, Any], 'approval': Dict[str, Any], 'result': "ToolsetResult"}, total=False)
|
|
52
|
+
Mailbox = TypedDict('Mailbox', {'id': str, 'object': Literal['mailbox'], 'account_id': str, 'agent_id': Optional[str], 'address': str, 'display_name': Optional[str], 'created_at': str}, total=False)
|
|
53
|
+
MailboxList = TypedDict('MailboxList', {'data': List["Mailbox"], 'has_more': bool, 'next_cursor': Optional[str]}, total=False)
|
|
54
|
+
MailMessage = TypedDict('MailMessage', {'id': str, 'object': Literal['mail_message'], 'mailbox_id': str, 'from': str, 'to': str, 'subject': Optional[str], 'snippet': Optional[str], 'codes': List[str], 'links': List[str], 'is_verification': bool, 'received_at': str}, total=False)
|
|
55
|
+
MailMessageList = TypedDict('MailMessageList', {'data': List["MailMessage"], 'has_more': bool, 'next_cursor': Optional[str]}, total=False)
|
|
56
|
+
Identity = TypedDict('Identity', {'id': str, 'object': Literal['identity'], 'account_id': str, 'agent_id': Optional[str], 'number': Union["PhoneNumber", Optional[Any]], 'mailbox': Union["Mailbox", Optional[Any]], 'phone_serial': Optional[str], 'bundle': Dict[str, Any], 'created_at': str}, total=False)
|
|
57
|
+
IdentityList = TypedDict('IdentityList', {'data': List["Identity"], 'has_more': bool, 'next_cursor': Optional[str]}, total=False)
|
|
58
|
+
Phone = TypedDict('Phone', {'object': Literal['phone'], 'serial': str, 'name': Optional[str], 'model': Optional[str], 'online': bool, 'desktop_online': bool, 'last_seen_at': Optional[str], 'call_bridge': Optional[Dict[str, Any]]}, total=False)
|
|
59
|
+
PhoneList = TypedDict('PhoneList', {'data': List["Phone"], 'has_more': bool, 'next_cursor': Optional[str]}, total=False)
|
|
60
|
+
PhoneSkillList = TypedDict('PhoneSkillList', {'data': List[Dict[str, Any]], 'has_more': bool, 'next_cursor': Optional[str]}, total=False)
|
|
61
|
+
PhoneStep = TypedDict('PhoneStep', {'object': Literal['phone.step'], 'id': str, 'run_id': str, 'serial': str, 'at': str, 'tool': str, 'label': str, 'args': Dict[str, Any], 'ok': bool, 'error': Optional[str], 'duration_ms': int, 'source': Optional[str], 'snapshot': bool}, total=False)
|
|
62
|
+
PhoneHistory = TypedDict('PhoneHistory', {'object': Literal['phone.history'], 'serial': str, 'steps': List["PhoneStep"], 'runs': List[Dict[str, Any]], 'retention_days': int, 'max_steps': int}, total=False)
|
|
63
|
+
PhoneLiveView = TypedDict('PhoneLiveView', {'object': Literal['phone.live_view'], 'id': str, 'serial': str, 'view_only': Literal[True], 'expires_in': int, 'expires_at': str, 'viewer_url': str, 'stream_url': str}, total=False)
|
|
64
|
+
PhoneFleetRun = TypedDict('PhoneFleetRun', {'object': Literal['phone.fleet_run'], 'id': str, 'kind': Literal['task', 'skill'], 'goal': Optional[str], 'skill': Optional[str], 'status': Literal['running', 'completed'], 'created_at': str, 'completed_at': Optional[str], 'counts': Dict[str, Any], 'runs': List[Dict[str, Any]], 'not_started': List[Dict[str, Any]]}, total=False)
|
|
65
|
+
PhoneRunStarted = TypedDict('PhoneRunStarted', {'object': Literal['phone.run'], 'run_id': str, 'serial': Optional[str], 'skill': Optional[str], 'status': Literal['running']}, total=False)
|
|
66
|
+
PhoneToolResult = TypedDict('PhoneToolResult', {'object': Literal['phone.tool_result'], 'ok': bool, 'run_id': str, 'tool': str, 'error': str, 'message': str}, total=False)
|
|
51
67
|
|
|
52
68
|
|
|
53
69
|
class AccountsResource:
|
|
@@ -377,6 +393,44 @@ class ConversationsResource:
|
|
|
377
393
|
_body['metadata'] = metadata
|
|
378
394
|
return self._client.request("POST", f"/v1/agents/{_q(id)}/conversations", body=_body, idempotency_key=idempotency_key, timeout=timeout, extra_headers=extra_headers) # type: ignore[return-value]
|
|
379
395
|
|
|
396
|
+
def run_agent_ag_ui_stream(self, id: str, *, messages: List[Dict[str, Any]], thread_id: str = NOT_GIVEN, run_id: str = NOT_GIVEN, parent_run_id: str = NOT_GIVEN, tools: List[Dict[str, Any]] = NOT_GIVEN, context: List[Dict[str, Any]] = NOT_GIVEN, state: Any = NOT_GIVEN, forwarded_props: Any = NOT_GIVEN, idempotency_key: Optional[str] = None, timeout: Optional[float] = None, extra_headers: Optional[Dict[str, str]] = None) -> Iterator[SSEEvent]:
|
|
397
|
+
"""Run the agent for an AG-UI frontend
|
|
398
|
+
|
|
399
|
+
Runs one conversation turn over the AG-UI protocol (https://docs.ag-ui.com), so AG-UI
|
|
400
|
+
clients such as CopilotKit's `HttpAgent` can drive the agent. The body is AG-UI's
|
|
401
|
+
`RunAgentInput`. `threadId` is one of the agent's conversation ids, or any other string,
|
|
402
|
+
which is kept on the conversation the first run opens so later runs continue it; no
|
|
403
|
+
`threadId` opens a new conversation. The server keeps the history: the turn's input is
|
|
404
|
+
the last `user` message's text. Client-side `tools`, `context` and `state` are not used.
|
|
405
|
+
The answer is server-sent events, one `data:` JSON AG-UI event each: `RUN_STARTED`,
|
|
406
|
+
`TEXT_MESSAGE_START` / `TEXT_MESSAGE_CONTENT` / `TEXT_MESSAGE_END`, `TOOL_CALL_START` /
|
|
407
|
+
`TOOL_CALL_ARGS` / `TOOL_CALL_END` / `TOOL_CALL_RESULT` for each tool the agent uses,
|
|
408
|
+
then `RUN_FINISHED` or `RUN_ERROR` (`{message, code}`). `runId` is echoed (generated when
|
|
409
|
+
missing). Refusals before the stream starts are plain errors, as for messages:
|
|
410
|
+
`402 spend_cap_reached` / `payment_method_required`, `409 conversation_busy`,
|
|
411
|
+
`503 runtime_starting`. The reply is stored and metered like any conversation message.
|
|
412
|
+
|
|
413
|
+
``POST /v1/agents/{id}/ag-ui``
|
|
414
|
+
"""
|
|
415
|
+
_body: Dict[str, Any] = {}
|
|
416
|
+
if thread_id is not NOT_GIVEN:
|
|
417
|
+
_body['threadId'] = thread_id
|
|
418
|
+
if run_id is not NOT_GIVEN:
|
|
419
|
+
_body['runId'] = run_id
|
|
420
|
+
if parent_run_id is not NOT_GIVEN:
|
|
421
|
+
_body['parentRunId'] = parent_run_id
|
|
422
|
+
if messages is not NOT_GIVEN:
|
|
423
|
+
_body['messages'] = messages
|
|
424
|
+
if tools is not NOT_GIVEN:
|
|
425
|
+
_body['tools'] = tools
|
|
426
|
+
if context is not NOT_GIVEN:
|
|
427
|
+
_body['context'] = context
|
|
428
|
+
if state is not NOT_GIVEN:
|
|
429
|
+
_body['state'] = state
|
|
430
|
+
if forwarded_props is not NOT_GIVEN:
|
|
431
|
+
_body['forwardedProps'] = forwarded_props
|
|
432
|
+
return self._client.stream_request("POST", f"/v1/agents/{_q(id)}/ag-ui", body=_body, idempotency_key=idempotency_key, timeout=timeout, extra_headers=extra_headers)
|
|
433
|
+
|
|
380
434
|
def get(self, id: str, *, timeout: Optional[float] = None, extra_headers: Optional[Dict[str, str]] = None) -> "Conversation":
|
|
381
435
|
"""Retrieve a conversation
|
|
382
436
|
|
|
@@ -390,8 +444,11 @@ class ConversationsResource:
|
|
|
390
444
|
"""Send a message
|
|
391
445
|
|
|
392
446
|
Sends a message and answers with the agent's reply. With `stream: true` the answer is
|
|
393
|
-
server-sent events: `message.delta` (`{"delta": "..."}`) while the agent writes,
|
|
394
|
-
`
|
|
447
|
+
server-sent events: `message.delta` (`{"delta": "..."}`) while the agent writes,
|
|
448
|
+
`tool.started` / `tool.completed` / `tool.failed` (`{"tool_call_id", "name", "input"?,
|
|
449
|
+
"output"?, "error"?}`; arguments and results are cut at 8,000 characters) as it uses
|
|
450
|
+
tools, then `message.completed` (the stored reply) or `error`. Clients should skip
|
|
451
|
+
event names they don't know. The first message starts the
|
|
395
452
|
project's hosted runtime: `503 runtime_starting` means retry in a few seconds.
|
|
396
453
|
One message at a time per conversation (`409 conversation_busy`). A project at its
|
|
397
454
|
monthly spend cap gets `402 spend_cap_reached`; one with no card on file gets
|
|
@@ -418,6 +475,49 @@ class UsageResource:
|
|
|
418
475
|
def __init__(self, client: Transport) -> None:
|
|
419
476
|
self._client = client
|
|
420
477
|
|
|
478
|
+
def get_project_tracing(self, id: str, *, timeout: Optional[float] = None, extra_headers: Optional[Dict[str, str]] = None) -> "ProjectTracing":
|
|
479
|
+
"""The project's trace export settings
|
|
480
|
+
|
|
481
|
+
Where the project's spans go. Header values are never returned, only their names. `404 tracing_not_configured` when none is set. Requires the `usage` scope.
|
|
482
|
+
|
|
483
|
+
``GET /v1/projects/{id}/tracing``
|
|
484
|
+
"""
|
|
485
|
+
return self._client.request("GET", f"/v1/projects/{_q(id)}/tracing", timeout=timeout, extra_headers=extra_headers) # type: ignore[return-value]
|
|
486
|
+
|
|
487
|
+
def put_project_tracing(self, id: str, *, endpoint: str, headers: Dict[str, Any] = NOT_GIVEN, service_name: str = NOT_GIVEN, enabled: bool = NOT_GIVEN, timeout: Optional[float] = None, extra_headers: Optional[Dict[str, str]] = None) -> "ProjectTracing":
|
|
488
|
+
"""Set the project's trace export
|
|
489
|
+
|
|
490
|
+
Send a span for every agent turn, phone run and call of this project to an OTLP/HTTP collector (Langfuse, Arize Phoenix, any OpenTelemetry collector), as OTLP protobuf following the OpenTelemetry GenAI conventions. `endpoint` is the collector's OTLP base URL (`/v1/traces` is appended) or its full traces URL; it must be a public https URL. `headers` are encrypted at rest; leave them out to keep the stored ones. Export runs in the background and never fails or slows a request. Not available to account-bound keys.
|
|
491
|
+
|
|
492
|
+
``PUT /v1/projects/{id}/tracing``
|
|
493
|
+
"""
|
|
494
|
+
_body: Dict[str, Any] = {}
|
|
495
|
+
if endpoint is not NOT_GIVEN:
|
|
496
|
+
_body['endpoint'] = endpoint
|
|
497
|
+
if headers is not NOT_GIVEN:
|
|
498
|
+
_body['headers'] = headers
|
|
499
|
+
if service_name is not NOT_GIVEN:
|
|
500
|
+
_body['service_name'] = service_name
|
|
501
|
+
if enabled is not NOT_GIVEN:
|
|
502
|
+
_body['enabled'] = enabled
|
|
503
|
+
return self._client.request("PUT", f"/v1/projects/{_q(id)}/tracing", body=_body, timeout=timeout, extra_headers=extra_headers) # type: ignore[return-value]
|
|
504
|
+
|
|
505
|
+
def delete_project_tracing(self, id: str, *, timeout: Optional[float] = None, extra_headers: Optional[Dict[str, str]] = None) -> Dict[str, Any]:
|
|
506
|
+
"""Turn off and forget the project's trace export
|
|
507
|
+
|
|
508
|
+
``DELETE /v1/projects/{id}/tracing``
|
|
509
|
+
"""
|
|
510
|
+
return self._client.request("DELETE", f"/v1/projects/{_q(id)}/tracing", timeout=timeout, extra_headers=extra_headers) # type: ignore[return-value]
|
|
511
|
+
|
|
512
|
+
def test_project_tracing(self, id: str, *, idempotency_key: Optional[str] = None, timeout: Optional[float] = None, extra_headers: Optional[Dict[str, str]] = None) -> Dict[str, Any]:
|
|
513
|
+
"""Send one test span
|
|
514
|
+
|
|
515
|
+
Sends one `allternit.tracing.test` span to the collector now and reports its answer.
|
|
516
|
+
|
|
517
|
+
``POST /v1/projects/{id}/tracing/test``
|
|
518
|
+
"""
|
|
519
|
+
return self._client.request("POST", f"/v1/projects/{_q(id)}/tracing/test", idempotency_key=idempotency_key, timeout=timeout, extra_headers=extra_headers) # type: ignore[return-value]
|
|
520
|
+
|
|
421
521
|
def get_spend_cap(self, *, timeout: Optional[float] = None, extra_headers: Optional[Dict[str, str]] = None) -> "SpendCap":
|
|
422
522
|
"""The project's monthly spend cap and spend so far
|
|
423
523
|
|
|
@@ -702,7 +802,7 @@ class WebhooksResource:
|
|
|
702
802
|
return self._client.request("GET", f"/v1/webhooks", query=q, timeout=timeout, extra_headers=extra_headers) # type: ignore[return-value]
|
|
703
803
|
return self._client.paginate(_fetch, after if after is not NOT_GIVEN else None)
|
|
704
804
|
|
|
705
|
-
def create(self, *, url: str, events: List[Literal['*', 'approval.requested', 'message.received', 'message.status', 'registration.updated', 'inbox.item.created', 'call.started', 'call.ended', 'call.transcript.ready', 'usage.threshold']], description: str = NOT_GIVEN, idempotency_key: Optional[str] = None, timeout: Optional[float] = None, extra_headers: Optional[Dict[str, str]] = None) -> Dict[str, Any]:
|
|
805
|
+
def create(self, *, url: str, events: List[Literal['*', 'approval.requested', 'message.received', 'message.status', 'registration.updated', 'inbox.item.created', 'call.started', 'call.ended', 'call.transcript.ready', 'usage.threshold', 'phone.step', 'phone.run.completed', 'phone.fleet_run.completed', 'mail.received']], description: str = NOT_GIVEN, idempotency_key: Optional[str] = None, timeout: Optional[float] = None, extra_headers: Optional[Dict[str, str]] = None) -> Dict[str, Any]:
|
|
706
806
|
"""Create a webhook endpoint
|
|
707
807
|
|
|
708
808
|
`url` must be public https. The response carries `secret` once.
|
|
@@ -1123,7 +1223,7 @@ class ComputersResource:
|
|
|
1123
1223
|
"""
|
|
1124
1224
|
return self._client.request("POST", f"/v1/computers/{_q(id)}/stop", idempotency_key=idempotency_key, timeout=timeout, extra_headers=extra_headers) # type: ignore[return-value]
|
|
1125
1225
|
|
|
1126
|
-
def call_toolset(self, id: str, *, toolset: Literal['computer', 'browser'], member: str, input: Dict[str, Any] = NOT_GIVEN, run_id: str = NOT_GIVEN, turn_id: str = NOT_GIVEN, call_index: int = NOT_GIVEN, model_frame: Dict[str, Any] = NOT_GIVEN, coordinate_space: Literal['pixels', 'normalized_1000'] = NOT_GIVEN, approval_grant: str = NOT_GIVEN, browser_session_id: str = NOT_GIVEN, enable: List[str] = NOT_GIVEN, idempotency_key: Optional[str] = None, timeout: Optional[float] = None, extra_headers: Optional[Dict[str, str]] = None) -> "ToolsetResult":
|
|
1226
|
+
def call_toolset(self, id: str, *, toolset: Literal['computer', 'browser'], member: str, input: Dict[str, Any] = NOT_GIVEN, run_id: str = NOT_GIVEN, turn_id: str = NOT_GIVEN, call_index: int = NOT_GIVEN, model_frame: Dict[str, Any] = NOT_GIVEN, coordinate_space: Literal['pixels', 'normalized_1000'] = NOT_GIVEN, approval_grant: str = NOT_GIVEN, browser_session_id: str = NOT_GIVEN, enable: List[str] = NOT_GIVEN, agent_id: str = NOT_GIVEN, agent_name: str = NOT_GIVEN, step: str = NOT_GIVEN, idempotency_key: Optional[str] = None, timeout: Optional[float] = None, extra_headers: Optional[Dict[str, str]] = None) -> "ToolsetResult":
|
|
1127
1227
|
"""Run one toolset call
|
|
1128
1228
|
|
|
1129
1229
|
One `allternit.computer.v1` / `allternit.browser.v1` call (member names and inputs match Anthropic's `computer_toolset_20260801` / `browser_toolset_20260801`). Runs through the computer's executor: contract validation, control lease, policy, approval, audit, then the action. Action failures are 200 with `is_error: true`.
|
|
@@ -1154,6 +1254,12 @@ class ComputersResource:
|
|
|
1154
1254
|
_body['browser_session_id'] = browser_session_id
|
|
1155
1255
|
if enable is not NOT_GIVEN:
|
|
1156
1256
|
_body['enable'] = enable
|
|
1257
|
+
if agent_id is not NOT_GIVEN:
|
|
1258
|
+
_body['agent_id'] = agent_id
|
|
1259
|
+
if agent_name is not NOT_GIVEN:
|
|
1260
|
+
_body['agent_name'] = agent_name
|
|
1261
|
+
if step is not NOT_GIVEN:
|
|
1262
|
+
_body['step'] = step
|
|
1157
1263
|
return self._client.request("POST", f"/v1/computers/{_q(id)}/toolset", body=_body, idempotency_key=idempotency_key, timeout=timeout, extra_headers=extra_headers) # type: ignore[return-value]
|
|
1158
1264
|
|
|
1159
1265
|
def get_toolset_schema(self, id: str, *, toolset: Literal['computer', 'browser'] = NOT_GIVEN, timeout: Optional[float] = None, extra_headers: Optional[Dict[str, str]] = None) -> Dict[str, Any]:
|
|
@@ -1229,6 +1335,351 @@ class ComputersResource:
|
|
|
1229
1335
|
return self._client.request("PATCH", f"/v1/computer_settings", body=_body, timeout=timeout, extra_headers=extra_headers) # type: ignore[return-value]
|
|
1230
1336
|
|
|
1231
1337
|
|
|
1338
|
+
class PhonesResource:
|
|
1339
|
+
def __init__(self, client: Transport) -> None:
|
|
1340
|
+
self._client = client
|
|
1341
|
+
|
|
1342
|
+
def list(self, *, timeout: Optional[float] = None, extra_headers: Optional[Dict[str, str]] = None) -> "PhoneList":
|
|
1343
|
+
"""List the owner's phones
|
|
1344
|
+
|
|
1345
|
+
The project owner's own phones, linked to their Allternit Desktop (Phone Link, adb or a USB Agent). Requires the `phones` scope and a key that is not bound to an account (403 `account_bound_key`).
|
|
1346
|
+
While the Desktop is offline the phones the cloud last saw are listed with `online: false` and `desktop_online: false`. Not paginated (a person links a handful).
|
|
1347
|
+
|
|
1348
|
+
``GET /v1/phones``
|
|
1349
|
+
"""
|
|
1350
|
+
return self._client.request("GET", f"/v1/phones", timeout=timeout, extra_headers=extra_headers) # type: ignore[return-value]
|
|
1351
|
+
|
|
1352
|
+
def list_skills(self, *, timeout: Optional[float] = None, extra_headers: Optional[Dict[str, str]] = None) -> "PhoneSkillList":
|
|
1353
|
+
"""List saved phone skills
|
|
1354
|
+
|
|
1355
|
+
Recorded phone runs that replay with no model. Each lists the `inputs` a run must pass (typed text is never stored). 503 `phone_desktop_offline` while the Desktop is offline.
|
|
1356
|
+
|
|
1357
|
+
``GET /v1/phones/skills``
|
|
1358
|
+
"""
|
|
1359
|
+
return self._client.request("GET", f"/v1/phones/skills", timeout=timeout, extra_headers=extra_headers) # type: ignore[return-value]
|
|
1360
|
+
|
|
1361
|
+
def run_skill(self, name: str, *, serial: str = NOT_GIVEN, inputs: Dict[str, Any] = NOT_GIVEN, bot_id: str = NOT_GIVEN, brain: str = NOT_GIVEN, idempotency_key: Optional[str] = None, timeout: Optional[float] = None, extra_headers: Optional[Dict[str, str]] = None) -> "PhoneRunStarted":
|
|
1362
|
+
"""Replay a phone skill
|
|
1363
|
+
|
|
1364
|
+
Starts the replay in the background and answers 202 with its `run_id`. Each step is a `phone.step` webhook; the end is `phone.run.completed`. A step whose element moved re-decides once on `bot_id`'s brain (or `brain`). Skills never send texts or place calls.
|
|
1365
|
+
|
|
1366
|
+
``POST /v1/phones/skills/{name}/run``
|
|
1367
|
+
"""
|
|
1368
|
+
_body: Dict[str, Any] = {}
|
|
1369
|
+
if serial is not NOT_GIVEN:
|
|
1370
|
+
_body['serial'] = serial
|
|
1371
|
+
if inputs is not NOT_GIVEN:
|
|
1372
|
+
_body['inputs'] = inputs
|
|
1373
|
+
if bot_id is not NOT_GIVEN:
|
|
1374
|
+
_body['bot_id'] = bot_id
|
|
1375
|
+
if brain is not NOT_GIVEN:
|
|
1376
|
+
_body['brain'] = brain
|
|
1377
|
+
return self._client.request("POST", f"/v1/phones/skills/{_q(name)}/run", body=_body, idempotency_key=idempotency_key, timeout=timeout, extra_headers=extra_headers) # type: ignore[return-value]
|
|
1378
|
+
|
|
1379
|
+
def get_history(self, serial: str, *, run_id: str = NOT_GIVEN, timeout: Optional[float] = None, extra_headers: Optional[Dict[str, str]] = None) -> "PhoneHistory":
|
|
1380
|
+
"""A phone's run history
|
|
1381
|
+
|
|
1382
|
+
Every phone tool call on this phone (500 steps / 30 days), newest run first. Typed text, numbers and message bodies are redacted on the Desktop; frames and screen text stay there.
|
|
1383
|
+
|
|
1384
|
+
``GET /v1/phones/{serial}/history``
|
|
1385
|
+
"""
|
|
1386
|
+
_query: Dict[str, Any] = {}
|
|
1387
|
+
if run_id is not NOT_GIVEN:
|
|
1388
|
+
_query['run_id'] = run_id
|
|
1389
|
+
return self._client.request("GET", f"/v1/phones/{_q(serial)}/history", query=_query, timeout=timeout, extra_headers=extra_headers) # type: ignore[return-value]
|
|
1390
|
+
|
|
1391
|
+
def call_tool(self, serial: str, *, name: str, arguments: Dict[str, Any] = NOT_GIVEN, run_id: str = NOT_GIVEN, idempotency_key: Optional[str] = None, timeout: Optional[float] = None, extra_headers: Optional[Dict[str, str]] = None) -> "PhoneToolResult":
|
|
1392
|
+
"""Run one phone tool
|
|
1393
|
+
|
|
1394
|
+
One phone tool through the Desktop phone gateway, exactly as a bot calls it: `phone.devices`, `phone.screenshot`, `phone.read_ui`, `phone.screen_text`, `phone.tap`, `phone.type`, `phone.find_and_tap`, `phone.swipe`, `phone.home`, `phone.back`, `phone.key`, `phone.open_app`, `phone.sms.read`, `phone.sms.send`, `phone.call.dial`. An iPhone running the Allternit app (serial `app:<phoneId>`) is screen only plus its in-app browser: `phone.screenshot`, `phone.devices` and `phone.web.open`, `phone.web.snapshot`, `phone.web.click`, `phone.web.type`, `phone.web.scroll`, `phone.web.back`, `phone.web.wait` (the app must be open; taps on native apps answer `not_supported`).
|
|
1395
|
+
`phone.sms.send` and `phone.call.dial` wait for the person to approve on their Desktop (up to 80 s; then 503 `phone_timeout`); a refusal is a 200 result with `ok: false, error: approval_denied`. Tool failures are 200 with `ok: false`. 409 `phone_offline` when the phone isn't connected, 503 `phone_desktop_offline` / `phone_gateway_unavailable` when the Desktop or its phone gateway isn't running. The call is a `phone.step` webhook like any other.
|
|
1396
|
+
|
|
1397
|
+
``POST /v1/phones/{serial}/tool``
|
|
1398
|
+
"""
|
|
1399
|
+
_body: Dict[str, Any] = {}
|
|
1400
|
+
if name is not NOT_GIVEN:
|
|
1401
|
+
_body['name'] = name
|
|
1402
|
+
if arguments is not NOT_GIVEN:
|
|
1403
|
+
_body['arguments'] = arguments
|
|
1404
|
+
if run_id is not NOT_GIVEN:
|
|
1405
|
+
_body['run_id'] = run_id
|
|
1406
|
+
return self._client.request("POST", f"/v1/phones/{_q(serial)}/tool", body=_body, idempotency_key=idempotency_key, timeout=timeout, extra_headers=extra_headers) # type: ignore[return-value]
|
|
1407
|
+
|
|
1408
|
+
def run_task(self, serial: str, *, goal: str, max_steps: int = NOT_GIVEN, bot_id: str = NOT_GIVEN, brain: str = NOT_GIVEN, idempotency_key: Optional[str] = None, timeout: Optional[float] = None, extra_headers: Optional[Dict[str, str]] = None) -> "PhoneRunStarted":
|
|
1409
|
+
"""Give a phone a goal
|
|
1410
|
+
|
|
1411
|
+
The phone task loop on the Desktop (read the screen, one action, repeat) with `bot_id`'s brain, `brain`, or the default. Answers 202 with the `run_id`; each step is a `phone.step` webhook and the end is `phone.run.completed`. Tasks never send texts or place calls. At most 5 minutes.
|
|
1412
|
+
|
|
1413
|
+
``POST /v1/phones/{serial}/task``
|
|
1414
|
+
"""
|
|
1415
|
+
_body: Dict[str, Any] = {}
|
|
1416
|
+
if goal is not NOT_GIVEN:
|
|
1417
|
+
_body['goal'] = goal
|
|
1418
|
+
if max_steps is not NOT_GIVEN:
|
|
1419
|
+
_body['max_steps'] = max_steps
|
|
1420
|
+
if bot_id is not NOT_GIVEN:
|
|
1421
|
+
_body['bot_id'] = bot_id
|
|
1422
|
+
if brain is not NOT_GIVEN:
|
|
1423
|
+
_body['brain'] = brain
|
|
1424
|
+
return self._client.request("POST", f"/v1/phones/{_q(serial)}/task", body=_body, idempotency_key=idempotency_key, timeout=timeout, extra_headers=extra_headers) # type: ignore[return-value]
|
|
1425
|
+
|
|
1426
|
+
def get_live_view(self, serial: str, *, idempotency_key: Optional[str] = None, timeout: Optional[float] = None, extra_headers: Optional[Dict[str, str]] = None) -> "PhoneLiveView":
|
|
1427
|
+
"""Watch a phone live
|
|
1428
|
+
|
|
1429
|
+
A view-only live view of the phone's screen for 10 minutes: `viewer_url` is a page you can open or put in an iframe, and `stream_url` is the raw stream (`{type:"meta"}` JSON, then H.264 Annex-B frames) for your own player. Both URLs carry the live view id as their only credential and stop working after `expires_in` seconds; neither can send input. A new view needs a new call. 409 `phone_offline` when the phone isn't connected to the Desktop.
|
|
1430
|
+
|
|
1431
|
+
``POST /v1/phones/{serial}/live``
|
|
1432
|
+
"""
|
|
1433
|
+
return self._client.request("POST", f"/v1/phones/{_q(serial)}/live", idempotency_key=idempotency_key, timeout=timeout, extra_headers=extra_headers) # type: ignore[return-value]
|
|
1434
|
+
|
|
1435
|
+
def start_fleet_run(self, *, idempotency_key: Optional[str] = None, timeout: Optional[float] = None, extra_headers: Optional[Dict[str, str]] = None) -> Dict[str, Any]:
|
|
1436
|
+
"""Run one task or skill on many phones
|
|
1437
|
+
|
|
1438
|
+
Starts one background run per listed phone: a task (`goal`) or a saved skill (`skill`). Answers 202 with the fleet run; phones whose run could not start are in `not_started` with the error. Every run counts against the plan's concurrent phone-run limit (Sandbox 1, Pay as you go 5, Growth 25, Enterprise by contract); asking for more than the room left is 429 `phone_run_limit` and starts nothing. Each run ends with `phone.run.completed`; the whole fleet run with `phone.fleet_run.completed`.
|
|
1439
|
+
|
|
1440
|
+
``POST /v1/phones/runs``
|
|
1441
|
+
"""
|
|
1442
|
+
return self._client.request("POST", f"/v1/phones/runs", idempotency_key=idempotency_key, timeout=timeout, extra_headers=extra_headers) # type: ignore[return-value]
|
|
1443
|
+
|
|
1444
|
+
def get_fleet_run(self, id: str, *, timeout: Optional[float] = None, extra_headers: Optional[Dict[str, str]] = None) -> Dict[str, Any]:
|
|
1445
|
+
"""A fleet run
|
|
1446
|
+
|
|
1447
|
+
The fleet run and each phone's run (status, ok, start and end).
|
|
1448
|
+
|
|
1449
|
+
``GET /v1/phones/runs/{id}``
|
|
1450
|
+
"""
|
|
1451
|
+
return self._client.request("GET", f"/v1/phones/runs/{_q(id)}", timeout=timeout, extra_headers=extra_headers) # type: ignore[return-value]
|
|
1452
|
+
|
|
1453
|
+
|
|
1454
|
+
class MailboxesResource:
|
|
1455
|
+
def __init__(self, client: Transport) -> None:
|
|
1456
|
+
self._client = client
|
|
1457
|
+
|
|
1458
|
+
def list(self, *, limit: int = NOT_GIVEN, after: str = NOT_GIVEN, account_id: str = NOT_GIVEN, agent_id: str = NOT_GIVEN, timeout: Optional[float] = None, extra_headers: Optional[Dict[str, str]] = None) -> "MailboxList":
|
|
1459
|
+
"""List mailboxes
|
|
1460
|
+
|
|
1461
|
+
``GET /v1/mailboxes``
|
|
1462
|
+
"""
|
|
1463
|
+
_query: Dict[str, Any] = {}
|
|
1464
|
+
if limit is not NOT_GIVEN:
|
|
1465
|
+
_query['limit'] = limit
|
|
1466
|
+
if after is not NOT_GIVEN:
|
|
1467
|
+
_query['after'] = after
|
|
1468
|
+
if account_id is not NOT_GIVEN:
|
|
1469
|
+
_query['account_id'] = account_id
|
|
1470
|
+
if agent_id is not NOT_GIVEN:
|
|
1471
|
+
_query['agent_id'] = agent_id
|
|
1472
|
+
return self._client.request("GET", f"/v1/mailboxes", query=_query, timeout=timeout, extra_headers=extra_headers) # type: ignore[return-value]
|
|
1473
|
+
|
|
1474
|
+
def list_all(self, *, limit: int = NOT_GIVEN, after: str = NOT_GIVEN, account_id: str = NOT_GIVEN, agent_id: str = NOT_GIVEN, timeout: Optional[float] = None, extra_headers: Optional[Dict[str, str]] = None) -> Iterator["Mailbox"]:
|
|
1475
|
+
"""Every item of ``list``, fetching pages as you iterate."""
|
|
1476
|
+
_query: Dict[str, Any] = {}
|
|
1477
|
+
if limit is not NOT_GIVEN:
|
|
1478
|
+
_query['limit'] = limit
|
|
1479
|
+
if after is not NOT_GIVEN:
|
|
1480
|
+
_query['after'] = after
|
|
1481
|
+
if account_id is not NOT_GIVEN:
|
|
1482
|
+
_query['account_id'] = account_id
|
|
1483
|
+
if agent_id is not NOT_GIVEN:
|
|
1484
|
+
_query['agent_id'] = agent_id
|
|
1485
|
+
def _fetch(cursor: Optional[str]) -> Page:
|
|
1486
|
+
q = dict(_query)
|
|
1487
|
+
if cursor is not None:
|
|
1488
|
+
q['after'] = cursor
|
|
1489
|
+
return self._client.request("GET", f"/v1/mailboxes", query=q, timeout=timeout, extra_headers=extra_headers) # type: ignore[return-value]
|
|
1490
|
+
return self._client.paginate(_fetch, after if after is not NOT_GIVEN else None)
|
|
1491
|
+
|
|
1492
|
+
def create_mailbox(self, *, account_id: str = NOT_GIVEN, agent_id: str = NOT_GIVEN, local_part: str = NOT_GIVEN, display_name: str = NOT_GIVEN, idempotency_key: Optional[str] = None, timeout: Optional[float] = None, extra_headers: Optional[Dict[str, str]] = None) -> "Mailbox":
|
|
1493
|
+
"""Create a mailbox for an account or agent
|
|
1494
|
+
|
|
1495
|
+
An address on Allternit Mail (`local_part@bots.allternit.com`; `-2`, `-3`… when taken). With `agent_id`, the account is the agent's. Needs a card on file; live projects are billed `mailbox_month` ($2 a month). Requires the `mail` scope.
|
|
1496
|
+
|
|
1497
|
+
``POST /v1/mailboxes``
|
|
1498
|
+
"""
|
|
1499
|
+
_body: Dict[str, Any] = {}
|
|
1500
|
+
if account_id is not NOT_GIVEN:
|
|
1501
|
+
_body['account_id'] = account_id
|
|
1502
|
+
if agent_id is not NOT_GIVEN:
|
|
1503
|
+
_body['agent_id'] = agent_id
|
|
1504
|
+
if local_part is not NOT_GIVEN:
|
|
1505
|
+
_body['local_part'] = local_part
|
|
1506
|
+
if display_name is not NOT_GIVEN:
|
|
1507
|
+
_body['display_name'] = display_name
|
|
1508
|
+
return self._client.request("POST", f"/v1/mailboxes", body=_body, idempotency_key=idempotency_key, timeout=timeout, extra_headers=extra_headers) # type: ignore[return-value]
|
|
1509
|
+
|
|
1510
|
+
def get_mailbox(self, id: str, *, timeout: Optional[float] = None, extra_headers: Optional[Dict[str, str]] = None) -> "Mailbox":
|
|
1511
|
+
"""Retrieve a mailbox
|
|
1512
|
+
|
|
1513
|
+
``GET /v1/mailboxes/{id}``
|
|
1514
|
+
"""
|
|
1515
|
+
return self._client.request("GET", f"/v1/mailboxes/{_q(id)}", timeout=timeout, extra_headers=extra_headers) # type: ignore[return-value]
|
|
1516
|
+
|
|
1517
|
+
def delete_mailbox(self, id: str, *, timeout: Optional[float] = None, extra_headers: Optional[Dict[str, str]] = None) -> Dict[str, Any]:
|
|
1518
|
+
"""Delete a mailbox
|
|
1519
|
+
|
|
1520
|
+
Removes the address. A mailbox inside an identity answers `409 mailbox_in_identity`; delete the identity instead.
|
|
1521
|
+
|
|
1522
|
+
``DELETE /v1/mailboxes/{id}``
|
|
1523
|
+
"""
|
|
1524
|
+
return self._client.request("DELETE", f"/v1/mailboxes/{_q(id)}", timeout=timeout, extra_headers=extra_headers) # type: ignore[return-value]
|
|
1525
|
+
|
|
1526
|
+
def list_mail_messages(self, id: str, *, limit: int = NOT_GIVEN, after: str = NOT_GIVEN, verification: bool = NOT_GIVEN, timeout: Optional[float] = None, extra_headers: Optional[Dict[str, str]] = None) -> "MailMessageList":
|
|
1527
|
+
"""List a mailbox's messages
|
|
1528
|
+
|
|
1529
|
+
Newest first. Each carries the one-time codes and sign-in links found in it.
|
|
1530
|
+
|
|
1531
|
+
``GET /v1/mailboxes/{id}/messages``
|
|
1532
|
+
"""
|
|
1533
|
+
_query: Dict[str, Any] = {}
|
|
1534
|
+
if limit is not NOT_GIVEN:
|
|
1535
|
+
_query['limit'] = limit
|
|
1536
|
+
if after is not NOT_GIVEN:
|
|
1537
|
+
_query['after'] = after
|
|
1538
|
+
if verification is not NOT_GIVEN:
|
|
1539
|
+
_query['verification'] = verification
|
|
1540
|
+
return self._client.request("GET", f"/v1/mailboxes/{_q(id)}/messages", query=_query, timeout=timeout, extra_headers=extra_headers) # type: ignore[return-value]
|
|
1541
|
+
|
|
1542
|
+
def list_mail_messages_all(self, id: str, *, limit: int = NOT_GIVEN, after: str = NOT_GIVEN, verification: bool = NOT_GIVEN, timeout: Optional[float] = None, extra_headers: Optional[Dict[str, str]] = None) -> Iterator["MailMessage"]:
|
|
1543
|
+
"""Every item of ``list_mail_messages``, fetching pages as you iterate."""
|
|
1544
|
+
_query: Dict[str, Any] = {}
|
|
1545
|
+
if limit is not NOT_GIVEN:
|
|
1546
|
+
_query['limit'] = limit
|
|
1547
|
+
if after is not NOT_GIVEN:
|
|
1548
|
+
_query['after'] = after
|
|
1549
|
+
if verification is not NOT_GIVEN:
|
|
1550
|
+
_query['verification'] = verification
|
|
1551
|
+
def _fetch(cursor: Optional[str]) -> Page:
|
|
1552
|
+
q = dict(_query)
|
|
1553
|
+
if cursor is not None:
|
|
1554
|
+
q['after'] = cursor
|
|
1555
|
+
return self._client.request("GET", f"/v1/mailboxes/{_q(id)}/messages", query=q, timeout=timeout, extra_headers=extra_headers) # type: ignore[return-value]
|
|
1556
|
+
return self._client.paginate(_fetch, after if after is not NOT_GIVEN else None)
|
|
1557
|
+
|
|
1558
|
+
def get_mail_message(self, id: str, mid: str, *, timeout: Optional[float] = None, extra_headers: Optional[Dict[str, str]] = None) -> Dict[str, Any]:
|
|
1559
|
+
"""Read one message
|
|
1560
|
+
|
|
1561
|
+
``GET /v1/mailboxes/{id}/messages/{mid}``
|
|
1562
|
+
"""
|
|
1563
|
+
return self._client.request("GET", f"/v1/mailboxes/{_q(id)}/messages/{_q(mid)}", timeout=timeout, extra_headers=extra_headers) # type: ignore[return-value]
|
|
1564
|
+
|
|
1565
|
+
def read_mail_otp(self, id: str, *, wait_seconds: int = NOT_GIVEN, since: str = NOT_GIVEN, idempotency_key: Optional[str] = None, timeout: Optional[float] = None, extra_headers: Optional[Dict[str, str]] = None) -> Dict[str, Any]:
|
|
1566
|
+
"""Wait for the newest one-time code
|
|
1567
|
+
|
|
1568
|
+
The newest message with a code or sign-in link received after `since` (default the last 10 minutes), waiting up to `wait_seconds` for one to arrive. `found` is false when none came.
|
|
1569
|
+
|
|
1570
|
+
``POST /v1/mailboxes/{id}/otp``
|
|
1571
|
+
"""
|
|
1572
|
+
_body: Dict[str, Any] = {}
|
|
1573
|
+
if wait_seconds is not NOT_GIVEN:
|
|
1574
|
+
_body['wait_seconds'] = wait_seconds
|
|
1575
|
+
if since is not NOT_GIVEN:
|
|
1576
|
+
_body['since'] = since
|
|
1577
|
+
return self._client.request("POST", f"/v1/mailboxes/{_q(id)}/otp", body=_body, idempotency_key=idempotency_key, timeout=timeout, extra_headers=extra_headers) # type: ignore[return-value]
|
|
1578
|
+
|
|
1579
|
+
def send_mail(self, id: str, *, serials: List[str], goal: str = NOT_GIVEN, skill: str = NOT_GIVEN, inputs: Dict[str, Any] = NOT_GIVEN, max_steps: int = NOT_GIVEN, bot_id: str = NOT_GIVEN, brain: str = NOT_GIVEN, idempotency_key: Optional[str] = None, timeout: Optional[float] = None, extra_headers: Optional[Dict[str, str]] = None) -> "PhoneFleetRun":
|
|
1580
|
+
"""Send an email from a mailbox
|
|
1581
|
+
|
|
1582
|
+
Live projects only. The call is the approval, so the send is queued at once; `pending_approval` only when Allternit Mail held it and could not release it.
|
|
1583
|
+
|
|
1584
|
+
``POST /v1/mailboxes/{id}/send``
|
|
1585
|
+
"""
|
|
1586
|
+
_body: Dict[str, Any] = {}
|
|
1587
|
+
if serials is not NOT_GIVEN:
|
|
1588
|
+
_body['serials'] = serials
|
|
1589
|
+
if goal is not NOT_GIVEN:
|
|
1590
|
+
_body['goal'] = goal
|
|
1591
|
+
if skill is not NOT_GIVEN:
|
|
1592
|
+
_body['skill'] = skill
|
|
1593
|
+
if inputs is not NOT_GIVEN:
|
|
1594
|
+
_body['inputs'] = inputs
|
|
1595
|
+
if max_steps is not NOT_GIVEN:
|
|
1596
|
+
_body['max_steps'] = max_steps
|
|
1597
|
+
if bot_id is not NOT_GIVEN:
|
|
1598
|
+
_body['bot_id'] = bot_id
|
|
1599
|
+
if brain is not NOT_GIVEN:
|
|
1600
|
+
_body['brain'] = brain
|
|
1601
|
+
return self._client.request("POST", f"/v1/mailboxes/{_q(id)}/send", body=_body, idempotency_key=idempotency_key, timeout=timeout, extra_headers=extra_headers) # type: ignore[return-value]
|
|
1602
|
+
|
|
1603
|
+
|
|
1604
|
+
class IdentitiesResource:
|
|
1605
|
+
def __init__(self, client: Transport) -> None:
|
|
1606
|
+
self._client = client
|
|
1607
|
+
|
|
1608
|
+
def list(self, *, limit: int = NOT_GIVEN, after: str = NOT_GIVEN, account_id: str = NOT_GIVEN, agent_id: str = NOT_GIVEN, timeout: Optional[float] = None, extra_headers: Optional[Dict[str, str]] = None) -> "IdentityList":
|
|
1609
|
+
"""List agent identities
|
|
1610
|
+
|
|
1611
|
+
``GET /v1/identities``
|
|
1612
|
+
"""
|
|
1613
|
+
_query: Dict[str, Any] = {}
|
|
1614
|
+
if limit is not NOT_GIVEN:
|
|
1615
|
+
_query['limit'] = limit
|
|
1616
|
+
if after is not NOT_GIVEN:
|
|
1617
|
+
_query['after'] = after
|
|
1618
|
+
if account_id is not NOT_GIVEN:
|
|
1619
|
+
_query['account_id'] = account_id
|
|
1620
|
+
if agent_id is not NOT_GIVEN:
|
|
1621
|
+
_query['agent_id'] = agent_id
|
|
1622
|
+
return self._client.request("GET", f"/v1/identities", query=_query, timeout=timeout, extra_headers=extra_headers) # type: ignore[return-value]
|
|
1623
|
+
|
|
1624
|
+
def list_all(self, *, limit: int = NOT_GIVEN, after: str = NOT_GIVEN, account_id: str = NOT_GIVEN, agent_id: str = NOT_GIVEN, timeout: Optional[float] = None, extra_headers: Optional[Dict[str, str]] = None) -> Iterator["Identity"]:
|
|
1625
|
+
"""Every item of ``list``, fetching pages as you iterate."""
|
|
1626
|
+
_query: Dict[str, Any] = {}
|
|
1627
|
+
if limit is not NOT_GIVEN:
|
|
1628
|
+
_query['limit'] = limit
|
|
1629
|
+
if after is not NOT_GIVEN:
|
|
1630
|
+
_query['after'] = after
|
|
1631
|
+
if account_id is not NOT_GIVEN:
|
|
1632
|
+
_query['account_id'] = account_id
|
|
1633
|
+
if agent_id is not NOT_GIVEN:
|
|
1634
|
+
_query['agent_id'] = agent_id
|
|
1635
|
+
def _fetch(cursor: Optional[str]) -> Page:
|
|
1636
|
+
q = dict(_query)
|
|
1637
|
+
if cursor is not None:
|
|
1638
|
+
q['after'] = cursor
|
|
1639
|
+
return self._client.request("GET", f"/v1/identities", query=q, timeout=timeout, extra_headers=extra_headers) # type: ignore[return-value]
|
|
1640
|
+
return self._client.paginate(_fetch, after if after is not NOT_GIVEN else None)
|
|
1641
|
+
|
|
1642
|
+
def create_identity(self, *, account_id: str = NOT_GIVEN, agent_id: str = NOT_GIVEN, area_code: str = NOT_GIVEN, number_type: Literal['local', 'toll_free'] = NOT_GIVEN, phone_serial: str = NOT_GIVEN, local_part: str = NOT_GIVEN, display_name: str = NOT_GIVEN, idempotency_key: Optional[str] = None, timeout: Optional[float] = None, extra_headers: Optional[Dict[str, str]] = None) -> "Identity":
|
|
1643
|
+
"""Give an agent a number, a mailbox and a phone in one call
|
|
1644
|
+
|
|
1645
|
+
Gets a number (simulated in sandbox; in live the first available, in `area_code` when given), a mailbox and, with `phone_serial`, binds one of the owner's linked phones. Requires the `numbers` and `mail` scopes, plus `phones` (and an unbound key) with `phone_serial`. Billed as the Agent identity bundle ($9 a month, meter `identity_month`).
|
|
1646
|
+
|
|
1647
|
+
``POST /v1/identities``
|
|
1648
|
+
"""
|
|
1649
|
+
_body: Dict[str, Any] = {}
|
|
1650
|
+
if account_id is not NOT_GIVEN:
|
|
1651
|
+
_body['account_id'] = account_id
|
|
1652
|
+
if agent_id is not NOT_GIVEN:
|
|
1653
|
+
_body['agent_id'] = agent_id
|
|
1654
|
+
if area_code is not NOT_GIVEN:
|
|
1655
|
+
_body['area_code'] = area_code
|
|
1656
|
+
if number_type is not NOT_GIVEN:
|
|
1657
|
+
_body['number_type'] = number_type
|
|
1658
|
+
if phone_serial is not NOT_GIVEN:
|
|
1659
|
+
_body['phone_serial'] = phone_serial
|
|
1660
|
+
if local_part is not NOT_GIVEN:
|
|
1661
|
+
_body['local_part'] = local_part
|
|
1662
|
+
if display_name is not NOT_GIVEN:
|
|
1663
|
+
_body['display_name'] = display_name
|
|
1664
|
+
return self._client.request("POST", f"/v1/identities", body=_body, idempotency_key=idempotency_key, timeout=timeout, extra_headers=extra_headers) # type: ignore[return-value]
|
|
1665
|
+
|
|
1666
|
+
def get_identity(self, id: str, *, timeout: Optional[float] = None, extra_headers: Optional[Dict[str, str]] = None) -> "Identity":
|
|
1667
|
+
"""Retrieve an identity
|
|
1668
|
+
|
|
1669
|
+
``GET /v1/identities/{id}``
|
|
1670
|
+
"""
|
|
1671
|
+
return self._client.request("GET", f"/v1/identities/{_q(id)}", timeout=timeout, extra_headers=extra_headers) # type: ignore[return-value]
|
|
1672
|
+
|
|
1673
|
+
def delete_identity(self, id: str, *, timeout: Optional[float] = None, extra_headers: Optional[Dict[str, str]] = None) -> Dict[str, Any]:
|
|
1674
|
+
"""Delete an identity
|
|
1675
|
+
|
|
1676
|
+
Releases the number and removes the mailbox.
|
|
1677
|
+
|
|
1678
|
+
``DELETE /v1/identities/{id}``
|
|
1679
|
+
"""
|
|
1680
|
+
return self._client.request("DELETE", f"/v1/identities/{_q(id)}", timeout=timeout, extra_headers=extra_headers) # type: ignore[return-value]
|
|
1681
|
+
|
|
1682
|
+
|
|
1232
1683
|
def _q(v: str) -> str:
|
|
1233
1684
|
from urllib.parse import quote
|
|
1234
1685
|
return quote(str(v), safe='')
|
|
@@ -1250,3 +1701,6 @@ class GeneratedResources:
|
|
|
1250
1701
|
self.channels = ChannelsResource(client)
|
|
1251
1702
|
self.twin = TwinResource(client)
|
|
1252
1703
|
self.computers = ComputersResource(client)
|
|
1704
|
+
self.phones = PhonesResource(client)
|
|
1705
|
+
self.mailboxes = MailboxesResource(client)
|
|
1706
|
+
self.identities = IdentitiesResource(client)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: allternit-platform
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Python SDK for the Allternit Platform API (agents, conversations, numbers, messaging, webhooks, usage).
|
|
5
5
|
License: Apache-2.0
|
|
6
6
|
Project-URL: Documentation, https://docs.allternit.com/api/platform/sdks/python
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "allternit-platform"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.3.0"
|
|
8
8
|
description = "Python SDK for the Allternit Platform API (agents, conversations, numbers, messaging, webhooks, usage)."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.9"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{allternit_platform-0.2.0 → allternit_platform-0.3.0}/allternit_platform.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
{allternit_platform-0.2.0 → allternit_platform-0.3.0}/allternit_platform.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|