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.
@@ -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