Metadata-Version: 2.4
Name: onceonly-sdk
Version: 2.0.2
Summary: Python SDK for OnceOnly idempotency API
Author-email: OnceOnly <support@onceonly.tech>
License: MIT
Project-URL: Homepage, https://onceonly.tech/
Project-URL: Documentation, https://onceonly.tech/docs/
Project-URL: Repository, https://github.com/mykolademyanov/onceonly-python
Keywords: idempotency,automation,zapier,make,ai-agents
Classifier: Programming Language :: Python :: 3
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: httpx>=0.25
Provides-Extra: test
Requires-Dist: pytest>=7.0; extra == "test"
Requires-Dist: pytest-asyncio>=0.23; extra == "test"
Requires-Dist: anyio>=4.0; extra == "test"
Provides-Extra: langchain
Requires-Dist: langchain-core>=0.1.0; extra == "langchain"
Dynamic: license-file

# OnceOnly Python SDK

**The Idempotency Layer for AI Agents, Webhooks, and Distributed Systems.**

OnceOnly is a high-performance Python SDK that ensures **exactly-once execution**.
It prevents duplicate actions (payments, emails, tool calls) in unstable environments like
AI agents, webhooks, retries, or background workers.


Website: https://onceonly.tech/ai/  
Documentation: https://onceonly.tech/docs/

[![PyPI version](https://img.shields.io/pypi/v/onceonly-sdk.svg)](https://pypi.org/project/onceonly-sdk/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
---

## Why duplicates happen

Duplicate actions are common in modern systems because:
- AI agents retry or re-plan tool calls
- Webhooks are delivered more than once
- Workers crash after side-effects
- Distributed systems replay events

Without an idempotency layer, the same action may run multiple times.

OnceOnly is designed to guard side-effects in non-deterministic AI agent loops,
where the same tool call may be executed multiple times.

---

## Features

- Sync + Async client (httpx-based)
- Fail-open mode for production safety
- Stable idempotency keys (supports Pydantic & dataclasses)
- Decorators for zero-boilerplate usage
- Native AI API (long-running jobs, local side-effects)
- Optional AI / LangChain integrations

---

## FAQ

**Does this prevent duplicate payments or emails?**  
Yes. OnceOnly guarantees exactly-once execution for side-effects.

**Is this a retry library?**  
No. Retries still happen — OnceOnly ensures the action itself runs only once.


---

## Installation

```bash
pip install onceonly-sdk
```

### With LangChain support included:

```bash
pip install "onceonly-sdk[langchain]"
```

---

## Quick Start (Webhooks / Automations)

```python
from onceonly import OnceOnly

client = OnceOnly(
    api_key="once_live_...",
    fail_open=True  # default: continues if API is down
)

res = client.check_lock(key="order:123", ttl=300)

if res.duplicate:
    print("Duplicate blocked")
else:
    print("First execution")
```

Use `check_lock()` for:
- Webhooks
- Make / Zapier scenarios
- Cron jobs
- Distributed workers

---

## AI Jobs (Server-side)

Use the AI API for long-running or asynchronous jobs.

```python
result = client.ai.run_and_wait(
    key="ai:job:daily_summary:2026-01-09",
    metadata={"task": "daily_summary", "model": "gpt-4.1"},
    timeout=60,
)

print(result.status)
print(result.result)
```

- Charged **once per key**
- Polling is free
- Safe across retries and restarts

---

## AI Agents / Local Side-Effects

Use the AI Lease API when your code performs the side-effect locally
(payments, emails, webhooks) but still needs exactly-once guarantees.

```python
lease = client.ai.lease(key="ai:agent:charge:user_42:invoice_100", ttl=300)

if lease["status"] == "acquired":
    try:
        do_side_effect()
        client.ai.complete(key=KEY, lease_id=lease["lease_id"], result={"ok": True})
    except Exception:
        client.ai.fail(key=KEY, lease_id=lease["lease_id"], error_code="failed")
```

---

## LangChain Integration 🤖

```python
from onceonly.integrations.langchain import make_idempotent_tool

tool = make_idempotent_tool(
    original_tool,
    client=client,
    key_prefix="agent:tool"
)
```

Repeated tool calls with the same inputs will execute **exactly once**,
even across retries or agent restarts.

See `examples/ai/` for canonical patterns.

---

## Decorators

```python
from onceonly.decorators import idempotent

@idempotent(client, ttl=3600)
def process_order(order_id):
    ...
```

Idempotency keys are generated automatically and remain stable across restarts.

---

## Fail-Open Mode

Fail-open is enabled by default.

Network errors, timeouts, or server errors (5xx) will **not break your application**.
The SDK will allow execution to continue safely.

Fail-open never applies to:
- Auth errors (401 / 403)
- Plan limits (402)
- Validation errors (422)
- Rate limits (429)

---

## Support

Need help?  
Email: support@onceonly.tech  
Or open an issue on GitHub.

---

## License

MIT
