paiment 0.2.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.
- paiment-0.2.0/PKG-INFO +166 -0
- paiment-0.2.0/README.md +138 -0
- paiment-0.2.0/paiment/__init__.py +78 -0
- paiment-0.2.0/paiment/_errors.py +25 -0
- paiment-0.2.0/paiment/_http.py +119 -0
- paiment-0.2.0/paiment/_models.py +221 -0
- paiment-0.2.0/paiment/resources/__init__.py +0 -0
- paiment-0.2.0/paiment/resources/plans.py +163 -0
- paiment-0.2.0/paiment/resources/subscriptions.py +216 -0
- paiment-0.2.0/paiment/resources/usage.py +130 -0
- paiment-0.2.0/paiment/resources/users.py +219 -0
- paiment-0.2.0/paiment.egg-info/PKG-INFO +166 -0
- paiment-0.2.0/paiment.egg-info/SOURCES.txt +16 -0
- paiment-0.2.0/paiment.egg-info/dependency_links.txt +1 -0
- paiment-0.2.0/paiment.egg-info/requires.txt +8 -0
- paiment-0.2.0/paiment.egg-info/top_level.txt +1 -0
- paiment-0.2.0/pyproject.toml +42 -0
- paiment-0.2.0/setup.cfg +4 -0
paiment-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: paiment
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Official Python SDK for the Paiment billing API
|
|
5
|
+
Author: Paiment
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://paiment.tech
|
|
8
|
+
Project-URL: Documentation, https://paiment.tech
|
|
9
|
+
Keywords: paiment,billing,sdk,llm,usage
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
19
|
+
Requires-Python: >=3.9
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
Requires-Dist: httpx>=0.27.0
|
|
22
|
+
Provides-Extra: dev
|
|
23
|
+
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
24
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
|
|
25
|
+
Requires-Dist: mypy>=1.9; extra == "dev"
|
|
26
|
+
Requires-Dist: build; extra == "dev"
|
|
27
|
+
Requires-Dist: twine; extra == "dev"
|
|
28
|
+
|
|
29
|
+
# paiment (Python SDK)
|
|
30
|
+
|
|
31
|
+
Official Python SDK for the [Paiment](https://paiment.tech) billing API.
|
|
32
|
+
|
|
33
|
+
> **npm users:** install `@paiment/sdk` instead. This package is the Python equivalent.
|
|
34
|
+
|
|
35
|
+
Use it in your **backend** to register subscribers, report LLM usage, and manage plans.
|
|
36
|
+
|
|
37
|
+
## Install
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
pip install paiment
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Python 3.9+.
|
|
44
|
+
|
|
45
|
+
## Setup
|
|
46
|
+
|
|
47
|
+
Get an API key from **Settings → API Keys**.
|
|
48
|
+
|
|
49
|
+
| Key format | Notes |
|
|
50
|
+
|------------|--------|
|
|
51
|
+
| `tenantId:workspaceId:secret` | **Recommended** |
|
|
52
|
+
| `tenantId:secret` | Pass `workspace_id` on the client |
|
|
53
|
+
|
|
54
|
+
```python
|
|
55
|
+
from paiment import PaimentClient
|
|
56
|
+
|
|
57
|
+
client = PaimentClient(api_key="tenantId:workspaceId:secret")
|
|
58
|
+
# client = PaimentClient(api_key="...", base_url="http://localhost:5246")
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Quick start
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
from paiment import PaimentClient
|
|
65
|
+
|
|
66
|
+
with PaimentClient(api_key="tenantId:workspaceId:secret") as client:
|
|
67
|
+
sub = client.subscriptions.create(
|
|
68
|
+
user_id="user_123",
|
|
69
|
+
plan_name="Pro",
|
|
70
|
+
price=2999,
|
|
71
|
+
currency="USD",
|
|
72
|
+
interval="monthly",
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
client.usage.report(
|
|
76
|
+
user_id="user_123",
|
|
77
|
+
input_tokens=1250,
|
|
78
|
+
output_tokens=340,
|
|
79
|
+
provider="openai",
|
|
80
|
+
model="gpt-4o",
|
|
81
|
+
plan_id=sub.plan_id,
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
client.subscriptions.end(sub.id)
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Async
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
from paiment import AsyncPaimentClient
|
|
91
|
+
|
|
92
|
+
async with AsyncPaimentClient(api_key="tenantId:workspaceId:secret") as client:
|
|
93
|
+
sub = await client.subscriptions.create(
|
|
94
|
+
user_id="user_123",
|
|
95
|
+
plan_name="Pro",
|
|
96
|
+
price=2999,
|
|
97
|
+
currency="USD",
|
|
98
|
+
interval="monthly",
|
|
99
|
+
)
|
|
100
|
+
await client.usage.report(
|
|
101
|
+
user_id="user_123",
|
|
102
|
+
input_tokens=1250,
|
|
103
|
+
output_tokens=340,
|
|
104
|
+
provider="openai",
|
|
105
|
+
model="gpt-4o",
|
|
106
|
+
)
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## API reference
|
|
110
|
+
|
|
111
|
+
### Subscriptions
|
|
112
|
+
|
|
113
|
+
| Method | Description |
|
|
114
|
+
|--------|-------------|
|
|
115
|
+
| `create(user_id, plan_name, price, currency, interval, ...)` | New subscription |
|
|
116
|
+
| `list(user_id=..., status=...)` | List subscriptions |
|
|
117
|
+
| `get(subscription_id)` | Get by id |
|
|
118
|
+
| `update(subscription_id, data)` | Patch subscription |
|
|
119
|
+
| `end(subscription_id)` | Close subscription |
|
|
120
|
+
| `cancel(subscription_id)` | End immediately |
|
|
121
|
+
|
|
122
|
+
### Users
|
|
123
|
+
|
|
124
|
+
| Method | Description |
|
|
125
|
+
|--------|-------------|
|
|
126
|
+
| `create(user_id, plan_name, price, currency, interval, ...)` | User + subscription shortcut |
|
|
127
|
+
| `get(user_id)` | Get by your user id |
|
|
128
|
+
| `list(plan_name=..., page=..., limit=...)` | List users |
|
|
129
|
+
| `update(user_id, ...)` | Partial update |
|
|
130
|
+
| `delete(user_id)` | Delete user |
|
|
131
|
+
|
|
132
|
+
### Usage
|
|
133
|
+
|
|
134
|
+
| Method | Description |
|
|
135
|
+
|--------|-------------|
|
|
136
|
+
| `report(user_id, input_tokens, output_tokens, provider, model, ...)` | Record usage |
|
|
137
|
+
| `list(user_id=..., page=..., limit=...)` | List usage rows |
|
|
138
|
+
|
|
139
|
+
### Plans
|
|
140
|
+
|
|
141
|
+
| Method | Description |
|
|
142
|
+
|--------|-------------|
|
|
143
|
+
| `create(name, currency, list_price_cents=..., ...)` | Create plan |
|
|
144
|
+
| `list()` / `get(id)` / `update(id, ...)` / `delete(id)` | CRUD |
|
|
145
|
+
|
|
146
|
+
## Fields
|
|
147
|
+
|
|
148
|
+
| Field | Meaning |
|
|
149
|
+
|-------|---------|
|
|
150
|
+
| `user_id` | Your identifier for the end-user |
|
|
151
|
+
| `plan_name` | Plan catalog name |
|
|
152
|
+
| `price` | Subscription price in **cents** |
|
|
153
|
+
| `list_price_cents` | Catalog list price on plans only |
|
|
154
|
+
|
|
155
|
+
## Errors
|
|
156
|
+
|
|
157
|
+
```python
|
|
158
|
+
from paiment import PaimentAuthError, PaimentNotFoundError, PaimentValidationError, PaimentError
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
| Class | HTTP status |
|
|
162
|
+
|-------|-------------|
|
|
163
|
+
| `PaimentAuthError` | 401 |
|
|
164
|
+
| `PaimentNotFoundError` | 404 |
|
|
165
|
+
| `PaimentValidationError` | 400 |
|
|
166
|
+
| `PaimentError` | other |
|
paiment-0.2.0/README.md
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# paiment (Python SDK)
|
|
2
|
+
|
|
3
|
+
Official Python SDK for the [Paiment](https://paiment.tech) billing API.
|
|
4
|
+
|
|
5
|
+
> **npm users:** install `@paiment/sdk` instead. This package is the Python equivalent.
|
|
6
|
+
|
|
7
|
+
Use it in your **backend** to register subscribers, report LLM usage, and manage plans.
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pip install paiment
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Python 3.9+.
|
|
16
|
+
|
|
17
|
+
## Setup
|
|
18
|
+
|
|
19
|
+
Get an API key from **Settings → API Keys**.
|
|
20
|
+
|
|
21
|
+
| Key format | Notes |
|
|
22
|
+
|------------|--------|
|
|
23
|
+
| `tenantId:workspaceId:secret` | **Recommended** |
|
|
24
|
+
| `tenantId:secret` | Pass `workspace_id` on the client |
|
|
25
|
+
|
|
26
|
+
```python
|
|
27
|
+
from paiment import PaimentClient
|
|
28
|
+
|
|
29
|
+
client = PaimentClient(api_key="tenantId:workspaceId:secret")
|
|
30
|
+
# client = PaimentClient(api_key="...", base_url="http://localhost:5246")
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Quick start
|
|
34
|
+
|
|
35
|
+
```python
|
|
36
|
+
from paiment import PaimentClient
|
|
37
|
+
|
|
38
|
+
with PaimentClient(api_key="tenantId:workspaceId:secret") as client:
|
|
39
|
+
sub = client.subscriptions.create(
|
|
40
|
+
user_id="user_123",
|
|
41
|
+
plan_name="Pro",
|
|
42
|
+
price=2999,
|
|
43
|
+
currency="USD",
|
|
44
|
+
interval="monthly",
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
client.usage.report(
|
|
48
|
+
user_id="user_123",
|
|
49
|
+
input_tokens=1250,
|
|
50
|
+
output_tokens=340,
|
|
51
|
+
provider="openai",
|
|
52
|
+
model="gpt-4o",
|
|
53
|
+
plan_id=sub.plan_id,
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
client.subscriptions.end(sub.id)
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Async
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
from paiment import AsyncPaimentClient
|
|
63
|
+
|
|
64
|
+
async with AsyncPaimentClient(api_key="tenantId:workspaceId:secret") as client:
|
|
65
|
+
sub = await client.subscriptions.create(
|
|
66
|
+
user_id="user_123",
|
|
67
|
+
plan_name="Pro",
|
|
68
|
+
price=2999,
|
|
69
|
+
currency="USD",
|
|
70
|
+
interval="monthly",
|
|
71
|
+
)
|
|
72
|
+
await client.usage.report(
|
|
73
|
+
user_id="user_123",
|
|
74
|
+
input_tokens=1250,
|
|
75
|
+
output_tokens=340,
|
|
76
|
+
provider="openai",
|
|
77
|
+
model="gpt-4o",
|
|
78
|
+
)
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## API reference
|
|
82
|
+
|
|
83
|
+
### Subscriptions
|
|
84
|
+
|
|
85
|
+
| Method | Description |
|
|
86
|
+
|--------|-------------|
|
|
87
|
+
| `create(user_id, plan_name, price, currency, interval, ...)` | New subscription |
|
|
88
|
+
| `list(user_id=..., status=...)` | List subscriptions |
|
|
89
|
+
| `get(subscription_id)` | Get by id |
|
|
90
|
+
| `update(subscription_id, data)` | Patch subscription |
|
|
91
|
+
| `end(subscription_id)` | Close subscription |
|
|
92
|
+
| `cancel(subscription_id)` | End immediately |
|
|
93
|
+
|
|
94
|
+
### Users
|
|
95
|
+
|
|
96
|
+
| Method | Description |
|
|
97
|
+
|--------|-------------|
|
|
98
|
+
| `create(user_id, plan_name, price, currency, interval, ...)` | User + subscription shortcut |
|
|
99
|
+
| `get(user_id)` | Get by your user id |
|
|
100
|
+
| `list(plan_name=..., page=..., limit=...)` | List users |
|
|
101
|
+
| `update(user_id, ...)` | Partial update |
|
|
102
|
+
| `delete(user_id)` | Delete user |
|
|
103
|
+
|
|
104
|
+
### Usage
|
|
105
|
+
|
|
106
|
+
| Method | Description |
|
|
107
|
+
|--------|-------------|
|
|
108
|
+
| `report(user_id, input_tokens, output_tokens, provider, model, ...)` | Record usage |
|
|
109
|
+
| `list(user_id=..., page=..., limit=...)` | List usage rows |
|
|
110
|
+
|
|
111
|
+
### Plans
|
|
112
|
+
|
|
113
|
+
| Method | Description |
|
|
114
|
+
|--------|-------------|
|
|
115
|
+
| `create(name, currency, list_price_cents=..., ...)` | Create plan |
|
|
116
|
+
| `list()` / `get(id)` / `update(id, ...)` / `delete(id)` | CRUD |
|
|
117
|
+
|
|
118
|
+
## Fields
|
|
119
|
+
|
|
120
|
+
| Field | Meaning |
|
|
121
|
+
|-------|---------|
|
|
122
|
+
| `user_id` | Your identifier for the end-user |
|
|
123
|
+
| `plan_name` | Plan catalog name |
|
|
124
|
+
| `price` | Subscription price in **cents** |
|
|
125
|
+
| `list_price_cents` | Catalog list price on plans only |
|
|
126
|
+
|
|
127
|
+
## Errors
|
|
128
|
+
|
|
129
|
+
```python
|
|
130
|
+
from paiment import PaimentAuthError, PaimentNotFoundError, PaimentValidationError, PaimentError
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
| Class | HTTP status |
|
|
134
|
+
|-------|-------------|
|
|
135
|
+
| `PaimentAuthError` | 401 |
|
|
136
|
+
| `PaimentNotFoundError` | 404 |
|
|
137
|
+
| `PaimentValidationError` | 400 |
|
|
138
|
+
| `PaimentError` | other |
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from ._errors import PaimentAuthError, PaimentError, PaimentNotFoundError, PaimentValidationError
|
|
6
|
+
from ._http import AsyncHttpClient, HttpClient
|
|
7
|
+
from ._models import FixedPrice, Page, Plan, Pagination, Subscription, Usage, UsageBasedPrice, User
|
|
8
|
+
from .resources.plans import AsyncPlansResource, PlansResource
|
|
9
|
+
from .resources.subscriptions import AsyncSubscriptionsResource, SubscriptionsResource
|
|
10
|
+
from .resources.usage import AsyncUsageResource, UsageResource
|
|
11
|
+
from .resources.users import AsyncUsersResource, UsersResource
|
|
12
|
+
|
|
13
|
+
__all__ = [
|
|
14
|
+
"PaimentClient",
|
|
15
|
+
"AsyncPaimentClient",
|
|
16
|
+
"PaimentError",
|
|
17
|
+
"PaimentAuthError",
|
|
18
|
+
"PaimentNotFoundError",
|
|
19
|
+
"PaimentValidationError",
|
|
20
|
+
"User",
|
|
21
|
+
"Subscription",
|
|
22
|
+
"Plan",
|
|
23
|
+
"UsageBasedPrice",
|
|
24
|
+
"FixedPrice",
|
|
25
|
+
"Usage",
|
|
26
|
+
"Page",
|
|
27
|
+
"Pagination",
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
_DEFAULT_BASE_URL = "https://api.paiment.tech"
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class PaimentClient:
|
|
34
|
+
def __init__(
|
|
35
|
+
self,
|
|
36
|
+
*,
|
|
37
|
+
api_key: str,
|
|
38
|
+
base_url: str = _DEFAULT_BASE_URL,
|
|
39
|
+
workspace_id: str | None = None,
|
|
40
|
+
) -> None:
|
|
41
|
+
self._http = HttpClient(api_key=api_key, base_url=base_url, workspace_id=workspace_id)
|
|
42
|
+
self.users = UsersResource(self._http)
|
|
43
|
+
self.subscriptions = SubscriptionsResource(self._http)
|
|
44
|
+
self.plans = PlansResource(self._http)
|
|
45
|
+
self.usage = UsageResource(self._http)
|
|
46
|
+
|
|
47
|
+
def close(self) -> None:
|
|
48
|
+
self._http.close()
|
|
49
|
+
|
|
50
|
+
def __enter__(self) -> "PaimentClient":
|
|
51
|
+
return self
|
|
52
|
+
|
|
53
|
+
def __exit__(self, *_: Any) -> None:
|
|
54
|
+
self.close()
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class AsyncPaimentClient:
|
|
58
|
+
def __init__(
|
|
59
|
+
self,
|
|
60
|
+
*,
|
|
61
|
+
api_key: str,
|
|
62
|
+
base_url: str = _DEFAULT_BASE_URL,
|
|
63
|
+
workspace_id: str | None = None,
|
|
64
|
+
) -> None:
|
|
65
|
+
self._http = AsyncHttpClient(api_key=api_key, base_url=base_url, workspace_id=workspace_id)
|
|
66
|
+
self.users = AsyncUsersResource(self._http)
|
|
67
|
+
self.subscriptions = AsyncSubscriptionsResource(self._http)
|
|
68
|
+
self.plans = AsyncPlansResource(self._http)
|
|
69
|
+
self.usage = AsyncUsageResource(self._http)
|
|
70
|
+
|
|
71
|
+
async def aclose(self) -> None:
|
|
72
|
+
await self._http.aclose()
|
|
73
|
+
|
|
74
|
+
async def __aenter__(self) -> "AsyncPaimentClient":
|
|
75
|
+
return self
|
|
76
|
+
|
|
77
|
+
async def __aexit__(self, *_: Any) -> None:
|
|
78
|
+
await self.aclose()
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class PaimentError(Exception):
|
|
7
|
+
def __init__(self, message: str, status_code: int | None = None, body: Any = None) -> None:
|
|
8
|
+
super().__init__(message)
|
|
9
|
+
self.status_code = status_code
|
|
10
|
+
self.body = body
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class PaimentAuthError(PaimentError):
|
|
14
|
+
def __init__(self, body: Any = None) -> None:
|
|
15
|
+
super().__init__("Authentication failed. Check your API key.", 401, body)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class PaimentNotFoundError(PaimentError):
|
|
19
|
+
def __init__(self, resource: str = "Resource") -> None:
|
|
20
|
+
super().__init__(f"{resource} not found.", 404)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class PaimentValidationError(PaimentError):
|
|
24
|
+
def __init__(self, body: Any = None) -> None:
|
|
25
|
+
super().__init__("Validation failed.", 400, body)
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
import httpx
|
|
6
|
+
|
|
7
|
+
from ._errors import PaimentAuthError, PaimentError, PaimentNotFoundError, PaimentValidationError
|
|
8
|
+
|
|
9
|
+
_DEFAULT_TIMEOUT = 30.0
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _raise_for_status(response: httpx.Response) -> None:
|
|
13
|
+
if response.is_success:
|
|
14
|
+
return
|
|
15
|
+
try:
|
|
16
|
+
body: Any = response.json()
|
|
17
|
+
except Exception:
|
|
18
|
+
body = response.text
|
|
19
|
+
|
|
20
|
+
status = response.status_code
|
|
21
|
+
if status == 401:
|
|
22
|
+
raise PaimentAuthError(body)
|
|
23
|
+
if status == 404:
|
|
24
|
+
raise PaimentNotFoundError()
|
|
25
|
+
if status == 400:
|
|
26
|
+
raise PaimentValidationError(body)
|
|
27
|
+
raise PaimentError(f"Request failed with status {status}", status, body)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class HttpClient:
|
|
31
|
+
def __init__(self, api_key: str, base_url: str, workspace_id: str | None = None) -> None:
|
|
32
|
+
headers: dict[str, str] = {"X-Api-Key": api_key, "Content-Type": "application/json"}
|
|
33
|
+
if workspace_id:
|
|
34
|
+
headers["X-Workspace-Id"] = workspace_id
|
|
35
|
+
self.workspace_id = workspace_id
|
|
36
|
+
self._client = httpx.Client(
|
|
37
|
+
base_url=base_url.rstrip("/"),
|
|
38
|
+
headers=headers,
|
|
39
|
+
timeout=_DEFAULT_TIMEOUT,
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
def close(self) -> None:
|
|
43
|
+
self._client.close()
|
|
44
|
+
|
|
45
|
+
def __enter__(self) -> "HttpClient":
|
|
46
|
+
return self
|
|
47
|
+
|
|
48
|
+
def __exit__(self, *_: Any) -> None:
|
|
49
|
+
self.close()
|
|
50
|
+
|
|
51
|
+
def get(self, path: str, params: dict[str, Any] | None = None) -> Any:
|
|
52
|
+
r = self._client.get(path, params={k: v for k, v in (params or {}).items() if v is not None})
|
|
53
|
+
_raise_for_status(r)
|
|
54
|
+
return r.json() if r.status_code != 204 else None
|
|
55
|
+
|
|
56
|
+
def post(self, path: str, body: Any = None) -> Any:
|
|
57
|
+
r = self._client.post(path, json=body)
|
|
58
|
+
_raise_for_status(r)
|
|
59
|
+
return r.json() if r.status_code != 204 else None
|
|
60
|
+
|
|
61
|
+
def patch(self, path: str, body: Any = None) -> Any:
|
|
62
|
+
r = self._client.patch(path, json=body)
|
|
63
|
+
_raise_for_status(r)
|
|
64
|
+
return r.json() if r.status_code != 204 else None
|
|
65
|
+
|
|
66
|
+
def put(self, path: str, body: Any = None) -> Any:
|
|
67
|
+
r = self._client.put(path, json=body)
|
|
68
|
+
_raise_for_status(r)
|
|
69
|
+
return r.json() if r.status_code != 204 else None
|
|
70
|
+
|
|
71
|
+
def delete(self, path: str) -> None:
|
|
72
|
+
r = self._client.delete(path)
|
|
73
|
+
_raise_for_status(r)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class AsyncHttpClient:
|
|
77
|
+
def __init__(self, api_key: str, base_url: str, workspace_id: str | None = None) -> None:
|
|
78
|
+
headers: dict[str, str] = {"X-Api-Key": api_key, "Content-Type": "application/json"}
|
|
79
|
+
if workspace_id:
|
|
80
|
+
headers["X-Workspace-Id"] = workspace_id
|
|
81
|
+
self.workspace_id = workspace_id
|
|
82
|
+
self._client = httpx.AsyncClient(
|
|
83
|
+
base_url=base_url.rstrip("/"),
|
|
84
|
+
headers=headers,
|
|
85
|
+
timeout=_DEFAULT_TIMEOUT,
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
async def aclose(self) -> None:
|
|
89
|
+
await self._client.aclose()
|
|
90
|
+
|
|
91
|
+
async def __aenter__(self) -> "AsyncHttpClient":
|
|
92
|
+
return self
|
|
93
|
+
|
|
94
|
+
async def __aexit__(self, *_: Any) -> None:
|
|
95
|
+
await self.aclose()
|
|
96
|
+
|
|
97
|
+
async def get(self, path: str, params: dict[str, Any] | None = None) -> Any:
|
|
98
|
+
r = await self._client.get(path, params={k: v for k, v in (params or {}).items() if v is not None})
|
|
99
|
+
_raise_for_status(r)
|
|
100
|
+
return r.json() if r.status_code != 204 else None
|
|
101
|
+
|
|
102
|
+
async def post(self, path: str, body: Any = None) -> Any:
|
|
103
|
+
r = await self._client.post(path, json=body)
|
|
104
|
+
_raise_for_status(r)
|
|
105
|
+
return r.json() if r.status_code != 204 else None
|
|
106
|
+
|
|
107
|
+
async def patch(self, path: str, body: Any = None) -> Any:
|
|
108
|
+
r = await self._client.patch(path, json=body)
|
|
109
|
+
_raise_for_status(r)
|
|
110
|
+
return r.json() if r.status_code != 204 else None
|
|
111
|
+
|
|
112
|
+
async def put(self, path: str, body: Any = None) -> Any:
|
|
113
|
+
r = await self._client.put(path, json=body)
|
|
114
|
+
_raise_for_status(r)
|
|
115
|
+
return r.json() if r.status_code != 204 else None
|
|
116
|
+
|
|
117
|
+
async def delete(self, path: str) -> None:
|
|
118
|
+
r = await self._client.delete(path)
|
|
119
|
+
_raise_for_status(r)
|