llamagen-python 0.1.3__tar.gz → 0.1.5__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.
- {llamagen_python-0.1.3 → llamagen_python-0.1.5}/.gitignore +1 -0
- {llamagen_python-0.1.3 → llamagen_python-0.1.5}/PKG-INFO +1 -1
- llamagen_python-0.1.5/examples/quickstart.py +40 -0
- {llamagen_python-0.1.3 → llamagen_python-0.1.5}/pyproject.toml +1 -1
- {llamagen_python-0.1.3 → llamagen_python-0.1.5}/src/llamagen/__init__.py +1 -1
- {llamagen_python-0.1.3 → llamagen_python-0.1.5}/src/llamagen/_http.py +6 -2
- {llamagen_python-0.1.3 → llamagen_python-0.1.5}/src/llamagen/resources/animations.py +9 -2
- {llamagen_python-0.1.3 → llamagen_python-0.1.5}/src/llamagen/resources/comics.py +9 -2
- {llamagen_python-0.1.3 → llamagen_python-0.1.5}/tests/test_client.py +36 -1
- llamagen_python-0.1.3/examples/quickstart.py +0 -23
- {llamagen_python-0.1.3 → llamagen_python-0.1.5}/LICENSE +0 -0
- {llamagen_python-0.1.3 → llamagen_python-0.1.5}/README.md +0 -0
- {llamagen_python-0.1.3 → llamagen_python-0.1.5}/docs/RELEASE.md +0 -0
- {llamagen_python-0.1.3 → llamagen_python-0.1.5}/src/llamagen/_client.py +0 -0
- {llamagen_python-0.1.3 → llamagen_python-0.1.5}/src/llamagen/_errors.py +0 -0
- {llamagen_python-0.1.3 → llamagen_python-0.1.5}/src/llamagen/_types.py +0 -0
- {llamagen_python-0.1.3 → llamagen_python-0.1.5}/src/llamagen/_webhooks.py +0 -0
- {llamagen_python-0.1.3 → llamagen_python-0.1.5}/src/llamagen/py.typed +0 -0
- {llamagen_python-0.1.3 → llamagen_python-0.1.5}/src/llamagen/resources/__init__.py +0 -0
- {llamagen_python-0.1.3 → llamagen_python-0.1.5}/tests/test_webhooks.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: llamagen-python
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.5
|
|
4
4
|
Summary: Official Python SDK for LlamaGen Comic API and Animation API
|
|
5
5
|
Project-URL: Homepage, https://llamagen.ai/comic-api
|
|
6
6
|
Project-URL: Documentation, https://llamagen.ai/comic-api/docs
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
from llamagen import LlamaGenAPIError, LlamaGenConnectionError, LlamaGenClient, LlamaGenTimeoutError
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def load_dotenv(path: Path) -> None:
|
|
8
|
+
if not path.exists():
|
|
9
|
+
return
|
|
10
|
+
|
|
11
|
+
for raw_line in path.read_text(encoding="utf-8").splitlines():
|
|
12
|
+
line = raw_line.strip()
|
|
13
|
+
if not line or line.startswith("#") or "=" not in line:
|
|
14
|
+
continue
|
|
15
|
+
key, value = line.split("=", 1)
|
|
16
|
+
key = key.strip()
|
|
17
|
+
value = value.strip().strip('"').strip("'")
|
|
18
|
+
os.environ.setdefault(key, value)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
load_dotenv(Path(__file__).resolve().parents[1] / ".env")
|
|
22
|
+
client = LlamaGenClient(api_key=os.environ["LLAMAGEN_API_KEY"], max_retries=5)
|
|
23
|
+
|
|
24
|
+
try:
|
|
25
|
+
created = client.comic.create(
|
|
26
|
+
{
|
|
27
|
+
"prompt": "A 4-panel comic about Leo finding a glowing key in a quiet library.",
|
|
28
|
+
"style": "manga",
|
|
29
|
+
"fixPanelNum": 4,
|
|
30
|
+
}
|
|
31
|
+
)
|
|
32
|
+
print("created:", created["id"], created.get("status"))
|
|
33
|
+
done = client.comic.wait_for_completion(created["id"])
|
|
34
|
+
print("done:", done["status"])
|
|
35
|
+
except LlamaGenAPIError as error:
|
|
36
|
+
print("LlamaGen API error:", error.status, error.data)
|
|
37
|
+
except LlamaGenConnectionError as error:
|
|
38
|
+
print("LlamaGen connection error:", error)
|
|
39
|
+
except LlamaGenTimeoutError as error:
|
|
40
|
+
print("LlamaGen timeout:", error)
|
|
@@ -10,7 +10,7 @@ from typing import Any, Dict, Mapping, Optional, Protocol, Tuple
|
|
|
10
10
|
|
|
11
11
|
from ._errors import LlamaGenAPIError, LlamaGenConnectionError, LlamaGenTimeoutError
|
|
12
12
|
|
|
13
|
-
DEFAULT_USER_AGENT = "llamagen-python/0.1.
|
|
13
|
+
DEFAULT_USER_AGENT = "llamagen-python/0.1.5"
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
class Transport(Protocol):
|
|
@@ -26,6 +26,10 @@ class Transport(Protocol):
|
|
|
26
26
|
|
|
27
27
|
|
|
28
28
|
class UrllibTransport:
|
|
29
|
+
def __init__(self) -> None:
|
|
30
|
+
self.context = ssl.create_default_context()
|
|
31
|
+
self.context.minimum_version = ssl.TLSVersion.TLSv1_2
|
|
32
|
+
|
|
29
33
|
def request(
|
|
30
34
|
self,
|
|
31
35
|
method: str,
|
|
@@ -36,7 +40,7 @@ class UrllibTransport:
|
|
|
36
40
|
) -> Tuple[int, Mapping[str, str], bytes]:
|
|
37
41
|
request = urllib.request.Request(url, data=body, method=method, headers=dict(headers))
|
|
38
42
|
try:
|
|
39
|
-
with urllib.request.urlopen(request, timeout=timeout) as response:
|
|
43
|
+
with urllib.request.urlopen(request, timeout=timeout, context=self.context) as response:
|
|
40
44
|
return response.status, dict(response.headers.items()), response.read()
|
|
41
45
|
except urllib.error.HTTPError as error:
|
|
42
46
|
return error.code, dict(error.headers.items()), error.read()
|
|
@@ -3,7 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
import time
|
|
4
4
|
from typing import Mapping, Optional, Sequence
|
|
5
5
|
|
|
6
|
-
from .._errors import LlamaGenTimeoutError
|
|
6
|
+
from .._errors import LlamaGenConnectionError, LlamaGenTimeoutError
|
|
7
7
|
from .._http import HTTPClient
|
|
8
8
|
from .._types import AnimationArtworkResponse
|
|
9
9
|
|
|
@@ -34,7 +34,14 @@ class AnimationsResource:
|
|
|
34
34
|
start = time.monotonic()
|
|
35
35
|
|
|
36
36
|
while True:
|
|
37
|
-
|
|
37
|
+
try:
|
|
38
|
+
result = self.get(artwork_id)
|
|
39
|
+
except (LlamaGenConnectionError, LlamaGenTimeoutError):
|
|
40
|
+
if (time.monotonic() - start) * 1000 >= timeout_ms:
|
|
41
|
+
raise
|
|
42
|
+
time.sleep(interval_ms / 1000)
|
|
43
|
+
continue
|
|
44
|
+
|
|
38
45
|
if str(result.get("status", "")).upper() in done:
|
|
39
46
|
return result
|
|
40
47
|
|
|
@@ -9,7 +9,7 @@ from pathlib import Path
|
|
|
9
9
|
from typing import Any, BinaryIO, Dict, Iterable, List, Mapping, Optional, Sequence, Union
|
|
10
10
|
from urllib.parse import urlencode
|
|
11
11
|
|
|
12
|
-
from .._errors import LlamaGenTimeoutError
|
|
12
|
+
from .._errors import LlamaGenConnectionError, LlamaGenTimeoutError
|
|
13
13
|
from .._http import HTTPClient
|
|
14
14
|
from .._types import ComicArtworkResponse, ComicUploadResponse, ComicUsage, SUPPORTED_COMIC_SIZES
|
|
15
15
|
|
|
@@ -99,7 +99,14 @@ class ComicsResource:
|
|
|
99
99
|
start = time.monotonic()
|
|
100
100
|
|
|
101
101
|
while True:
|
|
102
|
-
|
|
102
|
+
try:
|
|
103
|
+
result = self.get(artwork_id)
|
|
104
|
+
except (LlamaGenConnectionError, LlamaGenTimeoutError):
|
|
105
|
+
if (time.monotonic() - start) * 1000 >= timeout_ms:
|
|
106
|
+
raise
|
|
107
|
+
time.sleep(interval_ms / 1000)
|
|
108
|
+
continue
|
|
109
|
+
|
|
103
110
|
if str(result.get("status", "")).upper() in done:
|
|
104
111
|
return result
|
|
105
112
|
|
|
@@ -44,6 +44,27 @@ class FailingTransport:
|
|
|
44
44
|
raise self.errors.pop(0)
|
|
45
45
|
|
|
46
46
|
|
|
47
|
+
class MixedTransport:
|
|
48
|
+
def __init__(self, events):
|
|
49
|
+
self.events = list(events)
|
|
50
|
+
self.calls = []
|
|
51
|
+
|
|
52
|
+
def request(
|
|
53
|
+
self,
|
|
54
|
+
method: str,
|
|
55
|
+
url: str,
|
|
56
|
+
headers: Mapping[str, str],
|
|
57
|
+
body: Optional[bytes],
|
|
58
|
+
timeout: float,
|
|
59
|
+
) -> Tuple[int, Mapping[str, str], bytes]:
|
|
60
|
+
self.calls.append({"method": method, "url": url, "headers": dict(headers), "body": body})
|
|
61
|
+
event = self.events.pop(0)
|
|
62
|
+
if isinstance(event, BaseException):
|
|
63
|
+
raise event
|
|
64
|
+
status, payload = event
|
|
65
|
+
return status, {"content-type": "application/json"}, json.dumps(payload).encode("utf-8")
|
|
66
|
+
|
|
67
|
+
|
|
47
68
|
class ClientTests(unittest.TestCase):
|
|
48
69
|
def test_create_comic_applies_defaults_and_auth(self):
|
|
49
70
|
transport = FakeTransport([(200, {"id": "gen_1", "status": "PENDING"})])
|
|
@@ -57,7 +78,7 @@ class ClientTests(unittest.TestCase):
|
|
|
57
78
|
self.assertEqual(call["url"], "https://api.llamagen.ai/v1/comics/generations")
|
|
58
79
|
self.assertEqual(call["headers"]["Authorization"], "Bearer test-key")
|
|
59
80
|
self.assertEqual(call["headers"]["Accept"], "application/json")
|
|
60
|
-
self.assertEqual(call["headers"]["User-Agent"], "llamagen-python/0.1.
|
|
81
|
+
self.assertEqual(call["headers"]["User-Agent"], "llamagen-python/0.1.5")
|
|
61
82
|
body = json.loads(call["body"].decode("utf-8"))
|
|
62
83
|
self.assertEqual(body["preset"], "neutral")
|
|
63
84
|
self.assertEqual(body["size"], "1024x1024")
|
|
@@ -122,6 +143,20 @@ class ClientTests(unittest.TestCase):
|
|
|
122
143
|
|
|
123
144
|
self.assertEqual(len(transport.calls), 3)
|
|
124
145
|
|
|
146
|
+
def test_wait_for_completion_continues_after_transient_connection_error(self):
|
|
147
|
+
transport = MixedTransport(
|
|
148
|
+
[
|
|
149
|
+
urllib.error.URLError(ssl.SSLEOFError("unexpected eof")),
|
|
150
|
+
(200, {"id": "gen_1", "status": "SUCCEEDED"}),
|
|
151
|
+
]
|
|
152
|
+
)
|
|
153
|
+
client = LlamaGenClient(api_key="test-key", max_retries=0, retry_delay_ms=1, transport=transport)
|
|
154
|
+
|
|
155
|
+
result = client.comic.wait_for_completion("gen_1", interval_ms=1, timeout_ms=1000)
|
|
156
|
+
|
|
157
|
+
self.assertEqual(result["status"], "SUCCEEDED")
|
|
158
|
+
self.assertEqual(len(transport.calls), 2)
|
|
159
|
+
|
|
125
160
|
|
|
126
161
|
if __name__ == "__main__":
|
|
127
162
|
unittest.main()
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
|
|
3
|
-
from llamagen import LlamaGenAPIError, LlamaGenConnectionError, LlamaGenClient, LlamaGenTimeoutError
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
client = LlamaGenClient(api_key=os.environ["LLAMAGEN_API_KEY"])
|
|
7
|
-
|
|
8
|
-
try:
|
|
9
|
-
created = client.comic.create(
|
|
10
|
-
{
|
|
11
|
-
"prompt": "A 4-panel comic about Leo finding a glowing key in a quiet library.",
|
|
12
|
-
"style": "manga",
|
|
13
|
-
"fixPanelNum": 4,
|
|
14
|
-
}
|
|
15
|
-
)
|
|
16
|
-
done = client.comic.wait_for_completion(created["id"])
|
|
17
|
-
print(done["status"])
|
|
18
|
-
except LlamaGenAPIError as error:
|
|
19
|
-
print("LlamaGen API error:", error.status, error.data)
|
|
20
|
-
except LlamaGenConnectionError as error:
|
|
21
|
-
print("LlamaGen connection error:", error)
|
|
22
|
-
except LlamaGenTimeoutError as error:
|
|
23
|
-
print("LlamaGen timeout:", error)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|