llamagen-python 0.1.12__tar.gz → 0.1.14__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: llamagen-python
3
- Version: 0.1.12
3
+ Version: 0.1.14
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
@@ -66,8 +66,6 @@ WAIT_TIMEOUT_MS = 30 * 60 * 1000
66
66
  client = LlamaGenClient(
67
67
  api_key=os.environ["LLAMAGEN_API_KEY"],
68
68
  timeout_ms=30000,
69
- max_retries=5,
70
- retry_delay_ms=500,
71
69
  )
72
70
 
73
71
  created = client.comic.create({
@@ -95,13 +93,13 @@ client = LlamaGenClient(
95
93
  api_key="YOUR_API_KEY",
96
94
  base_url="https://api.llamagen.ai/v1",
97
95
  timeout_ms=30000,
98
- max_retries=5,
99
- retry_delay_ms=500,
100
96
  )
101
97
  ```
102
98
 
103
99
  The SDK uses Python's standard library HTTP stack and does not require runtime
104
- dependencies.
100
+ dependencies. The legacy `max_retries` and `retry_delay_ms` options remain
101
+ accepted for compatibility, but automatic HTTP retries are disabled to avoid
102
+ duplicating billable generation requests.
105
103
 
106
104
  ## Comic API
107
105
 
@@ -125,11 +123,16 @@ generation = client.comic.create({
125
123
  "comicRoles": [
126
124
  {
127
125
  "name": "The Little Prince",
128
- "image": "https://example.com/prince.png",
126
+ "image": "https://s.llamagen.ai/public/61965b97-23fe-4405-853d-efe17b5d6f08-prince.webp",
129
127
  "dress": "green coat and yellow scarf",
130
128
  }
131
129
  ],
132
- "attachments": [{"type": "image", "url": "https://example.com/reference.png"}],
130
+ "attachments": [
131
+ {
132
+ "type": "image",
133
+ "url": "https://s.llamagen.ai/public/7eeaad2c-fd6b-4679-90ce-884503f011b7-reference.webp",
134
+ }
135
+ ],
133
136
  "language": "en",
134
137
  "upscale": "2K",
135
138
  })
@@ -415,5 +418,5 @@ except LlamaGenTimeoutError as error:
415
418
  ```
416
419
 
417
420
  - `LlamaGenAPIError`: the API returned a non-2xx response, such as `401`, `403`, or `429`.
418
- - `LlamaGenConnectionError`: the SDK could not connect to the API after retries.
419
- - `LlamaGenTimeoutError`: the request timed out after retries.
421
+ - `LlamaGenConnectionError`: the SDK could not connect to the API.
422
+ - `LlamaGenTimeoutError`: the request timed out.
@@ -41,8 +41,6 @@ WAIT_TIMEOUT_MS = 30 * 60 * 1000
41
41
  client = LlamaGenClient(
42
42
  api_key=os.environ["LLAMAGEN_API_KEY"],
43
43
  timeout_ms=30000,
44
- max_retries=5,
45
- retry_delay_ms=500,
46
44
  )
47
45
 
48
46
  created = client.comic.create({
@@ -70,13 +68,13 @@ client = LlamaGenClient(
70
68
  api_key="YOUR_API_KEY",
71
69
  base_url="https://api.llamagen.ai/v1",
72
70
  timeout_ms=30000,
73
- max_retries=5,
74
- retry_delay_ms=500,
75
71
  )
76
72
  ```
77
73
 
78
74
  The SDK uses Python's standard library HTTP stack and does not require runtime
79
- dependencies.
75
+ dependencies. The legacy `max_retries` and `retry_delay_ms` options remain
76
+ accepted for compatibility, but automatic HTTP retries are disabled to avoid
77
+ duplicating billable generation requests.
80
78
 
81
79
  ## Comic API
82
80
 
@@ -100,11 +98,16 @@ generation = client.comic.create({
100
98
  "comicRoles": [
101
99
  {
102
100
  "name": "The Little Prince",
103
- "image": "https://example.com/prince.png",
101
+ "image": "https://s.llamagen.ai/public/61965b97-23fe-4405-853d-efe17b5d6f08-prince.webp",
104
102
  "dress": "green coat and yellow scarf",
105
103
  }
106
104
  ],
107
- "attachments": [{"type": "image", "url": "https://example.com/reference.png"}],
105
+ "attachments": [
106
+ {
107
+ "type": "image",
108
+ "url": "https://s.llamagen.ai/public/7eeaad2c-fd6b-4679-90ce-884503f011b7-reference.webp",
109
+ }
110
+ ],
108
111
  "language": "en",
109
112
  "upscale": "2K",
110
113
  })
@@ -390,5 +393,5 @@ except LlamaGenTimeoutError as error:
390
393
  ```
391
394
 
392
395
  - `LlamaGenAPIError`: the API returned a non-2xx response, such as `401`, `403`, or `429`.
393
- - `LlamaGenConnectionError`: the SDK could not connect to the API after retries.
394
- - `LlamaGenTimeoutError`: the request timed out after retries.
396
+ - `LlamaGenConnectionError`: the SDK could not connect to the API.
397
+ - `LlamaGenTimeoutError`: the request timed out.
@@ -71,11 +71,16 @@ def create_comic_with_advanced_options(client: LlamaGenClient) -> None:
71
71
  "comicRoles": [
72
72
  {
73
73
  "name": "The Little Prince",
74
- "image": "https://example.com/prince.png",
74
+ "image": "https://s.llamagen.ai/public/61965b97-23fe-4405-853d-efe17b5d6f08-prince.webp",
75
75
  "dress": "green coat and yellow scarf",
76
76
  }
77
77
  ],
78
- "attachments": [{"type": "image", "url": "https://example.com/reference.png"}],
78
+ "attachments": [
79
+ {
80
+ "type": "image",
81
+ "url": "https://s.llamagen.ai/public/7eeaad2c-fd6b-4679-90ce-884503f011b7-reference.webp",
82
+ }
83
+ ],
79
84
  "language": "en",
80
85
  "upscale": "2K",
81
86
  }
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "llamagen-python"
7
- version = "0.1.12"
7
+ version = "0.1.14"
8
8
  description = "Official Python SDK for LlamaGen Comic API and Animation API"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -14,4 +14,4 @@ __all__ = [
14
14
  "SUPPORTED_COMIC_SIZES",
15
15
  ]
16
16
 
17
- __version__ = "0.1.12"
17
+ __version__ = "0.1.14"
@@ -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.12"
13
+ DEFAULT_USER_AGENT = "llamagen-python/0.1.14"
14
14
 
15
15
 
16
16
  class Transport(Protocol):
@@ -60,8 +60,10 @@ class HTTPClient:
60
60
  self.api_key = api_key
61
61
  self.base_url = base_url.rstrip("/")
62
62
  self.timeout = timeout_ms / 1000
63
- self.max_retries = max_retries
64
- self.retry_delay = retry_delay_ms / 1000
63
+ # Kept in the public API for compatibility; automatic retries can
64
+ # duplicate non-idempotent, billable generation requests.
65
+ self.max_retries = 0
66
+ self.retry_delay = 60
65
67
  self.transport = transport or UrllibTransport()
66
68
 
67
69
  def request(
@@ -1,12 +1,13 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import json
4
+ import socket
4
5
  import ssl
5
6
  import unittest
6
7
  import urllib.error
7
8
  from typing import Mapping, Optional, Tuple
8
9
 
9
- from llamagen import LlamaGenAPIError, LlamaGenClient, LlamaGenConnectionError
10
+ from llamagen import LlamaGenAPIError, LlamaGenClient, LlamaGenConnectionError, LlamaGenTimeoutError
10
11
  from llamagen.resources.animations import DEFAULT_WAIT_TIMEOUT_MS as ANIMATION_WAIT_TIMEOUT_MS
11
12
  from llamagen.resources.comics import DEFAULT_WAIT_TIMEOUT_MS as COMIC_WAIT_TIMEOUT_MS
12
13
 
@@ -80,7 +81,7 @@ class ClientTests(unittest.TestCase):
80
81
  self.assertEqual(call["url"], "https://api.llamagen.ai/v1/comics/generations")
81
82
  self.assertEqual(call["headers"]["Authorization"], "Bearer test-key")
82
83
  self.assertEqual(call["headers"]["Accept"], "application/json")
83
- self.assertEqual(call["headers"]["User-Agent"], "llamagen-python/0.1.12")
84
+ self.assertEqual(call["headers"]["User-Agent"], "llamagen-python/0.1.14")
84
85
  body = json.loads(call["body"].decode("utf-8"))
85
86
  self.assertEqual(body["preset"], "neutral")
86
87
  self.assertEqual(body["size"], "1024x1024")
@@ -157,20 +158,33 @@ class ClientTests(unittest.TestCase):
157
158
  body = json.loads(transport.calls[0]["body"].decode("utf-8"))
158
159
  self.assertEqual(body["action"], "regeneratePanel")
159
160
 
160
- def test_retries_ssl_url_errors_and_raises_connection_error(self):
161
- transport = FailingTransport(
162
- [
163
- urllib.error.URLError(ssl.SSLEOFError("unexpected eof")),
164
- urllib.error.URLError(ssl.SSLEOFError("unexpected eof")),
165
- urllib.error.URLError(ssl.SSLEOFError("unexpected eof")),
166
- ]
161
+ def test_max_retries_is_ignored_for_connection_errors(self):
162
+ transport = FailingTransport([urllib.error.URLError(ssl.SSLEOFError("unexpected eof"))])
163
+ client = LlamaGenClient(
164
+ api_key="test-key",
165
+ max_retries=5,
166
+ retry_delay_ms=1,
167
+ transport=transport,
167
168
  )
168
- client = LlamaGenClient(api_key="test-key", retry_delay_ms=1, transport=transport)
169
169
 
170
170
  with self.assertRaises(LlamaGenConnectionError):
171
171
  client.comic.usage()
172
172
 
173
- self.assertEqual(len(transport.calls), 3)
173
+ self.assertEqual(len(transport.calls), 1)
174
+
175
+ def test_max_retries_is_ignored_for_create_timeouts(self):
176
+ transport = FailingTransport([socket.timeout("timed out")])
177
+ client = LlamaGenClient(
178
+ api_key="test-key",
179
+ max_retries=5,
180
+ retry_delay_ms=1,
181
+ transport=transport,
182
+ )
183
+
184
+ with self.assertRaises(LlamaGenTimeoutError):
185
+ client.comic.create({"prompt": "A short comic.", "fixPanelNum": 1})
186
+
187
+ self.assertEqual(len(transport.calls), 1)
174
188
 
175
189
  def test_wait_for_completion_continues_after_transient_connection_error(self):
176
190
  transport = MixedTransport(
@@ -1,96 +0,0 @@
1
- {
2
- "cells": [
3
- {
4
- "cell_type": "code",
5
- "execution_count": 1,
6
- "id": "1a448d8d-251a-4351-a830-0372b055fb65",
7
- "metadata": {},
8
- "outputs": [
9
- {
10
- "name": "stdout",
11
- "output_type": "stream",
12
- "text": [
13
- "comic.create: cmrfqxwsu0003l404hr39nmhm LOADING\n",
14
- "comic.wait_for_completion: cmrfqxwsu0003l404hr39nmhm PROCESSED [{'page': 0, 'prompt': 'A 4-panel comic about Leo finding a glowing key in a quiet library.', 'beginPageIndex': 0, 'totalPages': 1, 'panelsPerPage': 4, 'layout': 'Layout0', 'panels': [{'assetUrl': 'https://s.llamagen.ai/clr907a8t0000jp08cgdqv78h/f521f8e4-abcd-4e1f-9e42-48f6b7a8715c.png', 'panel': 0, 'caption': 'Leo quietly browses the library shelves, lost in the peaceful atmosphere.', 'image': []}, {'assetUrl': 'https://s.llamagen.ai/clr907a8t0000jp08cgdqv78h/72b6b5f4-05bb-499e-9528-0c5b418b5eba.png', 'panel': 1, 'caption': 'Leo notices a faint glow beneath one of the lower shelves.', 'image': []}, {'assetUrl': 'https://s.llamagen.ai/clr907a8t0000jp08cgdqv78h/62e36e8c-0a74-4bc8-a44a-c3bbf9f880c7.png', 'panel': 2, 'caption': 'Leo carefully reaches under the shelf and retrieves the glowing key.', 'image': []}, {'assetUrl': 'https://s.llamagen.ai/clr907a8t0000jp08cgdqv78h/7ed32fee-379f-4f48-9880-1c06739ee240.png', 'panel': 3, 'caption': 'Leo gazes at the glowing key, his eyes full of wonder and imagination.', 'image': []}], 'update': 1783737031958, 'firstUpdate': 1783737031958}]\n"
15
- ]
16
- }
17
- ],
18
- "source": [
19
- "import os\n",
20
- "\n",
21
- "from llamagen import LlamaGenClient\n",
22
- "\n",
23
- "WAIT_TIMEOUT_MS = 30 * 60 * 1000\n",
24
- "\n",
25
- "client = LlamaGenClient(\n",
26
- " api_key=\"sk-5d609cd64379d048edcb04dcc109a6cfaf7c066f12138c13c8c404766ab9629d\",\n",
27
- " timeout_ms=30000,\n",
28
- " max_retries=5,\n",
29
- " retry_delay_ms=500,\n",
30
- ")\n",
31
- "\n",
32
- "created = client.comic.create({\n",
33
- " \"prompt\": \"A 4-panel comic about Leo finding a glowing key in a quiet library.\",\n",
34
- " \"style\": \"manga\",\n",
35
- " \"fixPanelNum\": 4,\n",
36
- "})\n",
37
- "print(\"comic.create:\", created[\"id\"], created.get(\"status\"))\n",
38
- "\n",
39
- "done = client.comic.wait_for_completion(created[\"id\"], timeout_ms=WAIT_TIMEOUT_MS)\n",
40
- "print(\"comic.wait_for_completion:\", done[\"id\"], done.get(\"status\"), done.get(\"comics\"))"
41
- ]
42
- },
43
- {
44
- "cell_type": "code",
45
- "execution_count": null,
46
- "id": "f0c6df20-22f8-410e-9920-691f2d9b6557",
47
- "metadata": {},
48
- "outputs": [],
49
- "source": [
50
- "import os\n",
51
- "\n",
52
- "from llamagen import LlamaGenClient\n",
53
- "\n",
54
- "client = LlamaGenClient(api_key=\"sk-5d609cd64379d048edcb04dcc109a6cfaf7c066f12138c13c8c404766ab9629d\")\n",
55
- "\n",
56
- "generation = client.comic.create({\n",
57
- " \"prompt\": \"The Little Prince meets the Fox in a luminous desert.\",\n",
58
- " \"style\": \"storybook manga\",\n",
59
- " \"size\": \"1024x1024\",\n",
60
- " \"pagination\": {\"totalPages\": 2, \"panelsPerPage\": 4},\n",
61
- " \"comicRoles\": [\n",
62
- " {\n",
63
- " \"name\": \"The Little Prince\",\n",
64
- " \"image\": \"https://example.com/prince.png\",\n",
65
- " \"clothing\": \"green coat and yellow scarf\",\n",
66
- " }\n",
67
- " ],\n",
68
- " \"attachments\": [{\"type\": \"image\", \"url\": \"https://example.com/reference.png\"}],\n",
69
- " \"language\": \"en\",\n",
70
- " \"upscale\": \"2K\",\n",
71
- "})"
72
- ]
73
- }
74
- ],
75
- "metadata": {
76
- "kernelspec": {
77
- "display_name": "Python 3 (ipykernel)",
78
- "language": "python",
79
- "name": "python3"
80
- },
81
- "language_info": {
82
- "codemirror_mode": {
83
- "name": "ipython",
84
- "version": 3
85
- },
86
- "file_extension": ".py",
87
- "mimetype": "text/x-python",
88
- "name": "python",
89
- "nbconvert_exporter": "python",
90
- "pygments_lexer": "ipython3",
91
- "version": "3.12.7"
92
- }
93
- },
94
- "nbformat": 4,
95
- "nbformat_minor": 5
96
- }