assistant-runtime-sdk 1.9.0__tar.gz → 1.11.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: assistant_runtime_sdk
3
- Version: 1.9.0
3
+ Version: 1.11.0
4
4
  Summary: Python SDK for FAC Cloud - the Assistant Runtime AI assistant backend
5
5
  Keywords: frappe,ai,assistant,llm,mcp,anthropic,openai,streaming
6
6
  Author: Paul Clinton
@@ -158,7 +158,8 @@ surface. A representative selection:
158
158
 
159
159
  **Billing** — `get_plan_comparison()`, `get_usage_dashboard()`, `get_usage_history()`,
160
160
  `get_credit_balance()`, `initiate_checkout()`, `create_hosted_checkout()`,
161
- `verify_checkout()`, `upgrade_plan()`, `cancel_subscription()`, `get_invoices()`,
161
+ `get_checkout_session_status()`, `verify_checkout()`, `upgrade_plan()`,
162
+ `cancel_subscription()`, `get_invoices()`,
162
163
  `get_payment_instrument()`, `update_payment_method()`
163
164
 
164
165
  **Users & seats** — `register_user()`, `get_user()`, `list_users()`, `invite_user()`,
@@ -125,7 +125,8 @@ surface. A representative selection:
125
125
 
126
126
  **Billing** — `get_plan_comparison()`, `get_usage_dashboard()`, `get_usage_history()`,
127
127
  `get_credit_balance()`, `initiate_checkout()`, `create_hosted_checkout()`,
128
- `verify_checkout()`, `upgrade_plan()`, `cancel_subscription()`, `get_invoices()`,
128
+ `get_checkout_session_status()`, `verify_checkout()`, `upgrade_plan()`,
129
+ `cancel_subscription()`, `get_invoices()`,
129
130
  `get_payment_instrument()`, `update_payment_method()`
130
131
 
131
132
  **Users & seats** — `register_user()`, `get_user()`, `list_users()`, `invite_user()`,
@@ -53,7 +53,7 @@ Standalone Functions:
53
53
 
54
54
  """
55
55
 
56
- __version__ = "1.9.0"
56
+ __version__ = "1.11.0"
57
57
  __author__ = "Paul Clinton"
58
58
  __license__ = "AGPL-3.0"
59
59
 
@@ -61,7 +61,14 @@ __license__ = "AGPL-3.0"
61
61
  # Core Client Classes
62
62
  # =============================================================================
63
63
 
64
- from .client import AssistantRuntimeClient, get_terms, register_tenant, get_registration_state
64
+ from .client import (
65
+ AssistantRuntimeClient,
66
+ change_pending_owner_email,
67
+ get_registration_state,
68
+ get_terms,
69
+ register_tenant,
70
+ resend_owner_verification,
71
+ )
65
72
 
66
73
  # Async client - import lazily to avoid requiring aiohttp
67
74
  # Skill providers - import lazily to avoid requiring strands-agents
@@ -173,6 +180,8 @@ __all__ = [
173
180
  "get_terms",
174
181
  "register_tenant",
175
182
  "get_registration_state",
183
+ "resend_owner_verification",
184
+ "change_pending_owner_email",
176
185
  # Exceptions - new names
177
186
  "ARError",
178
187
  "ARAuthenticationError",
@@ -384,6 +384,7 @@ class AsyncAssistantRuntimeClient(BaseAssistantRuntimeClient):
384
384
  *,
385
385
  web_search: Optional[bool] = None,
386
386
  thinking_enabled: Optional[bool] = None,
387
+ reasoning_effort: Optional[str] = None,
387
388
  ) -> AsyncGenerator[Dict[str, Any], None]:
388
389
  """Async version of AssistantRuntimeClient.stream_chat.
389
390
 
@@ -400,6 +401,7 @@ class AsyncAssistantRuntimeClient(BaseAssistantRuntimeClient):
400
401
  continue_from_message_id=continue_from_message_id,
401
402
  web_search=web_search,
402
403
  thinking_enabled=thinking_enabled,
404
+ reasoning_effort=reasoning_effort,
403
405
  )
404
406
  url = self._build_endpoint_url("streaming.stream_chat")
405
407
  payload = self._with_site_url(payload)
@@ -947,6 +949,11 @@ class AsyncAssistantRuntimeClient(BaseAssistantRuntimeClient):
947
949
  endpoint, payload = self._prepare_create_hosted_checkout(purpose, params, return_url)
948
950
  return await self._request_post_json(endpoint, payload, api_base=self.billing_api_base)
949
951
 
952
+ async def get_checkout_session_status(self, session: str) -> Optional[Dict[str, Any]]:
953
+ """Async version of AssistantRuntimeClient.get_checkout_session_status."""
954
+ endpoint, payload = self._prepare_get_checkout_session_status(session)
955
+ return await self._request_post_json(endpoint, payload, api_base=self.billing_api_base)
956
+
950
957
  async def verify_checkout(self, session_id: Optional[str] = None) -> Optional[Dict[str, Any]]:
951
958
  """Async version of AssistantRuntimeClient.verify_checkout."""
952
959
  endpoint, payload = self._prepare_verify_checkout(session_id)
@@ -316,6 +316,7 @@ class BaseAssistantRuntimeClient:
316
316
  continue_from_message_id: Optional[str] = None,
317
317
  web_search: Optional[bool] = None,
318
318
  thinking_enabled: Optional[bool] = None,
319
+ reasoning_effort: Optional[str] = None,
319
320
  ) -> Dict[str, Any]:
320
321
  """
321
322
  Prepare JSON payload for stream_chat POST request.
@@ -347,6 +348,8 @@ class BaseAssistantRuntimeClient:
347
348
  previous truncated response from
348
349
  web_search: Optional explicit web-search toggle for this turn
349
350
  thinking_enabled: Optional explicit extended-thinking toggle for this turn
351
+ reasoning_effort: Optional thinking level for this turn — "off", "low", "medium",
352
+ "high", "xhigh" or "max"
350
353
 
351
354
  Returns:
352
355
  Payload dict ready for JSON body
@@ -400,6 +403,9 @@ class BaseAssistantRuntimeClient:
400
403
  if thinking_enabled is not None:
401
404
  payload["thinking_enabled"] = bool(thinking_enabled)
402
405
 
406
+ if reasoning_effort is not None:
407
+ payload["reasoning_effort"] = str(reasoning_effort).strip().lower()
408
+
403
409
  return payload
404
410
 
405
411
  def _prepare_resource_params(
@@ -987,6 +993,15 @@ class BaseAssistantRuntimeClient:
987
993
  payload["return_url"] = return_url
988
994
  return "create_hosted_checkout", payload
989
995
 
996
+ def _prepare_get_checkout_session_status(self, session: str) -> tuple:
997
+ self._require_billing()
998
+ if not session:
999
+ raise ValueError("session is required")
1000
+ return "get_checkout_session_status", {
1001
+ "tenant_id": self.tenant_id,
1002
+ "session": session,
1003
+ }
1004
+
990
1005
  def _prepare_verify_checkout(self, session_id: Optional[str] = None) -> tuple:
991
1006
  self._require_billing()
992
1007
  payload: Dict[str, Any] = {"tenant_id": self.tenant_id}
@@ -329,6 +329,7 @@ class AssistantRuntimeClient(BaseAssistantRuntimeClient):
329
329
  *,
330
330
  web_search: Optional[bool] = None,
331
331
  thinking_enabled: Optional[bool] = None,
332
+ reasoning_effort: Optional[str] = None,
332
333
  ) -> Generator[Dict[str, Any], None, None]:
333
334
  """
334
335
  Stream chat response from Assistant Runtime.
@@ -361,6 +362,9 @@ class AssistantRuntimeClient(BaseAssistantRuntimeClient):
361
362
  truncated response from
362
363
  web_search: Optional explicit web-search toggle for this turn
363
364
  thinking_enabled: Optional explicit extended-thinking toggle for this turn
365
+ reasoning_effort: Optional thinking level for this turn — "off", "low", "medium",
366
+ "high", "xhigh" or "max". Servers that predate it ignore it, so callers
367
+ should also send thinking_enabled.
364
368
 
365
369
  Yields:
366
370
  Parsed SSE events with structure:
@@ -390,6 +394,7 @@ class AssistantRuntimeClient(BaseAssistantRuntimeClient):
390
394
  continue_from_message_id=continue_from_message_id,
391
395
  web_search=web_search,
392
396
  thinking_enabled=thinking_enabled,
397
+ reasoning_effort=reasoning_effort,
393
398
  )
394
399
  url = self._build_endpoint_url("streaming.stream_chat")
395
400
  payload = self._with_site_url(payload)
@@ -1394,6 +1399,27 @@ class AssistantRuntimeClient(BaseAssistantRuntimeClient):
1394
1399
  endpoint, payload = self._prepare_create_hosted_checkout(purpose, params, return_url)
1395
1400
  return self._request_post_json(endpoint, payload, api_base=self.billing_api_base)
1396
1401
 
1402
+ def get_checkout_session_status(self, session: str) -> Optional[Dict[str, Any]]:
1403
+ """
1404
+ Ask whether the purchase a hosted checkout was opened for has landed.
1405
+
1406
+ The checkout page returns the user with ``fac_checkout=<session>``
1407
+ (and ``result=success|processing|cancelled|failed``) on the query
1408
+ string. Poll this with that session until ``done`` is true. It only
1409
+ reads, so polling it never touches the payment gateway.
1410
+
1411
+ Args:
1412
+ session: The ``fac_checkout`` value from the return URL — the
1413
+ ``session`` that :meth:`create_hosted_checkout` returned.
1414
+
1415
+ Returns:
1416
+ Dict with status, purpose, target_plan, plan, pending_plan,
1417
+ payment_status, done (bool) and outcome — one of "applied",
1418
+ "processing", "failed" or "cancelled".
1419
+ """
1420
+ endpoint, payload = self._prepare_get_checkout_session_status(session)
1421
+ return self._request_post_json(endpoint, payload, api_base=self.billing_api_base)
1422
+
1397
1423
  def verify_checkout(self, session_id: Optional[str] = None) -> Optional[Dict[str, Any]]:
1398
1424
  """Verify payment completion after checkout."""
1399
1425
  endpoint, payload = self._prepare_verify_checkout(session_id)
@@ -3308,7 +3334,11 @@ def register_tenant(
3308
3334
  registration cap exactly once.
3309
3335
 
3310
3336
  Returns:
3311
- Registration result with tenant_id (secret delivered via email link), or error
3337
+ Registration result with tenant_id (secret delivered via email link), or
3338
+ error. A newly created pending tenant also carries ``pending_token``,
3339
+ returned only this once: store it securely, because
3340
+ ``resend_owner_verification`` and ``change_pending_owner_email``
3341
+ require it.
3312
3342
  """
3313
3343
  url = f"{ar_url.rstrip('/')}/api/method/assistant_runtime.api.register_tenant"
3314
3344
 
@@ -3368,6 +3398,70 @@ def get_registration_state(
3368
3398
  return {"error": str(e)}
3369
3399
 
3370
3400
 
3401
+ def _post_guest(ar_url: str, method: str, payload: Dict[str, Any], timeout: int = 30) -> Dict[str, Any]:
3402
+ """POST to a guest AR method, returning AR's own message when it fails.
3403
+
3404
+ Failures come back as ``{"error", "status_code", "exc_type"}``. ``error`` is
3405
+ the message AR raised when the body carries one, so a caller can show it
3406
+ instead of ``str(HTTPError)``; ``exc_type`` is set only in that case.
3407
+ """
3408
+ url = f"{ar_url.rstrip('/')}/api/method/assistant_runtime.api.{method}"
3409
+ try:
3410
+ response = requests.post(
3411
+ url, json=payload, headers={"Content-Type": "application/json"}, timeout=timeout,
3412
+ )
3413
+ response.raise_for_status()
3414
+ return response.json().get("message", response.json())
3415
+ except requests.exceptions.HTTPError as e:
3416
+ status_code = e.response.status_code if e.response is not None else None
3417
+ try:
3418
+ data = e.response.json() if e.response is not None else None
3419
+ except ValueError:
3420
+ data = None
3421
+ message = BaseAssistantRuntimeClient._extract_error_from_data(data) if isinstance(data, dict) else None
3422
+ if not message:
3423
+ return {"error": str(e), "status_code": status_code}
3424
+ return {"error": message, "status_code": status_code, "exc_type": data.get("exc_type")}
3425
+ except requests.exceptions.RequestException as e:
3426
+ return {"error": str(e), "status_code": None}
3427
+
3428
+
3429
+ def resend_owner_verification(ar_url: str, site_url: str, pending_token: str) -> Dict[str, Any]:
3430
+ """Ask AR to re-send the pending verification link. No terms, no secret.
3431
+
3432
+ Args:
3433
+ pending_token: The token ``register_tenant`` returned when this site's
3434
+ tenant was created. AR refuses the call without it.
3435
+
3436
+ Returns:
3437
+ AR's result (``success``, ``retry_after``, ``already_verified``,
3438
+ ``owner_email_masked``), or ``{"error", "status_code", "exc_type"}``.
3439
+ """
3440
+ return _post_guest(
3441
+ ar_url, "resend_owner_verification", {"site_url": site_url, "pending_token": pending_token}
3442
+ )
3443
+
3444
+
3445
+ def change_pending_owner_email(
3446
+ ar_url: str, site_url: str, owner_email: str, pending_token: str
3447
+ ) -> Dict[str, Any]:
3448
+ """Correct the owner address while the tenant is still unverified.
3449
+
3450
+ Args:
3451
+ pending_token: The token ``register_tenant`` returned when this site's
3452
+ tenant was created. AR refuses the call without it.
3453
+
3454
+ Returns:
3455
+ AR's result (``success``, ``retry_after``, ``owner_email_masked``), or
3456
+ ``{"error", "status_code", "exc_type"}``.
3457
+ """
3458
+ return _post_guest(
3459
+ ar_url,
3460
+ "change_pending_owner_email",
3461
+ {"site_url": site_url, "owner_email": owner_email, "pending_token": pending_token},
3462
+ )
3463
+
3464
+
3371
3465
  def get_initial_secret(ar_url: str, verification_token: str) -> Dict[str, Any]:
3372
3466
  """One-shot retrieval of the freshly-minted tenant_secret after the
3373
3467
  owner has confirmed their email via the verification link.
@@ -16,7 +16,15 @@ from typing import TypedDict, List, Optional, Any
16
16
  # =============================================================================
17
17
 
18
18
 
19
- class ModelInfo(TypedDict):
19
+ class _ModelInfoOptional(TypedDict, total=False):
20
+ """Keys newer runtimes add to each model; absent on older ones."""
21
+
22
+ thinking_effective: bool
23
+ reasoning_levels: List[str]
24
+ reasoning_off_floor: bool
25
+
26
+
27
+ class ModelInfo(_ModelInfoOptional):
20
28
  """Information about an available AI model."""
21
29
 
22
30
  model_id: str
@@ -104,7 +112,7 @@ class RoutingReceiptData(TypedDict):
104
112
  shortlist_size: int
105
113
  pick_reason: str
106
114
  notices: List[str]
107
- thinking: dict # requested, applied, effort, not_applied_reason
115
+ thinking: dict # requested, applied, effort {requested, applied}, effective_budget, not_applied_reason
108
116
  credits: dict # actual
109
117
  cycles: int
110
118
  also_ran: List[str]
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "assistant_runtime_sdk"
3
- version = "1.9.0"
3
+ version = "1.11.0"
4
4
  description = "Python SDK for FAC Cloud - the Assistant Runtime AI assistant backend"
5
5
  readme = "README.md"
6
6
  license = {text = "AGPL-3.0"}