delula-sdk 0.1.0__py3-none-any.whl
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.
- delula_sdk/__init__.py +6 -0
- delula_sdk/client.py +122 -0
- delula_sdk/errors.py +17 -0
- delula_sdk-0.1.0.dist-info/METADATA +59 -0
- delula_sdk-0.1.0.dist-info/RECORD +7 -0
- delula_sdk-0.1.0.dist-info/WHEEL +5 -0
- delula_sdk-0.1.0.dist-info/top_level.txt +1 -0
delula_sdk/__init__.py
ADDED
delula_sdk/client.py
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import time
|
|
5
|
+
import uuid
|
|
6
|
+
from typing import Any
|
|
7
|
+
from urllib import error, request
|
|
8
|
+
|
|
9
|
+
from delula_sdk.errors import DelulaApiError
|
|
10
|
+
|
|
11
|
+
DEFAULT_BASE_URL = "https://delu.la/public/api"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class DelulaClient:
|
|
15
|
+
def __init__(self, api_key: str, base_url: str = DEFAULT_BASE_URL) -> None:
|
|
16
|
+
self.api_key = api_key
|
|
17
|
+
self.base_url = base_url.rstrip("/")
|
|
18
|
+
|
|
19
|
+
def _request(
|
|
20
|
+
self,
|
|
21
|
+
method: str,
|
|
22
|
+
path: str,
|
|
23
|
+
body: dict[str, Any] | None = None,
|
|
24
|
+
extra_headers: dict[str, str] | None = None,
|
|
25
|
+
) -> Any:
|
|
26
|
+
url = f"{self.base_url}{path}"
|
|
27
|
+
headers = {
|
|
28
|
+
"Authorization": f"Bearer {self.api_key}",
|
|
29
|
+
"Accept": "application/json",
|
|
30
|
+
}
|
|
31
|
+
data: bytes | None = None
|
|
32
|
+
if body is not None:
|
|
33
|
+
headers["Content-Type"] = "application/json"
|
|
34
|
+
data = json.dumps(body).encode("utf-8")
|
|
35
|
+
if extra_headers:
|
|
36
|
+
headers.update(extra_headers)
|
|
37
|
+
req = request.Request(url, data=data, headers=headers, method=method)
|
|
38
|
+
try:
|
|
39
|
+
with request.urlopen(req, timeout=120) as res:
|
|
40
|
+
payload = res.read().decode("utf-8")
|
|
41
|
+
return json.loads(payload) if payload else {}
|
|
42
|
+
except error.HTTPError as exc:
|
|
43
|
+
raw = exc.read().decode("utf-8")
|
|
44
|
+
try:
|
|
45
|
+
parsed = json.loads(raw)
|
|
46
|
+
except json.JSONDecodeError:
|
|
47
|
+
parsed = {}
|
|
48
|
+
raise DelulaApiError(
|
|
49
|
+
exc.code,
|
|
50
|
+
parsed.get("code", "internal_error"),
|
|
51
|
+
parsed.get("message", exc.reason),
|
|
52
|
+
parsed.get("details"),
|
|
53
|
+
) from exc
|
|
54
|
+
|
|
55
|
+
def get_account(self) -> dict[str, Any]:
|
|
56
|
+
return self._request("GET", "/account")
|
|
57
|
+
|
|
58
|
+
def get_credits(self) -> dict[str, Any]:
|
|
59
|
+
return self._request("GET", "/account/credits")
|
|
60
|
+
|
|
61
|
+
def list_recipes(self, lang: str = "en") -> dict[str, Any]:
|
|
62
|
+
return self._request("GET", f"/recipes?lang={lang}")
|
|
63
|
+
|
|
64
|
+
def get_recipe(self, recipe_id: int, lang: str = "en") -> dict[str, Any]:
|
|
65
|
+
return self._request("GET", f"/recipes/{recipe_id}?lang={lang}")
|
|
66
|
+
|
|
67
|
+
def preflight_generation(
|
|
68
|
+
self, recipe_id: int, form_data: dict[str, Any] | None = None
|
|
69
|
+
) -> dict[str, Any]:
|
|
70
|
+
return self._request(
|
|
71
|
+
"POST",
|
|
72
|
+
"/generations/preflight",
|
|
73
|
+
{"recipeId": recipe_id, "formData": form_data or {}},
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
def create_generation(
|
|
77
|
+
self,
|
|
78
|
+
recipe_id: int,
|
|
79
|
+
form_data: dict[str, Any] | None = None,
|
|
80
|
+
parameters: dict[str, Any] | None = None,
|
|
81
|
+
idempotency_key: str | None = None,
|
|
82
|
+
) -> dict[str, Any]:
|
|
83
|
+
key = idempotency_key or str(uuid.uuid4())
|
|
84
|
+
return self._request(
|
|
85
|
+
"POST",
|
|
86
|
+
"/generations",
|
|
87
|
+
{
|
|
88
|
+
"recipeId": recipe_id,
|
|
89
|
+
"formData": form_data or {},
|
|
90
|
+
"parameters": parameters or {},
|
|
91
|
+
},
|
|
92
|
+
extra_headers={"Idempotency-Key": key},
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
def get_generation(self, generation_id: int) -> dict[str, Any]:
|
|
96
|
+
return self._request("GET", f"/generations/{generation_id}")
|
|
97
|
+
|
|
98
|
+
def list_generations(
|
|
99
|
+
self,
|
|
100
|
+
page: int = 1,
|
|
101
|
+
limit: int = 20,
|
|
102
|
+
status: str | None = None,
|
|
103
|
+
) -> dict[str, Any]:
|
|
104
|
+
query = f"page={page}&limit={limit}"
|
|
105
|
+
if status:
|
|
106
|
+
query += f"&status={status}"
|
|
107
|
+
return self._request("GET", f"/generations?{query}")
|
|
108
|
+
|
|
109
|
+
def wait_for_generation(
|
|
110
|
+
self,
|
|
111
|
+
generation_id: int,
|
|
112
|
+
interval_s: float = 3.0,
|
|
113
|
+
timeout_s: float = 600.0,
|
|
114
|
+
) -> dict[str, Any]:
|
|
115
|
+
started = time.time()
|
|
116
|
+
while True:
|
|
117
|
+
gen = self.get_generation(generation_id)
|
|
118
|
+
if gen.get("status") in ("completed", "failed"):
|
|
119
|
+
return gen
|
|
120
|
+
if time.time() - started > timeout_s:
|
|
121
|
+
raise DelulaApiError(408, "internal_error", f"Timed out waiting for generation {generation_id}")
|
|
122
|
+
time.sleep(interval_s)
|
delula_sdk/errors.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class DelulaApiError(Exception):
|
|
7
|
+
def __init__(
|
|
8
|
+
self,
|
|
9
|
+
status: int,
|
|
10
|
+
code: str,
|
|
11
|
+
message: str,
|
|
12
|
+
details: dict[str, Any] | None = None,
|
|
13
|
+
) -> None:
|
|
14
|
+
super().__init__(message)
|
|
15
|
+
self.status = status
|
|
16
|
+
self.code = code
|
|
17
|
+
self.details = details or {}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: delula-sdk
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Delula Public API SDK (beta)
|
|
5
|
+
Author: Delula
|
|
6
|
+
License: Proprietary
|
|
7
|
+
Project-URL: Homepage, https://delu.la
|
|
8
|
+
Project-URL: Repository, https://github.com/tcotten-scrypted/MagicVidCreator
|
|
9
|
+
Project-URL: Issues, https://github.com/tcotten-scrypted/MagicVidCreator/issues
|
|
10
|
+
Keywords: delula,api,generative-ai,video,image
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
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: Typing :: Typed
|
|
19
|
+
Requires-Python: >=3.10
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
|
|
22
|
+
# delula-sdk
|
|
23
|
+
|
|
24
|
+
Python client for the [Delula Public API](https://delu.la) (beta).
|
|
25
|
+
|
|
26
|
+
## Install
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
pip install delula-sdk
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Requires Python **3.10+**. No third-party dependencies (stdlib only).
|
|
33
|
+
|
|
34
|
+
## Quick start
|
|
35
|
+
|
|
36
|
+
Create an API key in Delula → **Account → API**, then:
|
|
37
|
+
|
|
38
|
+
```python
|
|
39
|
+
import uuid
|
|
40
|
+
from delula_sdk import DelulaClient
|
|
41
|
+
|
|
42
|
+
client = DelulaClient(api_key="dlu_...")
|
|
43
|
+
|
|
44
|
+
credits = client.get_credits()
|
|
45
|
+
recipes = client.list_recipes()["recipes"]
|
|
46
|
+
preflight = client.preflight_generation(recipes[0]["id"], form_data={})
|
|
47
|
+
gen = client.create_generation(
|
|
48
|
+
recipes[0]["id"],
|
|
49
|
+
form_data={},
|
|
50
|
+
idempotency_key=str(uuid.uuid4()),
|
|
51
|
+
)
|
|
52
|
+
result = client.wait_for_generation(gen["generationId"])
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Default base URL: `https://delu.la/public/api`. Pass `base_url=` for local dev.
|
|
56
|
+
|
|
57
|
+
## Beta
|
|
58
|
+
|
|
59
|
+
Breaking changes may ship in `0.x` without a `/v1` path bump. Pin your version in production.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
delula_sdk/__init__.py,sha256=FFyZ_rrKyi5CTUg3C7a5af0E29Hni1bJIYYKkaICkgo,171
|
|
2
|
+
delula_sdk/client.py,sha256=1XFi__ihBMRPEELWBTzwtHp52vkAKUg9c5V5qcpZFH4,4051
|
|
3
|
+
delula_sdk/errors.py,sha256=H1rm3oNOZsYEDQwLriPKWrJ-C46Ad04epZ5EOOjbUZI,375
|
|
4
|
+
delula_sdk-0.1.0.dist-info/METADATA,sha256=kTC2GjwjGDwW1X0cWADq1k15EGd5I1zmqnhprQDpCDI,1686
|
|
5
|
+
delula_sdk-0.1.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
6
|
+
delula_sdk-0.1.0.dist-info/top_level.txt,sha256=_J90_biR1-AKkIJApQ4Qu9tF7BmD4vPgIZnsnkKDgKY,11
|
|
7
|
+
delula_sdk-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
delula_sdk
|