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.
@@ -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,7 @@
1
+ # a21e
2
+
3
+ Python client for the a21e API.
4
+
5
+ ```bash
6
+ pip install a21e
7
+ ```
@@ -0,0 +1,6 @@
1
+ """a21e — Python client for the a21e API."""
2
+
3
+ from .client import A21eClient, AsyncA21eClient, A21eError
4
+
5
+ __all__ = ["A21eClient", "AsyncA21eClient", "A21eError"]
6
+ __version__ = "0.1.0"
@@ -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,9 @@
1
+ README.md
2
+ pyproject.toml
3
+ a21e/__init__.py
4
+ a21e/client.py
5
+ a21e_sdk.egg-info/PKG-INFO
6
+ a21e_sdk.egg-info/SOURCES.txt
7
+ a21e_sdk.egg-info/dependency_links.txt
8
+ a21e_sdk.egg-info/requires.txt
9
+ a21e_sdk.egg-info/top_level.txt
@@ -0,0 +1,5 @@
1
+ httpx>=0.25.0
2
+
3
+ [dev]
4
+ pytest>=7.0
5
+ pytest-asyncio>=0.21
@@ -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*"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+