oneshot-python 0.12.1__tar.gz → 0.15.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.12.1 → oneshot_python-0.15.0}/PKG-INFO +1 -1
- {oneshot_python-0.12.1 → oneshot_python-0.15.0}/oneshot/client.py +56 -13
- {oneshot_python-0.12.1 → oneshot_python-0.15.0}/pyproject.toml +1 -1
- {oneshot_python-0.12.1 → oneshot_python-0.15.0}/tests/test_tag_receipt_value.py +36 -2
- {oneshot_python-0.12.1 → oneshot_python-0.15.0}/uv.lock +1 -1
- {oneshot_python-0.12.1 → oneshot_python-0.15.0}/.gitignore +0 -0
- {oneshot_python-0.12.1 → oneshot_python-0.15.0}/README.md +0 -0
- {oneshot_python-0.12.1 → oneshot_python-0.15.0}/oneshot/__init__.py +0 -0
- {oneshot_python-0.12.1 → oneshot_python-0.15.0}/oneshot/_errors.py +0 -0
- {oneshot_python-0.12.1 → oneshot_python-0.15.0}/oneshot/_types.py +0 -0
- {oneshot_python-0.12.1 → oneshot_python-0.15.0}/oneshot/x402.py +0 -0
- {oneshot_python-0.12.1 → oneshot_python-0.15.0}/tests/__init__.py +0 -0
- {oneshot_python-0.12.1 → oneshot_python-0.15.0}/tests/test_balance.py +0 -0
- {oneshot_python-0.12.1 → oneshot_python-0.15.0}/tests/test_compute.py +0 -0
- {oneshot_python-0.12.1 → oneshot_python-0.15.0}/tests/test_email_payload.py +0 -0
- {oneshot_python-0.12.1 → oneshot_python-0.15.0}/tests/test_emergency_error.py +0 -0
- {oneshot_python-0.12.1 → oneshot_python-0.15.0}/tests/test_max_cost_header.py +0 -0
- {oneshot_python-0.12.1 → oneshot_python-0.15.0}/tests/test_phones_pending.py +0 -0
- {oneshot_python-0.12.1 → oneshot_python-0.15.0}/tests/test_request_id.py +0 -0
- {oneshot_python-0.12.1 → oneshot_python-0.15.0}/tests/test_x402.py +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.15.0"
|
|
42
42
|
|
|
43
43
|
# ---------------------------------------------------------------------------
|
|
44
44
|
# Environment configuration
|
|
@@ -828,6 +828,22 @@ class OneShotClient:
|
|
|
828
828
|
"""Enrich a person's profile from LinkedIn URL, email, or name. Async."""
|
|
829
829
|
return await self.acall_tool("/v1/tools/enrich/profile", kwargs)
|
|
830
830
|
|
|
831
|
+
def company_search(self, **kwargs: Any) -> Any:
|
|
832
|
+
"""Search for companies by name, domain, industry, location, size, etc. Blocking."""
|
|
833
|
+
return self.call_tool("/v1/tools/research/company", kwargs)
|
|
834
|
+
|
|
835
|
+
async def acompany_search(self, **kwargs: Any) -> Any:
|
|
836
|
+
"""Search for companies by name, domain, industry, location, size, etc. Async."""
|
|
837
|
+
return await self.acall_tool("/v1/tools/research/company", kwargs)
|
|
838
|
+
|
|
839
|
+
def enrich_company(self, **kwargs: Any) -> Any:
|
|
840
|
+
"""Enrich a company from domain, name, LinkedIn URL, or ticker. Blocking."""
|
|
841
|
+
return self.call_tool("/v1/tools/enrich/company", kwargs)
|
|
842
|
+
|
|
843
|
+
async def aenrich_company(self, **kwargs: Any) -> Any:
|
|
844
|
+
"""Enrich a company from domain, name, LinkedIn URL, or ticker. Async."""
|
|
845
|
+
return await self.acall_tool("/v1/tools/enrich/company", kwargs)
|
|
846
|
+
|
|
831
847
|
def find_email(self, company_domain: str, *, full_name: Optional[str] = None, first_name: Optional[str] = None, last_name: Optional[str] = None, **kwargs: Any) -> Any:
|
|
832
848
|
"""Find a person's email address. Blocking."""
|
|
833
849
|
payload: dict[str, Any] = {"company_domain": company_domain, **kwargs}
|
|
@@ -948,32 +964,59 @@ class OneShotClient:
|
|
|
948
964
|
|
|
949
965
|
def tag_receipt_value(
|
|
950
966
|
self,
|
|
951
|
-
receipt_id: str,
|
|
952
|
-
value_tag: dict[str, Any],
|
|
967
|
+
receipt_id: Optional[str] = None,
|
|
968
|
+
value_tag: Optional[dict[str, Any]] = None,
|
|
969
|
+
*,
|
|
970
|
+
request_id: Optional[str] = None,
|
|
971
|
+
goal_id: Optional[str] = None,
|
|
953
972
|
) -> dict:
|
|
954
|
-
"""Tag
|
|
973
|
+
"""Tag business value for RoCS computation. Blocking.
|
|
974
|
+
|
|
975
|
+
Address the value three ways: by ``receipt_id`` (``rcpt_…``); by the
|
|
976
|
+
``request_id`` returned from the originating tool call (the API resolves
|
|
977
|
+
it via the receipt's ``job_id``); or by ``goal_id`` — a correlation key
|
|
978
|
+
that attributes a whole *cadence's* outcome (a closed deal across the
|
|
979
|
+
intro email + follow-ups + find/verify/enrich calls sharing
|
|
980
|
+
``decisionContext.goalId``) in one call. Read goal-level rollups back via
|
|
981
|
+
``rocs_by_goal``.
|
|
955
982
|
|
|
956
983
|
``value_tag`` shape: ``{"type": ..., "amount": ..., "label": ...}``.
|
|
957
984
|
``type`` must be one of ``revenue``, ``lead``, ``conversion``,
|
|
958
|
-
``savings``, ``engagement``.
|
|
959
|
-
|
|
985
|
+
``savings``, ``engagement``. Stored as ``pending`` until a judge service
|
|
986
|
+
confirms them against inbound signals.
|
|
960
987
|
"""
|
|
961
988
|
return asyncio.get_event_loop().run_until_complete(
|
|
962
|
-
self.atag_receipt_value(
|
|
989
|
+
self.atag_receipt_value(
|
|
990
|
+
receipt_id, value_tag, request_id=request_id, goal_id=goal_id
|
|
991
|
+
)
|
|
963
992
|
)
|
|
964
993
|
|
|
965
994
|
async def atag_receipt_value(
|
|
966
995
|
self,
|
|
967
|
-
receipt_id: str,
|
|
968
|
-
value_tag: dict[str, Any],
|
|
996
|
+
receipt_id: Optional[str] = None,
|
|
997
|
+
value_tag: Optional[dict[str, Any]] = None,
|
|
998
|
+
*,
|
|
999
|
+
request_id: Optional[str] = None,
|
|
1000
|
+
goal_id: Optional[str] = None,
|
|
969
1001
|
) -> dict:
|
|
970
|
-
"""Tag
|
|
971
|
-
if not receipt_id:
|
|
972
|
-
raise ValidationError("receipt_id is required", "receipt_id")
|
|
1002
|
+
"""Tag business value for RoCS computation. Async."""
|
|
973
1003
|
if not isinstance(value_tag, dict) or not value_tag.get("type"):
|
|
974
1004
|
raise ValidationError("value_tag.type is required", "value_tag.type")
|
|
1005
|
+
|
|
1006
|
+
# Cadence-level: route to the outcome ledger by correlation key.
|
|
1007
|
+
if goal_id and not receipt_id and not request_id:
|
|
1008
|
+
return await self.acall_free_post(
|
|
1009
|
+
"/v1/analytics/outcomes",
|
|
1010
|
+
{"goal_id": goal_id, **value_tag},
|
|
1011
|
+
)
|
|
1012
|
+
|
|
1013
|
+
target = receipt_id or request_id
|
|
1014
|
+
if not target:
|
|
1015
|
+
raise ValidationError(
|
|
1016
|
+
"receipt_id, request_id, or goal_id is required", "receipt_id"
|
|
1017
|
+
)
|
|
975
1018
|
return await self.acall_free_patch(
|
|
976
|
-
f"/v1/analytics/receipts/{
|
|
1019
|
+
f"/v1/analytics/receipts/{target}/value",
|
|
977
1020
|
value_tag,
|
|
978
1021
|
)
|
|
979
1022
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "oneshot-python"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.15.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"
|
|
@@ -70,18 +70,52 @@ async def test_accepts_minimal_valid_tag_with_just_type():
|
|
|
70
70
|
mock.assert_awaited_once()
|
|
71
71
|
|
|
72
72
|
|
|
73
|
+
# ── Resolving by request_id (== job_id) instead of receipt_id ────────────
|
|
74
|
+
|
|
75
|
+
@pytest.mark.asyncio
|
|
76
|
+
async def test_tags_by_request_id():
|
|
77
|
+
# A request_id from the original tool call resolves server-side via job_id.
|
|
78
|
+
c, mock = _client_with_mocked_patch()
|
|
79
|
+
tag = {"type": "lead", "amount": 1}
|
|
80
|
+
await c.atag_receipt_value(value_tag=tag, request_id="0cbb87ee-1f2a-4c3d-9e8b-7a6f5d4c3b2a")
|
|
81
|
+
mock.assert_awaited_once_with(
|
|
82
|
+
"/v1/analytics/receipts/0cbb87ee-1f2a-4c3d-9e8b-7a6f5d4c3b2a/value", tag
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
@pytest.mark.asyncio
|
|
87
|
+
async def test_receipt_id_preferred_over_request_id():
|
|
88
|
+
c, mock = _client_with_mocked_patch()
|
|
89
|
+
tag = {"type": "revenue", "amount": 2}
|
|
90
|
+
await c.atag_receipt_value("rcpt_01HX", tag, request_id="some-uuid")
|
|
91
|
+
mock.assert_awaited_once_with("/v1/analytics/receipts/rcpt_01HX/value", tag)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
@pytest.mark.asyncio
|
|
95
|
+
async def test_raises_when_neither_id_provided():
|
|
96
|
+
c, _ = _client_with_mocked_patch()
|
|
97
|
+
with pytest.raises(ValidationError) as excinfo:
|
|
98
|
+
await c.atag_receipt_value(value_tag={"type": "revenue", "amount": 1})
|
|
99
|
+
assert excinfo.value.field == "receipt_id"
|
|
100
|
+
|
|
101
|
+
|
|
73
102
|
def test_sync_wrapper_delegates_to_async(monkeypatch):
|
|
74
103
|
# The blocking ``tag_receipt_value`` should call through to the async
|
|
75
104
|
# version via the event loop, matching every other tool method.
|
|
76
105
|
c = OneShotClient(TEST_PRIVATE_KEY)
|
|
77
106
|
called: dict = {}
|
|
78
107
|
|
|
79
|
-
async def fake_async(receipt_id, value_tag):
|
|
108
|
+
async def fake_async(receipt_id=None, value_tag=None, *, request_id=None):
|
|
80
109
|
called["receipt_id"] = receipt_id
|
|
81
110
|
called["value_tag"] = value_tag
|
|
111
|
+
called["request_id"] = request_id
|
|
82
112
|
return {"ok": True}
|
|
83
113
|
|
|
84
114
|
c.atag_receipt_value = fake_async # type: ignore[method-assign]
|
|
85
115
|
result = c.tag_receipt_value("rcpt_01HX", {"type": "savings", "amount": 12})
|
|
86
116
|
assert result == {"ok": True}
|
|
87
|
-
assert called == {
|
|
117
|
+
assert called == {
|
|
118
|
+
"receipt_id": "rcpt_01HX",
|
|
119
|
+
"value_tag": {"type": "savings", "amount": 12},
|
|
120
|
+
"request_id": None,
|
|
121
|
+
}
|
|
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
|