onceonly-sdk 2.0.0__tar.gz → 2.0.2__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.
- {onceonly_sdk-2.0.0/onceonly_sdk.egg-info → onceonly_sdk-2.0.2}/PKG-INFO +84 -8
- onceonly_sdk-2.0.2/README.md +191 -0
- onceonly_sdk-2.0.2/onceonly/ai.py +403 -0
- onceonly_sdk-2.0.2/onceonly/version.py +1 -0
- {onceonly_sdk-2.0.0 → onceonly_sdk-2.0.2/onceonly_sdk.egg-info}/PKG-INFO +84 -8
- {onceonly_sdk-2.0.0 → onceonly_sdk-2.0.2}/pyproject.toml +1 -1
- {onceonly_sdk-2.0.0 → onceonly_sdk-2.0.2}/tests/test_client_behavior.py +34 -0
- {onceonly_sdk-2.0.0 → onceonly_sdk-2.0.2}/tests/test_integrations.py +41 -0
- {onceonly_sdk-2.0.0 → onceonly_sdk-2.0.2}/tests/test_parsing.py +27 -0
- onceonly_sdk-2.0.0/README.md +0 -115
- onceonly_sdk-2.0.0/onceonly/ai.py +0 -195
- onceonly_sdk-2.0.0/onceonly/version.py +0 -1
- {onceonly_sdk-2.0.0 → onceonly_sdk-2.0.2}/LICENSE +0 -0
- {onceonly_sdk-2.0.0 → onceonly_sdk-2.0.2}/onceonly/__init__.py +0 -0
- {onceonly_sdk-2.0.0 → onceonly_sdk-2.0.2}/onceonly/_http.py +0 -0
- {onceonly_sdk-2.0.0 → onceonly_sdk-2.0.2}/onceonly/_util.py +0 -0
- {onceonly_sdk-2.0.0 → onceonly_sdk-2.0.2}/onceonly/ai_models.py +0 -0
- {onceonly_sdk-2.0.0 → onceonly_sdk-2.0.2}/onceonly/client.py +0 -0
- {onceonly_sdk-2.0.0 → onceonly_sdk-2.0.2}/onceonly/decorators.py +0 -0
- {onceonly_sdk-2.0.0 → onceonly_sdk-2.0.2}/onceonly/exceptions.py +0 -0
- {onceonly_sdk-2.0.0 → onceonly_sdk-2.0.2}/onceonly/integrations/__init__.py +0 -0
- {onceonly_sdk-2.0.0 → onceonly_sdk-2.0.2}/onceonly/integrations/langchain.py +0 -0
- {onceonly_sdk-2.0.0 → onceonly_sdk-2.0.2}/onceonly/models.py +0 -0
- {onceonly_sdk-2.0.0 → onceonly_sdk-2.0.2}/onceonly_sdk.egg-info/SOURCES.txt +0 -0
- {onceonly_sdk-2.0.0 → onceonly_sdk-2.0.2}/onceonly_sdk.egg-info/dependency_links.txt +0 -0
- {onceonly_sdk-2.0.0 → onceonly_sdk-2.0.2}/onceonly_sdk.egg-info/requires.txt +0 -0
- {onceonly_sdk-2.0.0 → onceonly_sdk-2.0.2}/onceonly_sdk.egg-info/top_level.txt +0 -0
- {onceonly_sdk-2.0.0 → onceonly_sdk-2.0.2}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: onceonly-sdk
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.2
|
|
4
4
|
Summary: Python SDK for OnceOnly idempotency API
|
|
5
5
|
Author-email: OnceOnly <support@onceonly.tech>
|
|
6
6
|
License: MIT
|
|
@@ -31,9 +31,27 @@ OnceOnly is a high-performance Python SDK that ensures **exactly-once execution*
|
|
|
31
31
|
It prevents duplicate actions (payments, emails, tool calls) in unstable environments like
|
|
32
32
|
AI agents, webhooks, retries, or background workers.
|
|
33
33
|
|
|
34
|
+
|
|
34
35
|
Website: https://onceonly.tech/ai/
|
|
35
36
|
Documentation: https://onceonly.tech/docs/
|
|
36
37
|
|
|
38
|
+
[](https://pypi.org/project/onceonly-sdk/)
|
|
39
|
+
[](https://opensource.org/licenses/MIT)
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## Why duplicates happen
|
|
43
|
+
|
|
44
|
+
Duplicate actions are common in modern systems because:
|
|
45
|
+
- AI agents retry or re-plan tool calls
|
|
46
|
+
- Webhooks are delivered more than once
|
|
47
|
+
- Workers crash after side-effects
|
|
48
|
+
- Distributed systems replay events
|
|
49
|
+
|
|
50
|
+
Without an idempotency layer, the same action may run multiple times.
|
|
51
|
+
|
|
52
|
+
OnceOnly is designed to guard side-effects in non-deterministic AI agent loops,
|
|
53
|
+
where the same tool call may be executed multiple times.
|
|
54
|
+
|
|
37
55
|
---
|
|
38
56
|
|
|
39
57
|
## Features
|
|
@@ -41,9 +59,21 @@ Documentation: https://onceonly.tech/docs/
|
|
|
41
59
|
- Sync + Async client (httpx-based)
|
|
42
60
|
- Fail-open mode for production safety
|
|
43
61
|
- Stable idempotency keys (supports Pydantic & dataclasses)
|
|
44
|
-
-
|
|
62
|
+
- Decorators for zero-boilerplate usage
|
|
63
|
+
- Native AI API (long-running jobs, local side-effects)
|
|
45
64
|
- Optional AI / LangChain integrations
|
|
46
65
|
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## FAQ
|
|
69
|
+
|
|
70
|
+
**Does this prevent duplicate payments or emails?**
|
|
71
|
+
Yes. OnceOnly guarantees exactly-once execution for side-effects.
|
|
72
|
+
|
|
73
|
+
**Is this a retry library?**
|
|
74
|
+
No. Retries still happen — OnceOnly ensures the action itself runs only once.
|
|
75
|
+
|
|
76
|
+
|
|
47
77
|
---
|
|
48
78
|
|
|
49
79
|
## Installation
|
|
@@ -60,7 +90,7 @@ pip install "onceonly-sdk[langchain]"
|
|
|
60
90
|
|
|
61
91
|
---
|
|
62
92
|
|
|
63
|
-
## Quick Start
|
|
93
|
+
## Quick Start (Webhooks / Automations)
|
|
64
94
|
|
|
65
95
|
```python
|
|
66
96
|
from onceonly import OnceOnly
|
|
@@ -78,11 +108,54 @@ else:
|
|
|
78
108
|
print("First execution")
|
|
79
109
|
```
|
|
80
110
|
|
|
111
|
+
Use `check_lock()` for:
|
|
112
|
+
- Webhooks
|
|
113
|
+
- Make / Zapier scenarios
|
|
114
|
+
- Cron jobs
|
|
115
|
+
- Distributed workers
|
|
116
|
+
|
|
81
117
|
---
|
|
82
118
|
|
|
83
|
-
## AI
|
|
119
|
+
## AI Jobs (Server-side)
|
|
84
120
|
|
|
85
|
-
|
|
121
|
+
Use the AI API for long-running or asynchronous jobs.
|
|
122
|
+
|
|
123
|
+
```python
|
|
124
|
+
result = client.ai.run_and_wait(
|
|
125
|
+
key="ai:job:daily_summary:2026-01-09",
|
|
126
|
+
metadata={"task": "daily_summary", "model": "gpt-4.1"},
|
|
127
|
+
timeout=60,
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
print(result.status)
|
|
131
|
+
print(result.result)
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
- Charged **once per key**
|
|
135
|
+
- Polling is free
|
|
136
|
+
- Safe across retries and restarts
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## AI Agents / Local Side-Effects
|
|
141
|
+
|
|
142
|
+
Use the AI Lease API when your code performs the side-effect locally
|
|
143
|
+
(payments, emails, webhooks) but still needs exactly-once guarantees.
|
|
144
|
+
|
|
145
|
+
```python
|
|
146
|
+
lease = client.ai.lease(key="ai:agent:charge:user_42:invoice_100", ttl=300)
|
|
147
|
+
|
|
148
|
+
if lease["status"] == "acquired":
|
|
149
|
+
try:
|
|
150
|
+
do_side_effect()
|
|
151
|
+
client.ai.complete(key=KEY, lease_id=lease["lease_id"], result={"ok": True})
|
|
152
|
+
except Exception:
|
|
153
|
+
client.ai.fail(key=KEY, lease_id=lease["lease_id"], error_code="failed")
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
## LangChain Integration 🤖
|
|
86
159
|
|
|
87
160
|
```python
|
|
88
161
|
from onceonly.integrations.langchain import make_idempotent_tool
|
|
@@ -94,11 +167,14 @@ tool = make_idempotent_tool(
|
|
|
94
167
|
)
|
|
95
168
|
```
|
|
96
169
|
|
|
97
|
-
Repeated tool calls with the same inputs will execute **exactly once**,
|
|
170
|
+
Repeated tool calls with the same inputs will execute **exactly once**,
|
|
171
|
+
even across retries or agent restarts.
|
|
172
|
+
|
|
173
|
+
See `examples/ai/` for canonical patterns.
|
|
98
174
|
|
|
99
175
|
---
|
|
100
176
|
|
|
101
|
-
##
|
|
177
|
+
## Decorators
|
|
102
178
|
|
|
103
179
|
```python
|
|
104
180
|
from onceonly.decorators import idempotent
|
|
@@ -108,7 +184,7 @@ def process_order(order_id):
|
|
|
108
184
|
...
|
|
109
185
|
```
|
|
110
186
|
|
|
111
|
-
Idempotency keys are generated automatically and
|
|
187
|
+
Idempotency keys are generated automatically and remain stable across restarts.
|
|
112
188
|
|
|
113
189
|
---
|
|
114
190
|
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
# OnceOnly Python SDK
|
|
2
|
+
|
|
3
|
+
**The Idempotency Layer for AI Agents, Webhooks, and Distributed Systems.**
|
|
4
|
+
|
|
5
|
+
OnceOnly is a high-performance Python SDK that ensures **exactly-once execution**.
|
|
6
|
+
It prevents duplicate actions (payments, emails, tool calls) in unstable environments like
|
|
7
|
+
AI agents, webhooks, retries, or background workers.
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
Website: https://onceonly.tech/ai/
|
|
11
|
+
Documentation: https://onceonly.tech/docs/
|
|
12
|
+
|
|
13
|
+
[](https://pypi.org/project/onceonly-sdk/)
|
|
14
|
+
[](https://opensource.org/licenses/MIT)
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Why duplicates happen
|
|
18
|
+
|
|
19
|
+
Duplicate actions are common in modern systems because:
|
|
20
|
+
- AI agents retry or re-plan tool calls
|
|
21
|
+
- Webhooks are delivered more than once
|
|
22
|
+
- Workers crash after side-effects
|
|
23
|
+
- Distributed systems replay events
|
|
24
|
+
|
|
25
|
+
Without an idempotency layer, the same action may run multiple times.
|
|
26
|
+
|
|
27
|
+
OnceOnly is designed to guard side-effects in non-deterministic AI agent loops,
|
|
28
|
+
where the same tool call may be executed multiple times.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Features
|
|
33
|
+
|
|
34
|
+
- Sync + Async client (httpx-based)
|
|
35
|
+
- Fail-open mode for production safety
|
|
36
|
+
- Stable idempotency keys (supports Pydantic & dataclasses)
|
|
37
|
+
- Decorators for zero-boilerplate usage
|
|
38
|
+
- Native AI API (long-running jobs, local side-effects)
|
|
39
|
+
- Optional AI / LangChain integrations
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## FAQ
|
|
44
|
+
|
|
45
|
+
**Does this prevent duplicate payments or emails?**
|
|
46
|
+
Yes. OnceOnly guarantees exactly-once execution for side-effects.
|
|
47
|
+
|
|
48
|
+
**Is this a retry library?**
|
|
49
|
+
No. Retries still happen — OnceOnly ensures the action itself runs only once.
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## Installation
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
pip install onceonly-sdk
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### With LangChain support included:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
pip install "onceonly-sdk[langchain]"
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## Quick Start (Webhooks / Automations)
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
from onceonly import OnceOnly
|
|
72
|
+
|
|
73
|
+
client = OnceOnly(
|
|
74
|
+
api_key="once_live_...",
|
|
75
|
+
fail_open=True # default: continues if API is down
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
res = client.check_lock(key="order:123", ttl=300)
|
|
79
|
+
|
|
80
|
+
if res.duplicate:
|
|
81
|
+
print("Duplicate blocked")
|
|
82
|
+
else:
|
|
83
|
+
print("First execution")
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Use `check_lock()` for:
|
|
87
|
+
- Webhooks
|
|
88
|
+
- Make / Zapier scenarios
|
|
89
|
+
- Cron jobs
|
|
90
|
+
- Distributed workers
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## AI Jobs (Server-side)
|
|
95
|
+
|
|
96
|
+
Use the AI API for long-running or asynchronous jobs.
|
|
97
|
+
|
|
98
|
+
```python
|
|
99
|
+
result = client.ai.run_and_wait(
|
|
100
|
+
key="ai:job:daily_summary:2026-01-09",
|
|
101
|
+
metadata={"task": "daily_summary", "model": "gpt-4.1"},
|
|
102
|
+
timeout=60,
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
print(result.status)
|
|
106
|
+
print(result.result)
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
- Charged **once per key**
|
|
110
|
+
- Polling is free
|
|
111
|
+
- Safe across retries and restarts
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## AI Agents / Local Side-Effects
|
|
116
|
+
|
|
117
|
+
Use the AI Lease API when your code performs the side-effect locally
|
|
118
|
+
(payments, emails, webhooks) but still needs exactly-once guarantees.
|
|
119
|
+
|
|
120
|
+
```python
|
|
121
|
+
lease = client.ai.lease(key="ai:agent:charge:user_42:invoice_100", ttl=300)
|
|
122
|
+
|
|
123
|
+
if lease["status"] == "acquired":
|
|
124
|
+
try:
|
|
125
|
+
do_side_effect()
|
|
126
|
+
client.ai.complete(key=KEY, lease_id=lease["lease_id"], result={"ok": True})
|
|
127
|
+
except Exception:
|
|
128
|
+
client.ai.fail(key=KEY, lease_id=lease["lease_id"], error_code="failed")
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## LangChain Integration 🤖
|
|
134
|
+
|
|
135
|
+
```python
|
|
136
|
+
from onceonly.integrations.langchain import make_idempotent_tool
|
|
137
|
+
|
|
138
|
+
tool = make_idempotent_tool(
|
|
139
|
+
original_tool,
|
|
140
|
+
client=client,
|
|
141
|
+
key_prefix="agent:tool"
|
|
142
|
+
)
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Repeated tool calls with the same inputs will execute **exactly once**,
|
|
146
|
+
even across retries or agent restarts.
|
|
147
|
+
|
|
148
|
+
See `examples/ai/` for canonical patterns.
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## Decorators
|
|
153
|
+
|
|
154
|
+
```python
|
|
155
|
+
from onceonly.decorators import idempotent
|
|
156
|
+
|
|
157
|
+
@idempotent(client, ttl=3600)
|
|
158
|
+
def process_order(order_id):
|
|
159
|
+
...
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Idempotency keys are generated automatically and remain stable across restarts.
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## Fail-Open Mode
|
|
167
|
+
|
|
168
|
+
Fail-open is enabled by default.
|
|
169
|
+
|
|
170
|
+
Network errors, timeouts, or server errors (5xx) will **not break your application**.
|
|
171
|
+
The SDK will allow execution to continue safely.
|
|
172
|
+
|
|
173
|
+
Fail-open never applies to:
|
|
174
|
+
- Auth errors (401 / 403)
|
|
175
|
+
- Plan limits (402)
|
|
176
|
+
- Validation errors (422)
|
|
177
|
+
- Rate limits (429)
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
## Support
|
|
182
|
+
|
|
183
|
+
Need help?
|
|
184
|
+
Email: support@onceonly.tech
|
|
185
|
+
Or open an issue on GitHub.
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
## License
|
|
190
|
+
|
|
191
|
+
MIT
|