cheqpoint 0.1.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.
- cheqpoint-0.1.0/PKG-INFO +237 -0
- cheqpoint-0.1.0/README.md +208 -0
- cheqpoint-0.1.0/cheqpoint/__init__.py +25 -0
- cheqpoint-0.1.0/cheqpoint/client.py +437 -0
- cheqpoint-0.1.0/cheqpoint/crewai_tool.py +86 -0
- cheqpoint-0.1.0/cheqpoint/langchain_tool.py +109 -0
- cheqpoint-0.1.0/cheqpoint.egg-info/PKG-INFO +237 -0
- cheqpoint-0.1.0/cheqpoint.egg-info/SOURCES.txt +11 -0
- cheqpoint-0.1.0/cheqpoint.egg-info/dependency_links.txt +1 -0
- cheqpoint-0.1.0/cheqpoint.egg-info/requires.txt +1 -0
- cheqpoint-0.1.0/cheqpoint.egg-info/top_level.txt +1 -0
- cheqpoint-0.1.0/setup.cfg +4 -0
- cheqpoint-0.1.0/setup.py +28 -0
cheqpoint-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cheqpoint
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Human-in-the-loop approval SDK for AI agents
|
|
5
|
+
Home-page: https://cheqpoint.io
|
|
6
|
+
Author: Cheqpoint
|
|
7
|
+
Keywords: cheqpoint,human-in-the-loop,ai,agents,approval
|
|
8
|
+
Classifier: Development Status :: 3 - Alpha
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
17
|
+
Requires-Python: >=3.9
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
Requires-Dist: requests>=2.28.0
|
|
20
|
+
Dynamic: author
|
|
21
|
+
Dynamic: classifier
|
|
22
|
+
Dynamic: description
|
|
23
|
+
Dynamic: description-content-type
|
|
24
|
+
Dynamic: home-page
|
|
25
|
+
Dynamic: keywords
|
|
26
|
+
Dynamic: requires-dist
|
|
27
|
+
Dynamic: requires-python
|
|
28
|
+
Dynamic: summary
|
|
29
|
+
|
|
30
|
+
# cheqpoint
|
|
31
|
+
|
|
32
|
+
Official Python SDK for [Cheqpoint](https://cheqpoint.co) — human-in-the-loop approval queues for AI agents.
|
|
33
|
+
|
|
34
|
+
## Installation
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
pip install cheqpoint
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Quick start
|
|
41
|
+
|
|
42
|
+
```python
|
|
43
|
+
import os
|
|
44
|
+
from cheqpoint import CheqpointClient
|
|
45
|
+
|
|
46
|
+
client = CheqpointClient(api_key=os.environ["CHEQPOINT_API_KEY"])
|
|
47
|
+
|
|
48
|
+
# Your agent calls this instead of executing directly.
|
|
49
|
+
# checkpoint() submits the request and waits for a human decision.
|
|
50
|
+
result = client.checkpoint(
|
|
51
|
+
type="refund",
|
|
52
|
+
risk_level="high",
|
|
53
|
+
summary="Refund $149 to sarah@example.com",
|
|
54
|
+
details={"userId": "usr_123", "amount": 149, "currency": "USD"},
|
|
55
|
+
justification="Double-charged on invoice #1821",
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
# effective_details returns modifiedDetails if set, else original details
|
|
59
|
+
stripe.refunds.create(**result.effective_details)
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Constructor
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
CheqpointClient(
|
|
66
|
+
api_key: str, # Required. Your workspace API key.
|
|
67
|
+
base_url: str = "https://app.cheqpoint.co",
|
|
68
|
+
timeout: float = 300, # Default poll timeout in seconds
|
|
69
|
+
)
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Methods
|
|
73
|
+
|
|
74
|
+
### `checkpoint(...)` → `CheckpointResult`
|
|
75
|
+
|
|
76
|
+
Submit an action for review and **block** until a human decides.
|
|
77
|
+
|
|
78
|
+
| Parameter | Type | Default | Description |
|
|
79
|
+
|---|---|---|---|
|
|
80
|
+
| `type` | `str` | required | Short label for the action (e.g. `"refund"`, `"email"`) |
|
|
81
|
+
| `risk_level` | `"low" \| "medium" \| "high"` | required | Risk level of the action |
|
|
82
|
+
| `summary` | `str` | required | One-sentence description shown to reviewers |
|
|
83
|
+
| `details` | `dict` | required | Structured payload. Returned as-is (or modified) on approval |
|
|
84
|
+
| `justification` | `str \| None` | `None` | Agent's reasoning shown to reviewers |
|
|
85
|
+
| `webhook_url` | `str \| None` | `None` | URL for Cheqpoint to POST the decision to |
|
|
86
|
+
| `poll_interval` | `float` | `3` | Seconds between status polls |
|
|
87
|
+
| `timeout` | `float \| None` | client default | Max seconds to wait |
|
|
88
|
+
|
|
89
|
+
**Returns** `CheckpointResult` when approved.
|
|
90
|
+
**Raises** `RejectedError` if rejected.
|
|
91
|
+
**Raises** `TimeoutError` if no decision within timeout.
|
|
92
|
+
|
|
93
|
+
```python
|
|
94
|
+
@dataclass
|
|
95
|
+
class CheckpointResult:
|
|
96
|
+
id: str
|
|
97
|
+
status: str # "APPROVED"
|
|
98
|
+
details: dict # original payload
|
|
99
|
+
modified_details: dict | None # reviewer edits, if any
|
|
100
|
+
response_notes: str | None # reviewer's note
|
|
101
|
+
|
|
102
|
+
def effective_details(self) -> dict:
|
|
103
|
+
"""Returns modified_details if set, else original details."""
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### `create_request(...)` → `dict`
|
|
107
|
+
|
|
108
|
+
Fire-and-forget. Submits the request and returns immediately with `{"id": "..."}`. Pair with a `webhook_url` or poll manually with `get_request`.
|
|
109
|
+
|
|
110
|
+
### `get_request(request_id)` → `RequestStatus`
|
|
111
|
+
|
|
112
|
+
Fetch the current status of a request.
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
@dataclass
|
|
116
|
+
class RequestStatus:
|
|
117
|
+
id: str
|
|
118
|
+
status: str # "PENDING" | "APPROVED" | "REJECTED"
|
|
119
|
+
type: str
|
|
120
|
+
risk_level: str
|
|
121
|
+
summary: str
|
|
122
|
+
details: dict
|
|
123
|
+
modified_details: dict | None
|
|
124
|
+
response_notes: str | None
|
|
125
|
+
webhook_delivered: bool
|
|
126
|
+
created_at: str
|
|
127
|
+
decided_at: str | None
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Error handling
|
|
131
|
+
|
|
132
|
+
```python
|
|
133
|
+
from cheqpoint import CheqpointClient, CheqpointError, RejectedError, TimeoutError
|
|
134
|
+
|
|
135
|
+
try:
|
|
136
|
+
result = client.checkpoint(...)
|
|
137
|
+
except RejectedError as e:
|
|
138
|
+
print(f"Rejected: {e.response_notes}")
|
|
139
|
+
except TimeoutError as e:
|
|
140
|
+
print(f"Timed out for request {e.request_id}")
|
|
141
|
+
except CheqpointError as e:
|
|
142
|
+
print(f"API error {e.status_code}: {e}")
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Examples
|
|
146
|
+
|
|
147
|
+
### With webhook (recommended for production)
|
|
148
|
+
|
|
149
|
+
```python
|
|
150
|
+
result = client.create_request(
|
|
151
|
+
type="db-write",
|
|
152
|
+
risk_level="medium",
|
|
153
|
+
summary="Delete user account usr_456",
|
|
154
|
+
details={"userId": "usr_456", "reason": "GDPR deletion request"},
|
|
155
|
+
webhook_url="https://yourapp.com/webhook/cheqpoint",
|
|
156
|
+
)
|
|
157
|
+
request_id = result["id"]
|
|
158
|
+
# Store request_id — your Flask/Django webhook handler receives the decision
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### Manual polling
|
|
162
|
+
|
|
163
|
+
```python
|
|
164
|
+
result = client.create_request(type="email", risk_level="low", ...)
|
|
165
|
+
request_id = result["id"]
|
|
166
|
+
|
|
167
|
+
# Check later
|
|
168
|
+
status = client.get_request(request_id)
|
|
169
|
+
if status.status == "APPROVED":
|
|
170
|
+
payload = status.modified_details or status.details
|
|
171
|
+
send_email(**payload)
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### LangChain agent tool
|
|
175
|
+
|
|
176
|
+
```python
|
|
177
|
+
from langchain_core.tools import tool
|
|
178
|
+
|
|
179
|
+
@tool
|
|
180
|
+
def issue_refund(user_id: str, amount: float, reason: str) -> str:
|
|
181
|
+
"""Issue a refund to a customer. Requires human approval."""
|
|
182
|
+
result = client.checkpoint(
|
|
183
|
+
type="refund",
|
|
184
|
+
risk_level="high",
|
|
185
|
+
summary=f"Refund ${amount} to user {user_id}",
|
|
186
|
+
details={"userId": user_id, "amount": amount, "reason": reason},
|
|
187
|
+
)
|
|
188
|
+
payload = result.effective_details
|
|
189
|
+
return f"Refund approved for ${payload['amount']}"
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### CrewAI tool
|
|
193
|
+
|
|
194
|
+
```python
|
|
195
|
+
from crewai_tools import BaseTool
|
|
196
|
+
|
|
197
|
+
class CheqpointApprovalTool(BaseTool):
|
|
198
|
+
name: str = "Request Human Approval"
|
|
199
|
+
description: str = "Submit a risky action for human approval before executing."
|
|
200
|
+
|
|
201
|
+
def _run(self, action_type: str, summary: str, details: dict) -> str:
|
|
202
|
+
result = client.checkpoint(
|
|
203
|
+
type=action_type,
|
|
204
|
+
risk_level="high",
|
|
205
|
+
summary=summary,
|
|
206
|
+
details=details,
|
|
207
|
+
)
|
|
208
|
+
return f"Approved. Effective details: {result.effective_details}"
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### Flask webhook handler
|
|
212
|
+
|
|
213
|
+
```python
|
|
214
|
+
from flask import Flask, request, jsonify
|
|
215
|
+
|
|
216
|
+
app = Flask(__name__)
|
|
217
|
+
|
|
218
|
+
@app.route("/webhook/cheqpoint", methods=["POST"])
|
|
219
|
+
def cheqpoint_webhook():
|
|
220
|
+
data = request.get_json()
|
|
221
|
+
status = data["status"]
|
|
222
|
+
payload = data.get("modifiedDetails") or data["details"]
|
|
223
|
+
|
|
224
|
+
if status == "APPROVED":
|
|
225
|
+
process_action(payload)
|
|
226
|
+
|
|
227
|
+
return jsonify({"ok": True}), 200
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
## Requirements
|
|
231
|
+
|
|
232
|
+
- Python 3.9+
|
|
233
|
+
- `requests` library
|
|
234
|
+
|
|
235
|
+
## License
|
|
236
|
+
|
|
237
|
+
MIT
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
# cheqpoint
|
|
2
|
+
|
|
3
|
+
Official Python SDK for [Cheqpoint](https://cheqpoint.co) — human-in-the-loop approval queues for AI agents.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install cheqpoint
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick start
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
import os
|
|
15
|
+
from cheqpoint import CheqpointClient
|
|
16
|
+
|
|
17
|
+
client = CheqpointClient(api_key=os.environ["CHEQPOINT_API_KEY"])
|
|
18
|
+
|
|
19
|
+
# Your agent calls this instead of executing directly.
|
|
20
|
+
# checkpoint() submits the request and waits for a human decision.
|
|
21
|
+
result = client.checkpoint(
|
|
22
|
+
type="refund",
|
|
23
|
+
risk_level="high",
|
|
24
|
+
summary="Refund $149 to sarah@example.com",
|
|
25
|
+
details={"userId": "usr_123", "amount": 149, "currency": "USD"},
|
|
26
|
+
justification="Double-charged on invoice #1821",
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
# effective_details returns modifiedDetails if set, else original details
|
|
30
|
+
stripe.refunds.create(**result.effective_details)
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Constructor
|
|
34
|
+
|
|
35
|
+
```python
|
|
36
|
+
CheqpointClient(
|
|
37
|
+
api_key: str, # Required. Your workspace API key.
|
|
38
|
+
base_url: str = "https://app.cheqpoint.co",
|
|
39
|
+
timeout: float = 300, # Default poll timeout in seconds
|
|
40
|
+
)
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Methods
|
|
44
|
+
|
|
45
|
+
### `checkpoint(...)` → `CheckpointResult`
|
|
46
|
+
|
|
47
|
+
Submit an action for review and **block** until a human decides.
|
|
48
|
+
|
|
49
|
+
| Parameter | Type | Default | Description |
|
|
50
|
+
|---|---|---|---|
|
|
51
|
+
| `type` | `str` | required | Short label for the action (e.g. `"refund"`, `"email"`) |
|
|
52
|
+
| `risk_level` | `"low" \| "medium" \| "high"` | required | Risk level of the action |
|
|
53
|
+
| `summary` | `str` | required | One-sentence description shown to reviewers |
|
|
54
|
+
| `details` | `dict` | required | Structured payload. Returned as-is (or modified) on approval |
|
|
55
|
+
| `justification` | `str \| None` | `None` | Agent's reasoning shown to reviewers |
|
|
56
|
+
| `webhook_url` | `str \| None` | `None` | URL for Cheqpoint to POST the decision to |
|
|
57
|
+
| `poll_interval` | `float` | `3` | Seconds between status polls |
|
|
58
|
+
| `timeout` | `float \| None` | client default | Max seconds to wait |
|
|
59
|
+
|
|
60
|
+
**Returns** `CheckpointResult` when approved.
|
|
61
|
+
**Raises** `RejectedError` if rejected.
|
|
62
|
+
**Raises** `TimeoutError` if no decision within timeout.
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
@dataclass
|
|
66
|
+
class CheckpointResult:
|
|
67
|
+
id: str
|
|
68
|
+
status: str # "APPROVED"
|
|
69
|
+
details: dict # original payload
|
|
70
|
+
modified_details: dict | None # reviewer edits, if any
|
|
71
|
+
response_notes: str | None # reviewer's note
|
|
72
|
+
|
|
73
|
+
def effective_details(self) -> dict:
|
|
74
|
+
"""Returns modified_details if set, else original details."""
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### `create_request(...)` → `dict`
|
|
78
|
+
|
|
79
|
+
Fire-and-forget. Submits the request and returns immediately with `{"id": "..."}`. Pair with a `webhook_url` or poll manually with `get_request`.
|
|
80
|
+
|
|
81
|
+
### `get_request(request_id)` → `RequestStatus`
|
|
82
|
+
|
|
83
|
+
Fetch the current status of a request.
|
|
84
|
+
|
|
85
|
+
```python
|
|
86
|
+
@dataclass
|
|
87
|
+
class RequestStatus:
|
|
88
|
+
id: str
|
|
89
|
+
status: str # "PENDING" | "APPROVED" | "REJECTED"
|
|
90
|
+
type: str
|
|
91
|
+
risk_level: str
|
|
92
|
+
summary: str
|
|
93
|
+
details: dict
|
|
94
|
+
modified_details: dict | None
|
|
95
|
+
response_notes: str | None
|
|
96
|
+
webhook_delivered: bool
|
|
97
|
+
created_at: str
|
|
98
|
+
decided_at: str | None
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Error handling
|
|
102
|
+
|
|
103
|
+
```python
|
|
104
|
+
from cheqpoint import CheqpointClient, CheqpointError, RejectedError, TimeoutError
|
|
105
|
+
|
|
106
|
+
try:
|
|
107
|
+
result = client.checkpoint(...)
|
|
108
|
+
except RejectedError as e:
|
|
109
|
+
print(f"Rejected: {e.response_notes}")
|
|
110
|
+
except TimeoutError as e:
|
|
111
|
+
print(f"Timed out for request {e.request_id}")
|
|
112
|
+
except CheqpointError as e:
|
|
113
|
+
print(f"API error {e.status_code}: {e}")
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## Examples
|
|
117
|
+
|
|
118
|
+
### With webhook (recommended for production)
|
|
119
|
+
|
|
120
|
+
```python
|
|
121
|
+
result = client.create_request(
|
|
122
|
+
type="db-write",
|
|
123
|
+
risk_level="medium",
|
|
124
|
+
summary="Delete user account usr_456",
|
|
125
|
+
details={"userId": "usr_456", "reason": "GDPR deletion request"},
|
|
126
|
+
webhook_url="https://yourapp.com/webhook/cheqpoint",
|
|
127
|
+
)
|
|
128
|
+
request_id = result["id"]
|
|
129
|
+
# Store request_id — your Flask/Django webhook handler receives the decision
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### Manual polling
|
|
133
|
+
|
|
134
|
+
```python
|
|
135
|
+
result = client.create_request(type="email", risk_level="low", ...)
|
|
136
|
+
request_id = result["id"]
|
|
137
|
+
|
|
138
|
+
# Check later
|
|
139
|
+
status = client.get_request(request_id)
|
|
140
|
+
if status.status == "APPROVED":
|
|
141
|
+
payload = status.modified_details or status.details
|
|
142
|
+
send_email(**payload)
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### LangChain agent tool
|
|
146
|
+
|
|
147
|
+
```python
|
|
148
|
+
from langchain_core.tools import tool
|
|
149
|
+
|
|
150
|
+
@tool
|
|
151
|
+
def issue_refund(user_id: str, amount: float, reason: str) -> str:
|
|
152
|
+
"""Issue a refund to a customer. Requires human approval."""
|
|
153
|
+
result = client.checkpoint(
|
|
154
|
+
type="refund",
|
|
155
|
+
risk_level="high",
|
|
156
|
+
summary=f"Refund ${amount} to user {user_id}",
|
|
157
|
+
details={"userId": user_id, "amount": amount, "reason": reason},
|
|
158
|
+
)
|
|
159
|
+
payload = result.effective_details
|
|
160
|
+
return f"Refund approved for ${payload['amount']}"
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### CrewAI tool
|
|
164
|
+
|
|
165
|
+
```python
|
|
166
|
+
from crewai_tools import BaseTool
|
|
167
|
+
|
|
168
|
+
class CheqpointApprovalTool(BaseTool):
|
|
169
|
+
name: str = "Request Human Approval"
|
|
170
|
+
description: str = "Submit a risky action for human approval before executing."
|
|
171
|
+
|
|
172
|
+
def _run(self, action_type: str, summary: str, details: dict) -> str:
|
|
173
|
+
result = client.checkpoint(
|
|
174
|
+
type=action_type,
|
|
175
|
+
risk_level="high",
|
|
176
|
+
summary=summary,
|
|
177
|
+
details=details,
|
|
178
|
+
)
|
|
179
|
+
return f"Approved. Effective details: {result.effective_details}"
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### Flask webhook handler
|
|
183
|
+
|
|
184
|
+
```python
|
|
185
|
+
from flask import Flask, request, jsonify
|
|
186
|
+
|
|
187
|
+
app = Flask(__name__)
|
|
188
|
+
|
|
189
|
+
@app.route("/webhook/cheqpoint", methods=["POST"])
|
|
190
|
+
def cheqpoint_webhook():
|
|
191
|
+
data = request.get_json()
|
|
192
|
+
status = data["status"]
|
|
193
|
+
payload = data.get("modifiedDetails") or data["details"]
|
|
194
|
+
|
|
195
|
+
if status == "APPROVED":
|
|
196
|
+
process_action(payload)
|
|
197
|
+
|
|
198
|
+
return jsonify({"ok": True}), 200
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
## Requirements
|
|
202
|
+
|
|
203
|
+
- Python 3.9+
|
|
204
|
+
- `requests` library
|
|
205
|
+
|
|
206
|
+
## License
|
|
207
|
+
|
|
208
|
+
MIT
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from .client import (
|
|
2
|
+
CheqpointClient,
|
|
3
|
+
CheqpointError,
|
|
4
|
+
RejectedError,
|
|
5
|
+
TimeoutError,
|
|
6
|
+
CheckpointResult,
|
|
7
|
+
RequestStatus,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
"CheqpointClient",
|
|
12
|
+
"CheqpointError",
|
|
13
|
+
"RejectedError",
|
|
14
|
+
"TimeoutError",
|
|
15
|
+
"CheckpointResult",
|
|
16
|
+
"RequestStatus",
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
__version__ = "0.1.0"
|
|
20
|
+
|
|
21
|
+
# Framework-specific tools are importable directly but not auto-imported
|
|
22
|
+
# to avoid requiring optional dependencies at package load time.
|
|
23
|
+
# Usage:
|
|
24
|
+
# from cheqpoint.langchain_tool import CheqpointApprovalTool
|
|
25
|
+
# from cheqpoint.crewai_tool import CheqpointApprovalTool
|