No description
Find a file
2026-08-21 10:46:22 +02:00
example Implement Dart port of NovelAI image generation library 2026-02-09 20:34:55 +01:00
lib fix(v5): close the NaN gap in the Result<T> never-throws contract 2026-08-21 08:38:14 +00:00
reference feat(v5): Diffusion 5 request tree with multipart transport 2026-08-21 08:10:16 +00:00
test fix(v5): close the NaN gap in the Result<T> never-throws contract 2026-08-21 08:38:14 +00:00
.gitignore Implement Dart port of NovelAI image generation library 2026-02-09 20:34:55 +01:00
.gitmodules Repoint reference/NovelAI.ImageGen submodule to Forgejo mirror 2026-07-04 19:20:44 +00:00
analysis_options.yaml Implement Dart port of NovelAI image generation library 2026-02-09 20:34:55 +01:00
CHANGELOG.md feat(v5): Diffusion 5 request tree with multipart transport 2026-08-21 08:10:16 +00:00
LICENSE Implement Dart port of NovelAI image generation library 2026-02-09 20:34:55 +01:00
pubspec.yaml feat(v5): Diffusion 5 request tree with multipart transport 2026-08-21 08:10:16 +00:00
README.md feat(v5): Diffusion 5 request tree with multipart transport 2026-08-21 08:10:16 +00:00

novelai_image_gen

Strongly-typed Dart bindings for the NovelAI image generation API. Supports text-to-image, img2img, inpainting, vibe transfer, precise character/style reference, and multi-character V4 prompts.

Features

  • Text-to-image generation with full control over sampler, steps, guidance, noise schedule
  • Img2img and inpainting with automatic image scaling and mask grid alignment
  • Vibe transfer using pre-encoded style embeddings
  • Precise reference for character likeness and/or artistic style transfer
  • Multi-character V4 prompts with per-character tags, gender prefixes, and positioning
  • Diffusion 5 (v5) with free-form character positioning and alpha transparency (v5.dart entrypoint)
  • Result pattern (Result<T>) for explicit, type-safe error handling
  • Tag emphasis via NovelAI's strength::tag :: format

Getting started

Add the dependency to your pubspec.yaml:

dependencies:
  novelai_image_gen: ^1.0.0

You'll need a NovelAI API key. Obtain one from your NovelAI account settings.

Usage

Basic text-to-image

import 'dart:io';
import 'package:novelai_image_gen/novelai_image_gen.dart';

final client = NovelAIClient(
  options: NovelAIClientOptions(apiKey: 'your-api-key'),
);

final result = await client.generateImage(
  ImageGenerationRequest(
    width: 1024,
    height: 1024,
    positiveTags: [
      Tag('1girl'),
      Tag('solo'),
      Tag.withStrength('detailed background', 1.3),
    ],
    negativeTags: [Tag('lowres'), Tag('bad anatomy')],
  ),
);

result.match(
  onSuccess: (image) => File('output.png').writeAsBytesSync(image.data),
  onFailure: (error, statusCode) => print('Failed: $error'),
);

Multi-character scene

final result = await client.generateImage(
  ImageGenerationRequest(
    width: 1024,
    height: 1024,
    positiveTags: [Tag('outdoors'), Tag('sunny day')],
    characters: [
      Character(
        gender: CharacterGender.girl,
        positiveTags: [Tag('blonde hair'), Tag('blue eyes')],
        position: Position.at(0.3, 0.5),
      ),
      Character(
        gender: CharacterGender.boy,
        positiveTags: [Tag('black hair'), Tag('glasses')],
        position: Position.fromGrid(3, 4),
      ),
    ],
  ),
);

Diffusion 5 (V5)

Diffusion 5 models use a separate request tree imported from package:novelai_image_gen/v5.dart (V5 names collide with V4). Requests are sent as multipart/form-data; source images and masks are uploaded raw at native resolution or referenced by server-side cache key:

import 'package:novelai_image_gen/novelai_image_gen.dart';
import 'package:novelai_image_gen/v5.dart' as v5;

final result = await client.generateImage(
  v5.ImageGenerationRequest(
    width: 1216,
    height: 832,
    positiveTags: [Tag('1girl'), Tag('street')],
    transparentBackground: true,
    characters: [
      v5.Character(
        positiveTags: [Tag('long blonde hair')],
        position: v5.Position.at(0.32, 0.48), // free-form, no grid
      ),
    ],
  ),
);

Image-conditioned modes take v5.CachedImage values: v5.CachedImage.fromData(bytes) uploads, v5.CachedImage.fromCacheKey(key) reuses a server-side cache entry without uploading. Vibe transfer and precise reference are not yet available for v5 requests.

Vibe transfer

// 1. Encode a reference image
final vibeResult = await client.encodeVibe(
  imageBytes,
  Model.diffusion45Full,
  informationExtracted: 0.7,
);

// 2. Use the embedding in generation
final genResult = await client.generateImage(
  ImageGenerationRequest(
    width: 1024,
    height: 1024,
    positiveTags: [Tag('1girl'), Tag('solo')],
    vibeTransfer: VibeTransferOptions(
      embedding: vibeResult.valueOrDefault!,
      strength: 0.6,
    ),
  ),
);

Validate API key

final result = await client.validateApiKey();
print(result.isSuccess ? 'Valid' : 'Invalid: ${result.errorOrDefault}');

See the example for a complete demonstration.

API reference

NovelAIClient

Method Returns Description
generateImage(request) Future<Result<GeneratedImage>> Generate an image from a prompt
encodeVibe(imageData, model) Future<Result<VibeEmbedding>> Encode an image as a vibe embedding
validateApiKey() Future<Result<bool>> Check if the API key is valid

Generation modes

Mode Set via Notes
Text-to-image Default Just provide positiveTags
Img2img img2Img: Source image stretched to target size
Inpaint inpaint: Requires image + mask; cannot combine with vibe transfer
Vibe transfer vibeTransfer: Requires pre-encoded embedding; cannot combine with inpaint
Precise reference preciseReference: Style and/or character reference images
Multi-character characters: V4 prompt with per-character tags and positioning

Enums

Enum Values
Model diffusion45Full, diffusion45FullInpainting, diffusion5Full, diffusion5FullInpainting
Sampler eulerAncestral, euler, dpmPlusPlus2SAncestral, dpmPlusPlus2M, dpmPlusPlusSde, ddim
NoiseSchedule karras, native_, exponential, polyexponential
ReferenceType style, character, characterAndStyle
CharacterGender none, girl, boy, other

License

MIT - see LICENSE.