a21e-sdk 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.
- a21e_sdk-0.1.0/PKG-INFO +19 -0
- a21e_sdk-0.1.0/README.md +7 -0
- a21e_sdk-0.1.0/a21e/__init__.py +6 -0
- a21e_sdk-0.1.0/a21e/client.py +635 -0
- a21e_sdk-0.1.0/a21e_sdk.egg-info/PKG-INFO +19 -0
- a21e_sdk-0.1.0/a21e_sdk.egg-info/SOURCES.txt +9 -0
- a21e_sdk-0.1.0/a21e_sdk.egg-info/dependency_links.txt +1 -0
- a21e_sdk-0.1.0/a21e_sdk.egg-info/requires.txt +5 -0
- a21e_sdk-0.1.0/a21e_sdk.egg-info/top_level.txt +1 -0
- a21e_sdk-0.1.0/pyproject.toml +18 -0
- a21e_sdk-0.1.0/setup.cfg +4 -0
a21e_sdk-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: a21e-sdk
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python client for the a21e API
|
|
5
|
+
License: MIT
|
|
6
|
+
Requires-Python: >=3.9
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
Requires-Dist: httpx>=0.25.0
|
|
9
|
+
Provides-Extra: dev
|
|
10
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
11
|
+
Requires-Dist: pytest-asyncio>=0.21; extra == "dev"
|
|
12
|
+
|
|
13
|
+
# a21e
|
|
14
|
+
|
|
15
|
+
Python client for the a21e API.
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pip install a21e
|
|
19
|
+
```
|
a21e_sdk-0.1.0/README.md
ADDED
|
@@ -0,0 +1,635 @@
|
|
|
1
|
+
"""a21e API client — sync and async."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
from typing import Any, Generator, AsyncGenerator, Optional
|
|
7
|
+
|
|
8
|
+
import httpx
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class A21eError(Exception):
|
|
12
|
+
"""Error from the a21e API."""
|
|
13
|
+
|
|
14
|
+
def __init__(self, message: str, status: int = 0, code: str | None = None, data: dict | None = None):
|
|
15
|
+
super().__init__(message)
|
|
16
|
+
self.status = status
|
|
17
|
+
self.code = code
|
|
18
|
+
self.data = data or {}
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class A21eClient:
|
|
22
|
+
"""Synchronous client for the a21e API.
|
|
23
|
+
|
|
24
|
+
Usage::
|
|
25
|
+
|
|
26
|
+
client = A21eClient(api_key="a21e_...")
|
|
27
|
+
result = client.run("prompt-id", "Hello world")
|
|
28
|
+
print(result["output"])
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
def __init__(
|
|
32
|
+
self,
|
|
33
|
+
api_key: str,
|
|
34
|
+
base_url: str = "https://api.a21e.com",
|
|
35
|
+
timeout: float = 60.0,
|
|
36
|
+
):
|
|
37
|
+
if not api_key:
|
|
38
|
+
raise ValueError("api_key is required")
|
|
39
|
+
self.api_key = api_key
|
|
40
|
+
self.base_url = base_url.rstrip("/")
|
|
41
|
+
self._http = httpx.Client(
|
|
42
|
+
base_url=self.base_url,
|
|
43
|
+
headers={
|
|
44
|
+
"Authorization": f"Bearer {api_key}",
|
|
45
|
+
"Content-Type": "application/json",
|
|
46
|
+
},
|
|
47
|
+
timeout=timeout,
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
def close(self) -> None:
|
|
51
|
+
self._http.close()
|
|
52
|
+
|
|
53
|
+
def __enter__(self):
|
|
54
|
+
return self
|
|
55
|
+
|
|
56
|
+
def __exit__(self, *args):
|
|
57
|
+
self.close()
|
|
58
|
+
|
|
59
|
+
def _request(self, method: str, path: str, **kwargs) -> Any:
|
|
60
|
+
res = self._http.request(method, path, **kwargs)
|
|
61
|
+
if res.status_code >= 400:
|
|
62
|
+
body = res.json() if res.headers.get("content-type", "").startswith("application/json") else {}
|
|
63
|
+
extra = {k: v for k, v in body.items() if k not in ("error", "message", "code")}
|
|
64
|
+
raise A21eError(
|
|
65
|
+
body.get("error", body.get("message", f"API error: {res.status_code}")),
|
|
66
|
+
status=res.status_code,
|
|
67
|
+
code=body.get("code"),
|
|
68
|
+
data=extra,
|
|
69
|
+
)
|
|
70
|
+
return res.json()
|
|
71
|
+
|
|
72
|
+
# ── Prompts ──────────────────────────────────────────────────────────
|
|
73
|
+
|
|
74
|
+
def list_prompts(
|
|
75
|
+
self, *, topic: str | None = None, q: str | None = None
|
|
76
|
+
) -> list[dict]:
|
|
77
|
+
params = {}
|
|
78
|
+
if topic:
|
|
79
|
+
params["topic"] = topic
|
|
80
|
+
if q:
|
|
81
|
+
params["q"] = q
|
|
82
|
+
return self._request("GET", "/v1/prompts", params=params)
|
|
83
|
+
|
|
84
|
+
def get_prompt(self, prompt_id: str) -> dict:
|
|
85
|
+
return self._request("GET", f"/v1/prompts/{prompt_id}")
|
|
86
|
+
|
|
87
|
+
def list_versions(self, prompt_id: str) -> list[dict]:
|
|
88
|
+
return self._request("GET", f"/v1/prompts/{prompt_id}/versions")
|
|
89
|
+
|
|
90
|
+
def run(
|
|
91
|
+
self,
|
|
92
|
+
prompt_id: str,
|
|
93
|
+
input: str,
|
|
94
|
+
*,
|
|
95
|
+
model: str | None = None,
|
|
96
|
+
version: int | None = None,
|
|
97
|
+
provider_api_key: str | None = None,
|
|
98
|
+
) -> dict:
|
|
99
|
+
"""Run a prompt and return the result."""
|
|
100
|
+
body: dict[str, Any] = {"input": input}
|
|
101
|
+
if model:
|
|
102
|
+
body["model"] = model
|
|
103
|
+
if version is not None:
|
|
104
|
+
body["version"] = version
|
|
105
|
+
if provider_api_key:
|
|
106
|
+
body["provider_api_key"] = provider_api_key
|
|
107
|
+
return self._request("POST", f"/v1/prompts/{prompt_id}/run", json=body)
|
|
108
|
+
|
|
109
|
+
def run_stream(
|
|
110
|
+
self,
|
|
111
|
+
prompt_id: str,
|
|
112
|
+
input: str,
|
|
113
|
+
*,
|
|
114
|
+
model: str | None = None,
|
|
115
|
+
version: int | None = None,
|
|
116
|
+
provider_api_key: str | None = None,
|
|
117
|
+
) -> Generator[str, None, None]:
|
|
118
|
+
"""Run a prompt with SSE streaming. Yields text chunks."""
|
|
119
|
+
body: dict[str, Any] = {"input": input, "stream": True}
|
|
120
|
+
if model:
|
|
121
|
+
body["model"] = model
|
|
122
|
+
if version is not None:
|
|
123
|
+
body["version"] = version
|
|
124
|
+
if provider_api_key:
|
|
125
|
+
body["provider_api_key"] = provider_api_key
|
|
126
|
+
|
|
127
|
+
with self._http.stream(
|
|
128
|
+
"POST", f"/v1/prompts/{prompt_id}/run", json=body
|
|
129
|
+
) as res:
|
|
130
|
+
if res.status_code >= 400:
|
|
131
|
+
res.read()
|
|
132
|
+
data = res.json() if res.headers.get("content-type", "").startswith("application/json") else {}
|
|
133
|
+
raise A21eError(
|
|
134
|
+
data.get("error", f"API error: {res.status_code}"),
|
|
135
|
+
status=res.status_code,
|
|
136
|
+
code=data.get("code"),
|
|
137
|
+
)
|
|
138
|
+
for line in res.iter_lines():
|
|
139
|
+
if line.startswith("data: "):
|
|
140
|
+
data = line[6:]
|
|
141
|
+
if data == "[DONE]":
|
|
142
|
+
return
|
|
143
|
+
try:
|
|
144
|
+
parsed = json.loads(data)
|
|
145
|
+
content = parsed.get("choices", [{}])[0].get("delta", {}).get("content")
|
|
146
|
+
if content:
|
|
147
|
+
yield content
|
|
148
|
+
except json.JSONDecodeError:
|
|
149
|
+
continue
|
|
150
|
+
|
|
151
|
+
# ── Topics ───────────────────────────────────────────────────────────
|
|
152
|
+
|
|
153
|
+
def list_topics(self) -> list[dict]:
|
|
154
|
+
return self._request("GET", "/v1/topics")
|
|
155
|
+
|
|
156
|
+
# ── Library ──────────────────────────────────────────────────────────
|
|
157
|
+
|
|
158
|
+
def list_library(self) -> list[dict]:
|
|
159
|
+
return self._request("GET", "/v1/library")
|
|
160
|
+
|
|
161
|
+
def add_to_library(
|
|
162
|
+
self, prompt_id: str, prompt_version_id: str | None = None
|
|
163
|
+
) -> dict:
|
|
164
|
+
body: dict[str, Any] = {"prompt_id": prompt_id}
|
|
165
|
+
if prompt_version_id:
|
|
166
|
+
body["prompt_version_id"] = prompt_version_id
|
|
167
|
+
return self._request("POST", "/v1/library", json=body)
|
|
168
|
+
|
|
169
|
+
def update_library_entry(
|
|
170
|
+
self, prompt_id: str, prompt_version_id: str
|
|
171
|
+
) -> dict:
|
|
172
|
+
return self._request(
|
|
173
|
+
"PATCH",
|
|
174
|
+
f"/v1/library/{prompt_id}",
|
|
175
|
+
json={"prompt_version_id": prompt_version_id},
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
# ── Workspace / Favorites ───────────────────────────────────────────
|
|
179
|
+
|
|
180
|
+
def get_workspace(self) -> dict:
|
|
181
|
+
return self._request("GET", "/v1/workspace")
|
|
182
|
+
|
|
183
|
+
def list_favorites(self) -> list[dict]:
|
|
184
|
+
return self._request("GET", "/v1/workspace/favorites")
|
|
185
|
+
|
|
186
|
+
def add_favorite(
|
|
187
|
+
self, prompt_id: str, prompt_version_id: str | None = None
|
|
188
|
+
) -> dict:
|
|
189
|
+
body: dict[str, Any] = {"prompt_id": prompt_id}
|
|
190
|
+
if prompt_version_id:
|
|
191
|
+
body["prompt_version_id"] = prompt_version_id
|
|
192
|
+
return self._request("POST", "/v1/workspace/favorites", json=body)
|
|
193
|
+
|
|
194
|
+
def update_favorite_entry(
|
|
195
|
+
self, prompt_id: str, prompt_version_id: str
|
|
196
|
+
) -> dict:
|
|
197
|
+
return self._request(
|
|
198
|
+
"PATCH",
|
|
199
|
+
f"/v1/workspace/favorites/{prompt_id}",
|
|
200
|
+
json={"prompt_version_id": prompt_version_id},
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
# ── Intents ──────────────────────────────────────────────────────────
|
|
204
|
+
|
|
205
|
+
def submit_intent(self, intent: dict) -> dict:
|
|
206
|
+
return self._request("POST", "/v1/intents", json=intent)
|
|
207
|
+
|
|
208
|
+
def list_intents(
|
|
209
|
+
self,
|
|
210
|
+
*,
|
|
211
|
+
limit: int | None = None,
|
|
212
|
+
offset: int | None = None,
|
|
213
|
+
status: str | None = None,
|
|
214
|
+
) -> dict:
|
|
215
|
+
params = {}
|
|
216
|
+
if limit is not None:
|
|
217
|
+
params["limit"] = str(limit)
|
|
218
|
+
if offset is not None:
|
|
219
|
+
params["offset"] = str(offset)
|
|
220
|
+
if status:
|
|
221
|
+
params["status"] = status
|
|
222
|
+
return self._request("GET", "/v1/intents", params=params)
|
|
223
|
+
|
|
224
|
+
def get_intent(self, intent_id: str) -> dict:
|
|
225
|
+
return self._request("GET", f"/v1/intents/{intent_id}")
|
|
226
|
+
|
|
227
|
+
def clarify_intent(self, intent_id: str, clarification_input: str) -> dict:
|
|
228
|
+
return self._request(
|
|
229
|
+
"POST",
|
|
230
|
+
f"/v1/intents/{intent_id}/clarify",
|
|
231
|
+
json={"clarification_input": clarification_input},
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
# ── Packages ─────────────────────────────────────────────────────────
|
|
235
|
+
|
|
236
|
+
def execute_package(
|
|
237
|
+
self,
|
|
238
|
+
package_id: str,
|
|
239
|
+
*,
|
|
240
|
+
input: str | None = None,
|
|
241
|
+
model: str | None = None,
|
|
242
|
+
provider_api_key: str | None = None,
|
|
243
|
+
) -> dict:
|
|
244
|
+
body: dict[str, Any] = {}
|
|
245
|
+
if input:
|
|
246
|
+
body["input"] = input
|
|
247
|
+
if model:
|
|
248
|
+
body["model"] = model
|
|
249
|
+
if provider_api_key:
|
|
250
|
+
body["provider_api_key"] = provider_api_key
|
|
251
|
+
return self._request(
|
|
252
|
+
"POST", f"/v1/packages/{package_id}/execute", json=body
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
# ── Notifications ────────────────────────────────────────────────────
|
|
256
|
+
|
|
257
|
+
def get_notifications(self, *, limit: int | None = None) -> dict:
|
|
258
|
+
params = {}
|
|
259
|
+
if limit is not None:
|
|
260
|
+
params["limit"] = str(limit)
|
|
261
|
+
return self._request("GET", "/v1/notifications", params=params)
|
|
262
|
+
|
|
263
|
+
def mark_notifications_read(self, ids: list[str] | str) -> dict:
|
|
264
|
+
return self._request("POST", "/v1/notifications/read", json={"ids": ids})
|
|
265
|
+
|
|
266
|
+
# ── Analytics ────────────────────────────────────────────────────────
|
|
267
|
+
|
|
268
|
+
def get_creator_analytics(
|
|
269
|
+
self,
|
|
270
|
+
*,
|
|
271
|
+
period_start: str | None = None,
|
|
272
|
+
period_end: str | None = None,
|
|
273
|
+
) -> dict:
|
|
274
|
+
params = {}
|
|
275
|
+
if period_start:
|
|
276
|
+
params["period_start"] = period_start
|
|
277
|
+
if period_end:
|
|
278
|
+
params["period_end"] = period_end
|
|
279
|
+
return self._request("GET", "/v1/analytics/creator", params=params)
|
|
280
|
+
|
|
281
|
+
# ── Usage ────────────────────────────────────────────────────────────
|
|
282
|
+
|
|
283
|
+
def get_usage_summary(self, *, days: int | None = None) -> dict:
|
|
284
|
+
params = {}
|
|
285
|
+
if days is not None:
|
|
286
|
+
params["days"] = str(days)
|
|
287
|
+
return self._request("GET", "/v1/usage/summary", params=params)
|
|
288
|
+
|
|
289
|
+
def get_usage_by_day(self, *, days: int | None = None) -> dict:
|
|
290
|
+
params = {}
|
|
291
|
+
if days is not None:
|
|
292
|
+
params["days"] = str(days)
|
|
293
|
+
return self._request("GET", "/v1/usage/by-day", params=params)
|
|
294
|
+
|
|
295
|
+
# ── Feedback ─────────────────────────────────────────────────────────
|
|
296
|
+
|
|
297
|
+
def submit_feedback(self, feedback: dict) -> dict:
|
|
298
|
+
return self._request("POST", "/v1/feedback", json=feedback)
|
|
299
|
+
|
|
300
|
+
# ── RPC ──────────────────────────────────────────────────────────────
|
|
301
|
+
|
|
302
|
+
def rpc(self, method: str, params: dict) -> Any:
|
|
303
|
+
"""Send a raw JSON-RPC 2.0 request."""
|
|
304
|
+
import time
|
|
305
|
+
|
|
306
|
+
response = self._request(
|
|
307
|
+
"POST",
|
|
308
|
+
"/v1/rpc",
|
|
309
|
+
json={
|
|
310
|
+
"jsonrpc": "2.0",
|
|
311
|
+
"method": method,
|
|
312
|
+
"params": params,
|
|
313
|
+
"id": int(time.time() * 1000),
|
|
314
|
+
},
|
|
315
|
+
)
|
|
316
|
+
if "error" in response:
|
|
317
|
+
raise A21eError(
|
|
318
|
+
response["error"].get("message", "RPC error"),
|
|
319
|
+
code=response["error"].get("code"),
|
|
320
|
+
)
|
|
321
|
+
return response.get("result")
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
class AsyncA21eClient:
|
|
325
|
+
"""Async client for the a21e API.
|
|
326
|
+
|
|
327
|
+
Usage::
|
|
328
|
+
|
|
329
|
+
async with AsyncA21eClient(api_key="a21e_...") as client:
|
|
330
|
+
result = await client.run("prompt-id", "Hello world")
|
|
331
|
+
print(result["output"])
|
|
332
|
+
"""
|
|
333
|
+
|
|
334
|
+
def __init__(
|
|
335
|
+
self,
|
|
336
|
+
api_key: str,
|
|
337
|
+
base_url: str = "https://api.a21e.com",
|
|
338
|
+
timeout: float = 60.0,
|
|
339
|
+
):
|
|
340
|
+
if not api_key:
|
|
341
|
+
raise ValueError("api_key is required")
|
|
342
|
+
self.api_key = api_key
|
|
343
|
+
self.base_url = base_url.rstrip("/")
|
|
344
|
+
self._http = httpx.AsyncClient(
|
|
345
|
+
base_url=self.base_url,
|
|
346
|
+
headers={
|
|
347
|
+
"Authorization": f"Bearer {api_key}",
|
|
348
|
+
"Content-Type": "application/json",
|
|
349
|
+
},
|
|
350
|
+
timeout=timeout,
|
|
351
|
+
)
|
|
352
|
+
|
|
353
|
+
async def close(self) -> None:
|
|
354
|
+
await self._http.aclose()
|
|
355
|
+
|
|
356
|
+
async def __aenter__(self):
|
|
357
|
+
return self
|
|
358
|
+
|
|
359
|
+
async def __aexit__(self, *args):
|
|
360
|
+
await self.close()
|
|
361
|
+
|
|
362
|
+
async def _request(self, method: str, path: str, **kwargs) -> Any:
|
|
363
|
+
res = await self._http.request(method, path, **kwargs)
|
|
364
|
+
if res.status_code >= 400:
|
|
365
|
+
body = res.json() if res.headers.get("content-type", "").startswith("application/json") else {}
|
|
366
|
+
extra = {k: v for k, v in body.items() if k not in ("error", "message", "code")}
|
|
367
|
+
raise A21eError(
|
|
368
|
+
body.get("error", body.get("message", f"API error: {res.status_code}")),
|
|
369
|
+
status=res.status_code,
|
|
370
|
+
code=body.get("code"),
|
|
371
|
+
data=extra,
|
|
372
|
+
)
|
|
373
|
+
return res.json()
|
|
374
|
+
|
|
375
|
+
# ── Prompts ──────────────────────────────────────────────────────────
|
|
376
|
+
|
|
377
|
+
async def list_prompts(
|
|
378
|
+
self, *, topic: str | None = None, q: str | None = None
|
|
379
|
+
) -> list[dict]:
|
|
380
|
+
params = {}
|
|
381
|
+
if topic:
|
|
382
|
+
params["topic"] = topic
|
|
383
|
+
if q:
|
|
384
|
+
params["q"] = q
|
|
385
|
+
return await self._request("GET", "/v1/prompts", params=params)
|
|
386
|
+
|
|
387
|
+
async def get_prompt(self, prompt_id: str) -> dict:
|
|
388
|
+
return await self._request("GET", f"/v1/prompts/{prompt_id}")
|
|
389
|
+
|
|
390
|
+
async def list_versions(self, prompt_id: str) -> list[dict]:
|
|
391
|
+
return await self._request("GET", f"/v1/prompts/{prompt_id}/versions")
|
|
392
|
+
|
|
393
|
+
async def run(
|
|
394
|
+
self,
|
|
395
|
+
prompt_id: str,
|
|
396
|
+
input: str,
|
|
397
|
+
*,
|
|
398
|
+
model: str | None = None,
|
|
399
|
+
version: int | None = None,
|
|
400
|
+
provider_api_key: str | None = None,
|
|
401
|
+
) -> dict:
|
|
402
|
+
body: dict[str, Any] = {"input": input}
|
|
403
|
+
if model:
|
|
404
|
+
body["model"] = model
|
|
405
|
+
if version is not None:
|
|
406
|
+
body["version"] = version
|
|
407
|
+
if provider_api_key:
|
|
408
|
+
body["provider_api_key"] = provider_api_key
|
|
409
|
+
return await self._request(
|
|
410
|
+
"POST", f"/v1/prompts/{prompt_id}/run", json=body
|
|
411
|
+
)
|
|
412
|
+
|
|
413
|
+
async def run_stream(
|
|
414
|
+
self,
|
|
415
|
+
prompt_id: str,
|
|
416
|
+
input: str,
|
|
417
|
+
*,
|
|
418
|
+
model: str | None = None,
|
|
419
|
+
version: int | None = None,
|
|
420
|
+
provider_api_key: str | None = None,
|
|
421
|
+
) -> AsyncGenerator[str, None]:
|
|
422
|
+
body: dict[str, Any] = {"input": input, "stream": True}
|
|
423
|
+
if model:
|
|
424
|
+
body["model"] = model
|
|
425
|
+
if version is not None:
|
|
426
|
+
body["version"] = version
|
|
427
|
+
if provider_api_key:
|
|
428
|
+
body["provider_api_key"] = provider_api_key
|
|
429
|
+
|
|
430
|
+
async with self._http.stream(
|
|
431
|
+
"POST", f"/v1/prompts/{prompt_id}/run", json=body
|
|
432
|
+
) as res:
|
|
433
|
+
if res.status_code >= 400:
|
|
434
|
+
await res.aread()
|
|
435
|
+
data = res.json() if res.headers.get("content-type", "").startswith("application/json") else {}
|
|
436
|
+
raise A21eError(
|
|
437
|
+
data.get("error", f"API error: {res.status_code}"),
|
|
438
|
+
status=res.status_code,
|
|
439
|
+
code=data.get("code"),
|
|
440
|
+
)
|
|
441
|
+
async for line in res.aiter_lines():
|
|
442
|
+
if line.startswith("data: "):
|
|
443
|
+
data = line[6:]
|
|
444
|
+
if data == "[DONE]":
|
|
445
|
+
return
|
|
446
|
+
try:
|
|
447
|
+
parsed = json.loads(data)
|
|
448
|
+
content = (
|
|
449
|
+
parsed.get("choices", [{}])[0]
|
|
450
|
+
.get("delta", {})
|
|
451
|
+
.get("content")
|
|
452
|
+
)
|
|
453
|
+
if content:
|
|
454
|
+
yield content
|
|
455
|
+
except json.JSONDecodeError:
|
|
456
|
+
continue
|
|
457
|
+
|
|
458
|
+
# ── Topics ───────────────────────────────────────────────────────────
|
|
459
|
+
|
|
460
|
+
async def list_topics(self) -> list[dict]:
|
|
461
|
+
return await self._request("GET", "/v1/topics")
|
|
462
|
+
|
|
463
|
+
# ── Library ──────────────────────────────────────────────────────────
|
|
464
|
+
|
|
465
|
+
async def list_library(self) -> list[dict]:
|
|
466
|
+
return await self._request("GET", "/v1/library")
|
|
467
|
+
|
|
468
|
+
async def add_to_library(
|
|
469
|
+
self, prompt_id: str, prompt_version_id: str | None = None
|
|
470
|
+
) -> dict:
|
|
471
|
+
body: dict[str, Any] = {"prompt_id": prompt_id}
|
|
472
|
+
if prompt_version_id:
|
|
473
|
+
body["prompt_version_id"] = prompt_version_id
|
|
474
|
+
return await self._request("POST", "/v1/library", json=body)
|
|
475
|
+
|
|
476
|
+
async def update_library_entry(
|
|
477
|
+
self, prompt_id: str, prompt_version_id: str
|
|
478
|
+
) -> dict:
|
|
479
|
+
return await self._request(
|
|
480
|
+
"PATCH",
|
|
481
|
+
f"/v1/library/{prompt_id}",
|
|
482
|
+
json={"prompt_version_id": prompt_version_id},
|
|
483
|
+
)
|
|
484
|
+
|
|
485
|
+
# ── Workspace / Favorites ───────────────────────────────────────────
|
|
486
|
+
|
|
487
|
+
async def get_workspace(self) -> dict:
|
|
488
|
+
return await self._request("GET", "/v1/workspace")
|
|
489
|
+
|
|
490
|
+
async def list_favorites(self) -> list[dict]:
|
|
491
|
+
return await self._request("GET", "/v1/workspace/favorites")
|
|
492
|
+
|
|
493
|
+
async def add_favorite(
|
|
494
|
+
self, prompt_id: str, prompt_version_id: str | None = None
|
|
495
|
+
) -> dict:
|
|
496
|
+
body: dict[str, Any] = {"prompt_id": prompt_id}
|
|
497
|
+
if prompt_version_id:
|
|
498
|
+
body["prompt_version_id"] = prompt_version_id
|
|
499
|
+
return await self._request("POST", "/v1/workspace/favorites", json=body)
|
|
500
|
+
|
|
501
|
+
async def update_favorite_entry(
|
|
502
|
+
self, prompt_id: str, prompt_version_id: str
|
|
503
|
+
) -> dict:
|
|
504
|
+
return await self._request(
|
|
505
|
+
"PATCH",
|
|
506
|
+
f"/v1/workspace/favorites/{prompt_id}",
|
|
507
|
+
json={"prompt_version_id": prompt_version_id},
|
|
508
|
+
)
|
|
509
|
+
|
|
510
|
+
# ── Intents ──────────────────────────────────────────────────────────
|
|
511
|
+
|
|
512
|
+
async def submit_intent(self, intent: dict) -> dict:
|
|
513
|
+
return await self._request("POST", "/v1/intents", json=intent)
|
|
514
|
+
|
|
515
|
+
async def list_intents(
|
|
516
|
+
self,
|
|
517
|
+
*,
|
|
518
|
+
limit: int | None = None,
|
|
519
|
+
offset: int | None = None,
|
|
520
|
+
status: str | None = None,
|
|
521
|
+
) -> dict:
|
|
522
|
+
params = {}
|
|
523
|
+
if limit is not None:
|
|
524
|
+
params["limit"] = str(limit)
|
|
525
|
+
if offset is not None:
|
|
526
|
+
params["offset"] = str(offset)
|
|
527
|
+
if status:
|
|
528
|
+
params["status"] = status
|
|
529
|
+
return await self._request("GET", "/v1/intents", params=params)
|
|
530
|
+
|
|
531
|
+
async def get_intent(self, intent_id: str) -> dict:
|
|
532
|
+
return await self._request("GET", f"/v1/intents/{intent_id}")
|
|
533
|
+
|
|
534
|
+
async def clarify_intent(
|
|
535
|
+
self, intent_id: str, clarification_input: str
|
|
536
|
+
) -> dict:
|
|
537
|
+
return await self._request(
|
|
538
|
+
"POST",
|
|
539
|
+
f"/v1/intents/{intent_id}/clarify",
|
|
540
|
+
json={"clarification_input": clarification_input},
|
|
541
|
+
)
|
|
542
|
+
|
|
543
|
+
# ── Packages ─────────────────────────────────────────────────────────
|
|
544
|
+
|
|
545
|
+
async def execute_package(
|
|
546
|
+
self,
|
|
547
|
+
package_id: str,
|
|
548
|
+
*,
|
|
549
|
+
input: str | None = None,
|
|
550
|
+
model: str | None = None,
|
|
551
|
+
provider_api_key: str | None = None,
|
|
552
|
+
) -> dict:
|
|
553
|
+
body: dict[str, Any] = {}
|
|
554
|
+
if input:
|
|
555
|
+
body["input"] = input
|
|
556
|
+
if model:
|
|
557
|
+
body["model"] = model
|
|
558
|
+
if provider_api_key:
|
|
559
|
+
body["provider_api_key"] = provider_api_key
|
|
560
|
+
return await self._request(
|
|
561
|
+
"POST", f"/v1/packages/{package_id}/execute", json=body
|
|
562
|
+
)
|
|
563
|
+
|
|
564
|
+
# ── Notifications ────────────────────────────────────────────────────
|
|
565
|
+
|
|
566
|
+
async def get_notifications(self, *, limit: int | None = None) -> dict:
|
|
567
|
+
params = {}
|
|
568
|
+
if limit is not None:
|
|
569
|
+
params["limit"] = str(limit)
|
|
570
|
+
return await self._request("GET", "/v1/notifications", params=params)
|
|
571
|
+
|
|
572
|
+
async def mark_notifications_read(
|
|
573
|
+
self, ids: list[str] | str
|
|
574
|
+
) -> dict:
|
|
575
|
+
return await self._request(
|
|
576
|
+
"POST", "/v1/notifications/read", json={"ids": ids}
|
|
577
|
+
)
|
|
578
|
+
|
|
579
|
+
# ── Analytics ────────────────────────────────────────────────────────
|
|
580
|
+
|
|
581
|
+
async def get_creator_analytics(
|
|
582
|
+
self,
|
|
583
|
+
*,
|
|
584
|
+
period_start: str | None = None,
|
|
585
|
+
period_end: str | None = None,
|
|
586
|
+
) -> dict:
|
|
587
|
+
params = {}
|
|
588
|
+
if period_start:
|
|
589
|
+
params["period_start"] = period_start
|
|
590
|
+
if period_end:
|
|
591
|
+
params["period_end"] = period_end
|
|
592
|
+
return await self._request(
|
|
593
|
+
"GET", "/v1/analytics/creator", params=params
|
|
594
|
+
)
|
|
595
|
+
|
|
596
|
+
# ── Usage ────────────────────────────────────────────────────────────
|
|
597
|
+
|
|
598
|
+
async def get_usage_summary(self, *, days: int | None = None) -> dict:
|
|
599
|
+
params = {}
|
|
600
|
+
if days is not None:
|
|
601
|
+
params["days"] = str(days)
|
|
602
|
+
return await self._request("GET", "/v1/usage/summary", params=params)
|
|
603
|
+
|
|
604
|
+
async def get_usage_by_day(self, *, days: int | None = None) -> dict:
|
|
605
|
+
params = {}
|
|
606
|
+
if days is not None:
|
|
607
|
+
params["days"] = str(days)
|
|
608
|
+
return await self._request("GET", "/v1/usage/by-day", params=params)
|
|
609
|
+
|
|
610
|
+
# ── Feedback ─────────────────────────────────────────────────────────
|
|
611
|
+
|
|
612
|
+
async def submit_feedback(self, feedback: dict) -> dict:
|
|
613
|
+
return await self._request("POST", "/v1/feedback", json=feedback)
|
|
614
|
+
|
|
615
|
+
# ── RPC ──────────────────────────────────────────────────────────────
|
|
616
|
+
|
|
617
|
+
async def rpc(self, method: str, params: dict) -> Any:
|
|
618
|
+
import time
|
|
619
|
+
|
|
620
|
+
response = await self._request(
|
|
621
|
+
"POST",
|
|
622
|
+
"/v1/rpc",
|
|
623
|
+
json={
|
|
624
|
+
"jsonrpc": "2.0",
|
|
625
|
+
"method": method,
|
|
626
|
+
"params": params,
|
|
627
|
+
"id": int(time.time() * 1000),
|
|
628
|
+
},
|
|
629
|
+
)
|
|
630
|
+
if "error" in response:
|
|
631
|
+
raise A21eError(
|
|
632
|
+
response["error"].get("message", "RPC error"),
|
|
633
|
+
code=response["error"].get("code"),
|
|
634
|
+
)
|
|
635
|
+
return response.get("result")
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: a21e-sdk
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python client for the a21e API
|
|
5
|
+
License: MIT
|
|
6
|
+
Requires-Python: >=3.9
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
Requires-Dist: httpx>=0.25.0
|
|
9
|
+
Provides-Extra: dev
|
|
10
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
11
|
+
Requires-Dist: pytest-asyncio>=0.21; extra == "dev"
|
|
12
|
+
|
|
13
|
+
# a21e
|
|
14
|
+
|
|
15
|
+
Python client for the a21e API.
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pip install a21e
|
|
19
|
+
```
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
a21e
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "a21e-sdk"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Python client for the a21e API"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
license = {text = "MIT"}
|
|
12
|
+
dependencies = ["httpx>=0.25.0"]
|
|
13
|
+
|
|
14
|
+
[project.optional-dependencies]
|
|
15
|
+
dev = ["pytest>=7.0", "pytest-asyncio>=0.21"]
|
|
16
|
+
|
|
17
|
+
[tool.setuptools.packages.find]
|
|
18
|
+
include = ["a21e*"]
|
a21e_sdk-0.1.0/setup.cfg
ADDED