Metadata-Version: 2.4
Name: llamagen-python
Version: 0.1.6
Summary: Official Python SDK for LlamaGen Comic API and Animation API
Project-URL: Homepage, https://llamagen.ai/comic-api
Project-URL: Documentation, https://llamagen.ai/comic-api/docs
Project-URL: Source, https://github.com/LlamaGenAI/llamagen-python
Project-URL: Issues, https://github.com/LlamaGenAI/llamagen-python/issues
Author-email: "llamagen.ai" <support@llamagen.ai>
License-Expression: MIT
License-File: LICENSE
Keywords: ai,ai comic api,animation,comic,image-to-video,llamagen,sdk,text-to-video
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.9
Description-Content-Type: text/markdown

# llamagen-python

Official Python SDK for LlamaGen's Comic API and Animation API.

Homepage: <https://llamagen.ai/comic-api>

## Install

```bash
pip install llamagen-python
```

## Quick Start

```python
import os
from pathlib import Path

from llamagen import LlamaGenClient

WAIT_TIMEOUT_MS = 30 * 60 * 1000

client = LlamaGenClient(
    api_key=os.environ["LLAMAGEN_API_KEY"],
    timeout_ms=30000,
    max_retries=5,
    retry_delay_ms=500,
)

created = client.comic.create({
    "prompt": "A 4-panel comic about Leo finding a glowing key in a quiet library.",
    "style": "manga",
    "fixPanelNum": 4,
})
print("comic.create:", created["id"], created.get("status"))

done = client.comic.wait_for_completion(created["id"], timeout_ms=WAIT_TIMEOUT_MS)
print("comic.wait_for_completion:", done["id"], done.get("status"), done.get("comics"))
```

The full runnable demo is in [`examples/quickstart.py`](examples/quickstart.py).
It loads `LLAMAGEN_API_KEY` from the environment or a local `.env` file.
Set `LLAMAGEN_UPLOAD_FILE=/path/to/reference.png` to run the upload demo, and
set `LLAMAGEN_RUN_OPTIONAL_DEMOS=1` to run extra paid generation examples.

## Client

```python
client = LlamaGenClient(
    api_key="YOUR_API_KEY",
    base_url="https://api.llamagen.ai/v1",
    timeout_ms=30000,
    max_retries=5,
    retry_delay_ms=500,
)
```

The SDK uses Python's standard library HTTP stack and does not require runtime
dependencies.

## Comic API

Use `llamagen.comic` to create comics, manga, webtoons, storyboards,
consistent-character pages, and panel edits.

```python
generation = client.comic.create({
    "prompt": "The Little Prince meets the Fox in a luminous desert.",
    "style": "storybook manga",
    "size": "1024x1024",
    "pagination": {"totalPages": 2, "panelsPerPage": 4},
    "comicRoles": [
        {
            "name": "The Little Prince",
            "image": "https://example.com/prince.png",
            "clothing": "green coat and yellow scarf",
        }
    ],
    "attachments": [{"type": "image", "url": "https://example.com/reference.png"}],
    "language": "en",
    "upscale": "2K",
})
```

Comic methods:

- `llamagen.comic.create(params)` calls `POST /v1/comics/generations`.
- `llamagen.comic.get(id, page=None, panel=None)` calls `GET /v1/comics/generations/{id}`.
- `llamagen.comic.continue_write(id, params)` extends an existing comic.
- `llamagen.comic.update_panel(id, params)` regenerates one panel.
- `llamagen.comic.usage()` calls `GET /v1/comics/usage`.
- `llamagen.comic.upload(file, filename=None)` calls `POST /v1/comics/upload`.
- `llamagen.comic.wait_for_completion(id, **options)` polls until a terminal status.
- `llamagen.comic.create_and_wait(params, **options)` creates and polls.
- `llamagen.comic.create_batch(params_list, concurrency=3)` submits many jobs.
- `llamagen.comic.wait_for_many(ids, concurrency=3)` polls many jobs.

Comic examples:

```python
# Create a comic generation.
created = client.comic.create({
    "prompt": "A 4-panel comic about Leo finding a glowing key in a quiet library.",
    "style": "manga",
    "fixPanelNum": 4,
})
print("comic.create:", created["id"], created.get("status"))

# Fetch the whole generation, or one page/panel.
fetched = client.comic.get(created["id"])
print("comic.get:", fetched["id"], fetched.get("status"))

panel = client.comic.get(created["id"], page=0, panel=0)
print("comic.get panel:", panel.get("id", created["id"]), panel.get("status"))

# Wait until the generation reaches a terminal status.
# The default timeout is 30 minutes.
done = client.comic.wait_for_completion(created["id"], timeout_ms=WAIT_TIMEOUT_MS)
print("comic.wait_for_completion:", done["id"], done.get("status"), done.get("comics"))

# Create and wait in one call.
finished = client.comic.create_and_wait({
    "prompt": "A cozy 3-panel comic about a robot learning to bake bread.",
    "style": "storybook",
    "fixPanelNum": 3,
}, timeout_ms=WAIT_TIMEOUT_MS)
print("comic.create_and_wait:", finished["id"], finished.get("status"))

# Continue an existing comic.
continued = client.comic.continue_write(
    created["id"],
    {
        "prompt": "Continue the story with Leo opening a secret reading room.",
        "pagination": {"totalPages": 1, "panelsPerPage": 4},
    },
)
print("comic.continue_write:", continued["id"], continued.get("status"))

# Regenerate one panel.
updated = client.comic.update_panel(
    created["id"],
    {
        "panelIndex": 0,
        "prompt": "Make the glowing key brighter and add dramatic moonlight.",
    },
)
print("comic.update_panel:", updated["id"], updated.get("status"))

# Check usage.
usage = client.comic.usage()
print("comic.usage:", usage)

# Upload a local reference image.
upload_file = os.environ.get("LLAMAGEN_UPLOAD_FILE")
if upload_file:
    uploaded = client.comic.upload(Path(upload_file))
    print("comic.upload:", uploaded)

# Submit many jobs and wait for many jobs.
batch = client.comic.create_batch(
    [
        {
            "prompt": "A 2-panel comic about a cloud trying on hats.",
            "style": "cartoon",
            "fixPanelNum": 2,
        },
        {
            "prompt": "A 2-panel comic about a tiny train crossing a desk.",
            "style": "manga",
            "fixPanelNum": 2,
        },
    ],
    concurrency=2,
)
ids = [item["result"]["id"] for item in batch if "result" in item]
print("comic.create_batch:", ids)

finished_many = client.comic.wait_for_many(ids, concurrency=2, timeout_ms=WAIT_TIMEOUT_MS)
print("comic.wait_for_many:", [(item["id"], item.get("status")) for item in finished_many])
```

JavaScript-style aliases are also available: `createComic`, `getComic`,
`continueComic`, `regeneratePanel`, `updateComicPanel`, `waitForCompletion`,
`createAndWait`, and `waitForMany`.

Alias examples:

```python
created = client.comic.createComic({
    "prompt": "A 4-panel comic about a lighthouse keeper finding a map.",
    "fixPanelNum": 4,
})
print("comic.createComic:", created["id"], created.get("status"))

fetched = client.comic.getComic(created["id"])
print("comic.getComic:", fetched["id"], fetched.get("status"))

done = client.comic.waitForCompletion(created["id"], timeout_ms=WAIT_TIMEOUT_MS)
print("comic.waitForCompletion:", done["id"], done.get("status"))
```

## Animation API

```python
video = client.animation.create({
    "prompt": "A heroic fox detective walks through neon rain, cinematic camera move.",
    "videoOptions": {
        "duration": 5,
        "resolution": "720p",
        "aspect_ratio": "16:9",
    },
})

finished = client.animation.wait_for_completion(
    video["id"],
    interval_ms=5000,
    timeout_ms=30 * 60 * 1000,
)
```

Animation methods:

- `llamagen.animation.create(params)` calls `POST /v1/artworks/generations`.
- `llamagen.animation.get(id)` calls `GET /v1/artworks/generations/{id}`.
- `llamagen.animation.wait_for_completion(id, **options)` polls video status.
- `llamagen.animation.create_and_wait(params, **options)` creates and polls.

`llamagen.animations` is an alias for `llamagen.animation`.

Animation examples:

```python
# Create an animation job.
video = client.animation.create({
    "prompt": "A heroic fox detective walks through neon rain, cinematic camera move.",
    "videoOptions": {
        "duration": 5,
        "resolution": "720p",
        "aspect_ratio": "16:9",
    },
})
print("animation.create:", video["id"], video.get("status"))

# Fetch and wait.
fetched = client.animation.get(video["id"])
print("animation.get:", fetched["id"], fetched.get("status"))

finished = client.animation.wait_for_completion(video["id"], timeout_ms=WAIT_TIMEOUT_MS)
print("animation.wait_for_completion:", finished["id"], finished.get("status"))

# Create and wait in one call.
finished = client.animation.create_and_wait({
    "prompt": "A paper boat sailing across a watercolor city at sunrise.",
    "videoOptions": {
        "duration": 5,
        "resolution": "720p",
        "aspect_ratio": "16:9",
    },
}, timeout_ms=WAIT_TIMEOUT_MS)
print("animation.create_and_wait:", finished["id"], finished.get("status"))

# Alias form.
same_result = client.animations.waitForCompletion(video["id"], timeout_ms=WAIT_TIMEOUT_MS)
print("animations.waitForCompletion:", same_result["id"], same_result.get("status"))
```

## Webhooks

```python
import hashlib
import hmac
import json
import time

from llamagen import construct_webhook_event, verify_webhook_signature

payload = json.dumps({"type": "comic.completed", "data": {"id": "cm_demo"}}, separators=(",", ":"))
secret = "whsec_demo"
timestamp = str(int(time.time()))
signature = hmac.new(
    secret.encode("utf-8"),
    f"{timestamp}.{payload}".encode("utf-8"),
    hashlib.sha256,
).hexdigest()
headers = {
    "X-Llama-Webhook-Timestamp": timestamp,
    "X-Llama-Webhook-Signature": f"v1={signature}",
}

is_valid = verify_webhook_signature(
    payload=payload,
    headers=headers,
    secret=secret,
)
event = construct_webhook_event(
    payload=payload,
    headers=headers,
    secret=secret,
)
print("verify_webhook_signature:", is_valid)
print("construct_webhook_event:", event["type"], event["data"]["id"])
```

The helper verifies `X-Llama-Webhook-Timestamp` and
`X-Llama-Webhook-Signature` with HMAC SHA-256 and a default 5-minute tolerance.

## Errors

```python
from llamagen import LlamaGenAPIError, LlamaGenConnectionError, LlamaGenTimeoutError

try:
    usage = client.comic.usage()
except LlamaGenAPIError as error:
    print(error.status, error.data)
except LlamaGenConnectionError as error:
    print("Network connection failed:", error)
except LlamaGenTimeoutError as error:
    print("Request timed out:", error)
```

- `LlamaGenAPIError`: the API returned a non-2xx response, such as `401`, `403`, or `429`.
- `LlamaGenConnectionError`: the SDK could not connect to the API after retries.
- `LlamaGenTimeoutError`: the request timed out after retries.
