Metadata-Version: 2.4
Name: drawthings-py
Version: 0.2.0
Summary: Python SDK for automating Draw Things image generation over gRPC.
License-Expression: GPL-3.0-only
License-File: LICENSE
Keywords: draw-things,grpc,image-generation,stable-diffusion,ai,diffusion,drawthings,image-generation,text-to-image,inpainting
Author: Kelly Jerrell
Author-email: kcjerrell@gmail.com
Requires-Python: >=3.11,<4.0
Classifier: Development Status :: 4 - Beta
Classifier: Framework :: AsyncIO
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Typing :: Typed
Classifier: Operating System :: OS Independent
Requires-Dist: betterproto (>=2.0.0b6,<3.0.0)
Requires-Dist: flatbuffers (>=25.12.19,<26.0.0)
Requires-Dist: fpzip (>=1.2.5,<2.0.0)
Requires-Dist: grpclib (>=0.4.9,<0.5.0)
Requires-Dist: numpy (>=2.4.4,<3.0.0)
Requires-Dist: pillow (>=12.2.0,<13.0.0)
Requires-Dist: strictyaml (>=1.7.3,<2.0.0)
Requires-Dist: tqdm (>=4.67.3,<5.0.0)
Project-URL: Homepage, https://github.com/kcjerrell/drawthings-py
Project-URL: Issues, https://github.com/kcjerrell/drawthings-py/issues
Project-URL: Repository, https://github.com/kcjerrell/drawthings-py
Description-Content-Type: text/markdown

<!-- fullWidth: false tocVisible: false tableWrap: true -->
# drawthings-py

`drawthings-py` is a Python SDK for automating image generation with Draw Things. It provides an async client for the Draw Things gRPC service, helpers for building generation requests, reusable model/config presets, image result handling, seed utilities, and filename helpers for scripting repeatable image workflows.

The package is published as `drawthings-py` and imported as `drawthings_py`.

### Installation

Install the package with pip:

```sh
pip install drawthings-py
```

`drawthings-py` requires Python 3.11 or newer.

To generate images, you will also need a running Draw Things service that accepts gRPC requests, such as the [Draw Things](https://drawthings.ai/) app or the [gRPCServerCLI](https://github.com/drawthingsai/draw-things-community). Support for the CLI is not yet released.

To use the [Draw Things](https://drawthings.ai/) app as a gRPC server, go to the advanced settings tab and enable the API server, select gRPC, and enable Transport Layer Security and Response Compression. If you are DT+ subscriber and want to use Cloud Compute, also enable Bridge Mode.

To use the gRPCServerCLI, follow the instructions [here](https://github.com/drawthingsai/draw-things-community#self-host-grpcservercli-from-packaged-binaries).

### Basic Usage

To generate images with drawthings-py, you'll need to:

1. Connect the gRPC client
2. Load a config (either from a preset or custom)
3. Build your request with a `RequestBuilder`
4. Generate the image and save the result

```python
import asyncio
from drawthings_py import DrawThings, Configs, RequestBuilder

async def main():
    async with DrawThings.grpc() as service:
        # Load a community preset (guaranteed to work with bridge mode/DT+)
        config = Configs.from_preset("ernie_image_turbo")

        # Build your image request with prompt and negative prompt
        req = RequestBuilder(config, "An astronaut in a space helmet riding a bucking bronco on an alien planet")

        # Generate the image
        (result,) = await service.generate_image(req)

        # Save the result to a file
        result.to_file("astrorider.png")

asyncio.run(main())
```

### `RequestBuilder`

Regardless of whether you are using the gRPC service or the CLI, you will use a `RequestBuilder` to construct your image generation request. At a minimum, a `RequestBuilder` needs a config.

```python
from drawthings_py import RequestBuilder, Configs

config = Configs.from_preset("flux_2_klein_9b")
req = RequestBuilder(config)
```

You can assign prompts when creating the `RequestBuilder`, or set/change them later with the `prompt()` and `negative_prompt()` methods.

```python
req = RequestBuilder(config, "A firebreathing dragon", "Ugly, boring, unimpressive")
req.prompt("An icebreathing dragon")
req.negative_prompt("Ugly, boring, unimpressive, fire")
```

`RequestBuilder` instances are reusable across requests. You don't need to call a build method; the service will build the appropriate request when you call `generate_image()`.

```python
(result_a,) = await grpc_service.generate_image(req)
(result_b,) = await cli_service.generate_image(req)
```

(Note: the CLI service is coming soon.)

You can update properties on the config through the request's `config` property. See the Configs section for more examples.

```python
req.config["steps"] = 8
```



If you want to change to a different config, for example to alternate between different models, it's easier to use another `RequestBuilder`.

```python
req_alt = RequestBuilder(Configs.from_preset("anima_preview_3"))
```

You can add reference images, control images, or an init image for Img2Img using either a file path or an `ImageBuffer` returned from `generate_image()`.

```python
req.add_moodboard_image("my_character.png")
req.add_moodboard_image("awesome_outfit.png")
req.control_image("something.png", "pose")
req.init_image(gen_result)
```

Images will remain on the `RequestBuilder` until cleared.

```python
req.clear_moodboard()
req.clear_init_image()
req.clear_control_image("pose")
```

### `ImageBuffer`

Generated images are returned as a list of `ImageBuffer`s. An `ImageBuffer` contains the image's pixel data, dimensions, channel count, and any generation metadata returned by Draw Things.

You can save a generated image directly with `to_file()`:

```python
(image,) = await service.generate_image(req)
image.to_file("output.png")
```

Note: Images saved with the .png format will have metadata importable by Draw Things.

`ImageBuffer` can also load images from disk for use as reference, control, or init images in a `RequestBuilder`.

### `Configs`

There are a number of ways to get a config.

```python
from drawthings_py import Configs

# Load a Community Configuration preset
config = Configs.from_preset("flux_2_klein_4b")

# From JSON, as copied from the Draw Things app
config = Configs.from_json("""{"model":"z_image_turbo_1.0_q6p.ckpt","strength":1,"height":1024,"width":1024, ...}""")

# Create from scratch
config = Configs.create(width=1024, height=1024, ...)
config = Configs.create({
  "width": 768,
  "height": 768,
  ...
})
```

Change individual properties using ["item"] notation. Update many at once using .set().

```python
config["steps"] = 8
config.set({
    "width": 1024,
    "height": 1024,
})
config.set(height=1024, width=1024)
```

Loras and controlnets are exposed as lists and can be accessed and updatd through their respective items. Typed helper methods are provided to add new items.

```python
config.add_lora("dmd2.ckpt", 0.5)
config["loras"][0].weight = 0.25
config["loras"].clear()
```

### Seeds

Draw Things supports any seed value from 1 to 4,294,967,295, in addition to -1, which will use a random seed for each generation. This package also adds features to help manage seeds in your scripts.

**Reusable seeds** - For scripts that make multiple requests, it can sometimes be useful to use the same random seed more than once. If you use a negative value other than `-1`, a random seed will be used and saved for reuse. Any time you use the same negative value, you will get the same seed.

```python
req.config["seed"] = -2
(result_a,) = await service.generate_image(req) # a random seed will be chosen and assigned to -2
req.config["seed"] = -1
(result_b,) = await service.generate_image(req) # another random seed
req.config["seed"] = -2
(result_c,) = await service.generate_image(req) # this gen will have the same seed as result_a
```

(This is currently specific to an individual `RequestBuilder`)

**Determinate seeds** - You can initialize a `RequestBuilder`'s RNG by calling `req.seed_seed(value)`, allowing you to get the same sequence of random seeds across multiple runs.

```python
req.seed_seed("example seed") # you can also use any int, float, or bytes
req.config["seed"] = -1
(result,) = await service.generate(req) # the seed is 3137907891
(result2,) = await service.generate(req) # this seed is 604582375
```

Every time you run, you will get the same sequence of seeds.

(This is also currently specific to an individual `RequestBuilder`)

**Checking a gen's seed** - If you need to see what seed was used for a gen, you can check `result.metadata["seed"]`. It will also be in the PNG metadata when the result is saved to a file.

### Filenames

You can use a `FilenamePattern` to generate filenames for your images. A `FilenamePattern` is a filename with a placeholder for a numeric counter, for example `image_####.png`. Any time the returned function is called, the specified directory will be scanned for files matching the pattern, and the highest matching filename + 1 will be returned.

```python
next_filename = FilenamePattern("image_####.png", "~/Documents/Draw Things images/")
(result,) = await service.generate_image(req)
result.to_file(next_filename()) # creates image_0001.png
(result,) = await service.generate_image(req)
result.to_file(next_filename()) # creates image_0002.png
```

This makes it easy to generate a number of images without having to worry about overwriting anything.
