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 ADDED
@@ -0,0 +1,6 @@
1
+ """Delula Public API SDK (beta)."""
2
+
3
+ from delula_sdk.client import DelulaClient
4
+ from delula_sdk.errors import DelulaApiError
5
+
6
+ __all__ = ["DelulaClient", "DelulaApiError"]
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,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ delula_sdk