oneshot-python 0.14.0__tar.gz → 0.16.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.
- {oneshot_python-0.14.0 → oneshot_python-0.16.0}/PKG-INFO +1 -1
- {oneshot_python-0.14.0 → oneshot_python-0.16.0}/oneshot/client.py +49 -15
- {oneshot_python-0.14.0 → oneshot_python-0.16.0}/pyproject.toml +1 -1
- {oneshot_python-0.14.0 → oneshot_python-0.16.0}/tests/test_email_payload.py +12 -0
- {oneshot_python-0.14.0 → oneshot_python-0.16.0}/.gitignore +0 -0
- {oneshot_python-0.14.0 → oneshot_python-0.16.0}/README.md +0 -0
- {oneshot_python-0.14.0 → oneshot_python-0.16.0}/oneshot/__init__.py +0 -0
- {oneshot_python-0.14.0 → oneshot_python-0.16.0}/oneshot/_errors.py +0 -0
- {oneshot_python-0.14.0 → oneshot_python-0.16.0}/oneshot/_types.py +0 -0
- {oneshot_python-0.14.0 → oneshot_python-0.16.0}/oneshot/x402.py +0 -0
- {oneshot_python-0.14.0 → oneshot_python-0.16.0}/tests/__init__.py +0 -0
- {oneshot_python-0.14.0 → oneshot_python-0.16.0}/tests/test_balance.py +0 -0
- {oneshot_python-0.14.0 → oneshot_python-0.16.0}/tests/test_compute.py +0 -0
- {oneshot_python-0.14.0 → oneshot_python-0.16.0}/tests/test_emergency_error.py +0 -0
- {oneshot_python-0.14.0 → oneshot_python-0.16.0}/tests/test_max_cost_header.py +0 -0
- {oneshot_python-0.14.0 → oneshot_python-0.16.0}/tests/test_phones_pending.py +0 -0
- {oneshot_python-0.14.0 → oneshot_python-0.16.0}/tests/test_request_id.py +0 -0
- {oneshot_python-0.14.0 → oneshot_python-0.16.0}/tests/test_tag_receipt_value.py +0 -0
- {oneshot_python-0.14.0 → oneshot_python-0.16.0}/tests/test_x402.py +0 -0
- {oneshot_python-0.14.0 → oneshot_python-0.16.0}/uv.lock +0 -0
|
@@ -38,7 +38,7 @@ try:
|
|
|
38
38
|
|
|
39
39
|
SDK_VERSION = _pkg_version("oneshot-python")
|
|
40
40
|
except Exception: # pragma: no cover - editable/source runs without dist metadata
|
|
41
|
-
SDK_VERSION = "0.
|
|
41
|
+
SDK_VERSION = "0.16.0"
|
|
42
42
|
|
|
43
43
|
# ---------------------------------------------------------------------------
|
|
44
44
|
# Environment configuration
|
|
@@ -62,6 +62,7 @@ def _build_email_payload(
|
|
|
62
62
|
extra: dict[str, Any],
|
|
63
63
|
*,
|
|
64
64
|
reply_to_email_id: Optional[str] = None,
|
|
65
|
+
mailbox_mode: Optional[str] = None,
|
|
65
66
|
) -> dict[str, Any]:
|
|
66
67
|
"""Build the /v1/tools/email/send body in the API's field convention.
|
|
67
68
|
|
|
@@ -93,6 +94,8 @@ def _build_email_payload(
|
|
|
93
94
|
payload["reply_to_email_id"] = reply_to_email_id
|
|
94
95
|
if from_name:
|
|
95
96
|
payload["from_name"] = from_name
|
|
97
|
+
if mailbox_mode:
|
|
98
|
+
payload["mailbox_mode"] = mailbox_mode
|
|
96
99
|
return payload
|
|
97
100
|
|
|
98
101
|
|
|
@@ -760,6 +763,7 @@ class OneShotClient:
|
|
|
760
763
|
from_mailbox: Optional[str] = None,
|
|
761
764
|
from_name: Optional[str] = None,
|
|
762
765
|
reply_to_email_id: Optional[str] = None,
|
|
766
|
+
mailbox_mode: Optional[str] = None,
|
|
763
767
|
**kwargs: Any,
|
|
764
768
|
) -> Any:
|
|
765
769
|
"""Send an email. Blocking.
|
|
@@ -767,10 +771,21 @@ class OneShotClient:
|
|
|
767
771
|
Set ``reply_to_email_id`` (an inbound email id from ``inbox()``) to
|
|
768
772
|
reply within a thread — ``to``/``subject`` are then derived from the
|
|
769
773
|
inbound message unless given.
|
|
774
|
+
|
|
775
|
+
The first send from any ``from_mailbox`` on an owned domain — including
|
|
776
|
+
the default ``agent@`` — provisions a mailbox per address and adds a
|
|
777
|
+
one-time ``mailbox_provisioning_fee`` to the quote; it's free only once
|
|
778
|
+
that address is already provisioned (``active``).
|
|
779
|
+
|
|
780
|
+
``mailbox_mode`` ('relay' | 'mailbox', default 'relay') selects how a
|
|
781
|
+
domain sends: 'relay' is header-only send (no per-address mailbox, no
|
|
782
|
+
mailbox fee); 'mailbox' provisions a real dedicated mailbox per address
|
|
783
|
+
(better deliverability + per-address warmup). Only applies when a domain
|
|
784
|
+
is first set up — use it on a new ``from_domain``.
|
|
770
785
|
"""
|
|
771
786
|
return self.call_tool(
|
|
772
787
|
"/v1/tools/email/send",
|
|
773
|
-
_build_email_payload(to, subject, body, from_domain, from_mailbox, from_name, kwargs, reply_to_email_id=reply_to_email_id),
|
|
788
|
+
_build_email_payload(to, subject, body, from_domain, from_mailbox, from_name, kwargs, reply_to_email_id=reply_to_email_id, mailbox_mode=mailbox_mode),
|
|
774
789
|
)
|
|
775
790
|
|
|
776
791
|
async def aemail(
|
|
@@ -783,6 +798,7 @@ class OneShotClient:
|
|
|
783
798
|
from_mailbox: Optional[str] = None,
|
|
784
799
|
from_name: Optional[str] = None,
|
|
785
800
|
reply_to_email_id: Optional[str] = None,
|
|
801
|
+
mailbox_mode: Optional[str] = None,
|
|
786
802
|
**kwargs: Any,
|
|
787
803
|
) -> Any:
|
|
788
804
|
"""Send an email. Async.
|
|
@@ -790,10 +806,13 @@ class OneShotClient:
|
|
|
790
806
|
Set ``reply_to_email_id`` (an inbound email id from ``ainbox()``) to
|
|
791
807
|
reply within a thread — ``to``/``subject`` are then derived from the
|
|
792
808
|
inbound message unless given.
|
|
809
|
+
|
|
810
|
+
``mailbox_mode`` ('relay' | 'mailbox', default 'relay') selects how a
|
|
811
|
+
domain sends — see ``email()`` for details.
|
|
793
812
|
"""
|
|
794
813
|
return await self.acall_tool(
|
|
795
814
|
"/v1/tools/email/send",
|
|
796
|
-
_build_email_payload(to, subject, body, from_domain, from_mailbox, from_name, kwargs, reply_to_email_id=reply_to_email_id),
|
|
815
|
+
_build_email_payload(to, subject, body, from_domain, from_mailbox, from_name, kwargs, reply_to_email_id=reply_to_email_id, mailbox_mode=mailbox_mode),
|
|
797
816
|
)
|
|
798
817
|
|
|
799
818
|
def voice(self, objective: str, target_number: str, **kwargs: Any) -> Any:
|
|
@@ -968,21 +987,27 @@ class OneShotClient:
|
|
|
968
987
|
value_tag: Optional[dict[str, Any]] = None,
|
|
969
988
|
*,
|
|
970
989
|
request_id: Optional[str] = None,
|
|
990
|
+
goal_id: Optional[str] = None,
|
|
971
991
|
) -> dict:
|
|
972
|
-
"""Tag
|
|
992
|
+
"""Tag business value for RoCS computation. Blocking.
|
|
973
993
|
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
994
|
+
Address the value three ways: by ``receipt_id`` (``rcpt_…``); by the
|
|
995
|
+
``request_id`` returned from the originating tool call (the API resolves
|
|
996
|
+
it via the receipt's ``job_id``); or by ``goal_id`` — a correlation key
|
|
997
|
+
that attributes a whole *cadence's* outcome (a closed deal across the
|
|
998
|
+
intro email + follow-ups + find/verify/enrich calls sharing
|
|
999
|
+
``decisionContext.goalId``) in one call. Read goal-level rollups back via
|
|
1000
|
+
``rocs_by_goal``.
|
|
978
1001
|
|
|
979
1002
|
``value_tag`` shape: ``{"type": ..., "amount": ..., "label": ...}``.
|
|
980
1003
|
``type`` must be one of ``revenue``, ``lead``, ``conversion``,
|
|
981
|
-
``savings``, ``engagement``.
|
|
982
|
-
|
|
1004
|
+
``savings``, ``engagement``. Stored as ``pending`` until a judge service
|
|
1005
|
+
confirms them against inbound signals.
|
|
983
1006
|
"""
|
|
984
1007
|
return asyncio.get_event_loop().run_until_complete(
|
|
985
|
-
self.atag_receipt_value(
|
|
1008
|
+
self.atag_receipt_value(
|
|
1009
|
+
receipt_id, value_tag, request_id=request_id, goal_id=goal_id
|
|
1010
|
+
)
|
|
986
1011
|
)
|
|
987
1012
|
|
|
988
1013
|
async def atag_receipt_value(
|
|
@@ -991,15 +1016,24 @@ class OneShotClient:
|
|
|
991
1016
|
value_tag: Optional[dict[str, Any]] = None,
|
|
992
1017
|
*,
|
|
993
1018
|
request_id: Optional[str] = None,
|
|
1019
|
+
goal_id: Optional[str] = None,
|
|
994
1020
|
) -> dict:
|
|
995
|
-
"""Tag
|
|
1021
|
+
"""Tag business value for RoCS computation. Async."""
|
|
1022
|
+
if not isinstance(value_tag, dict) or not value_tag.get("type"):
|
|
1023
|
+
raise ValidationError("value_tag.type is required", "value_tag.type")
|
|
1024
|
+
|
|
1025
|
+
# Cadence-level: route to the outcome ledger by correlation key.
|
|
1026
|
+
if goal_id and not receipt_id and not request_id:
|
|
1027
|
+
return await self.acall_free_post(
|
|
1028
|
+
"/v1/analytics/outcomes",
|
|
1029
|
+
{"goal_id": goal_id, **value_tag},
|
|
1030
|
+
)
|
|
1031
|
+
|
|
996
1032
|
target = receipt_id or request_id
|
|
997
1033
|
if not target:
|
|
998
1034
|
raise ValidationError(
|
|
999
|
-
"receipt_id or
|
|
1035
|
+
"receipt_id, request_id, or goal_id is required", "receipt_id"
|
|
1000
1036
|
)
|
|
1001
|
-
if not isinstance(value_tag, dict) or not value_tag.get("type"):
|
|
1002
|
-
raise ValidationError("value_tag.type is required", "value_tag.type")
|
|
1003
1037
|
return await self.acall_free_patch(
|
|
1004
1038
|
f"/v1/analytics/receipts/{target}/value",
|
|
1005
1039
|
value_tag,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "oneshot-python"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.16.0"
|
|
4
4
|
description = "Core Python SDK for the OneShot API — HTTP client with x402 payment handling"
|
|
5
5
|
readme = {text = "Core Python SDK for the OneShot API", content-type = "text/plain"}
|
|
6
6
|
license = "MIT"
|
|
@@ -46,6 +46,18 @@ def test_payload_passes_extra_kwargs():
|
|
|
46
46
|
assert p["wait"] is True
|
|
47
47
|
|
|
48
48
|
|
|
49
|
+
def test_payload_forwards_mailbox_mode():
|
|
50
|
+
# mailbox_mode is an explicit keyword-only param, forwarded into the send body so
|
|
51
|
+
# the API's per-domain provider override + fee logic sees it.
|
|
52
|
+
p = _build_email_payload("r@x.com", "S", "B", "acme.com", None, None, {}, mailbox_mode="mailbox")
|
|
53
|
+
assert p["mailbox_mode"] == "mailbox"
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def test_payload_omits_mailbox_mode_when_absent():
|
|
57
|
+
p = _build_email_payload("r@x.com", "S", "B", "acme.com", None, None, {})
|
|
58
|
+
assert "mailbox_mode" not in p
|
|
59
|
+
|
|
60
|
+
|
|
49
61
|
# ── reply threading (reply_to_email_id) ───────────────────────────────────
|
|
50
62
|
|
|
51
63
|
def test_reply_payload_omits_to_and_subject_when_derivable():
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|