nekoai-js
Version:
A lightweight JavaScript/TypeScript API for NovelAI image generation and director tools
748 lines (735 loc) • 22.3 kB
text/typescript
declare enum Host {
WEB = "https://image.novelai.net",
API = "https://api.novelai.net"
}
declare enum Model {
V3 = "nai-diffusion-3",
V3_INP = "nai-diffusion-3-inpainting",
V4 = "nai-diffusion-4-full",
V4_INP = "nai-diffusion-4-full-inpainting",
V4_CUR = "nai-diffusion-4-curated-preview",
V4_CUR_INP = "nai-diffusion-4-curated-inpainting",
V4_5 = "nai-diffusion-4-5-full",
V4_5_INP = "nai-diffusion-4-5-full-inpainting",
V4_5_CUR = "nai-diffusion-4-5-curated",
V4_5_CUR_INP = "nai-diffusion-4-5-curated-inpainting",
FURRY = "nai-diffusion-furry-3",
FURRY_INP = "nai-diffusion-furry-3-inpainting"
}
declare enum Controlnet {
PALETTESWAP = "hed",
FORMLOCK = "midas",
SCRIBBLER = "fake_scribble",
BUILDINGCONTROL = "mlsd",
LANDSCAPER = "uniformer"
}
declare enum Action {
GENERATE = "generate",
INPAINT = "infill",
IMG2IMG = "img2img"
}
declare enum DirectorTools {
LINEART = "lineart",
SKETCH = "sketch",
BACKGROUND_REMOVAL = "bg-removal",
EMOTION = "emotion",
DECLUTTER = "declutter",
COLORIZE = "colorize"
}
declare enum EmotionOptions {
NEUTRAL = "neutral",
HAPPY = "happy",
SAD = "sad",
ANGRY = "angry",
SCARED = "scared",
SURPRISED = "surprised",
TIRED = "tired",
EXCITED = "excited",
NERVOUS = "nervous",
THINKING = "thinking",
CONFUSED = "confused",
SHY = "shy",
DISGUSTED = "disgusted",
SMUG = "smug",
BORED = "bored",
LAUGHING = "laughing",
IRRITATED = "irritated",
AROUSED = "aroused",
EMBARRASSED = "embarrassed",
WORRIED = "worried",
LOVE = "love",
DETERMINED = "determined",
HURT = "hurt",
PLAYFUL = "playful"
}
declare enum EmotionLevel {
NORMAL = 0,
SLIGHTLY_WEAK = 1,
WEAK = 2,
EVEN_WEAKER = 3,
VERY_WEAK = 4,
WEAKEST = 5
}
declare enum Resolution {
SMALL_PORTRAIT = "small_portrait",
SMALL_LANDSCAPE = "small_landscape",
SMALL_SQUARE = "small_square",
NORMAL_PORTRAIT = "normal_portrait",
NORMAL_LANDSCAPE = "normal_landscape",
NORMAL_SQUARE = "normal_square",
LARGE_PORTRAIT = "large_portrait",
LARGE_LANDSCAPE = "large_landscape",
LARGE_SQUARE = "large_square",
WALLPAPER_PORTRAIT = "wallpaper_portrait",
WALLPAPER_LANDSCAPE = "wallpaper_landscape"
}
declare const RESOLUTION_DIMENSIONS: Record<Resolution, [number, number]>;
declare enum Sampler {
EULER = "k_euler",
EULER_ANC = "k_euler_ancestral",
DPM2S_ANC = "k_dpmpp_2s_ancestral",
DPM2M = "k_dpmpp_2m",
DPMSDE = "k_dpmpp_sde",
DPM2MSDE = "k_dpmpp_2m_sde",
DDIM = "ddim_v3"
}
declare enum Noise {
NATIVE = "native",
KARRAS = "karras",
EXPONENTIAL = "exponential",
POLYEXPONENTIAL = "polyexponential"
}
interface User {
token: string;
}
/**
* Configuration for API request retries
*/
interface RetryConfig {
/**
* Whether to enable retry for failed requests
* @default true
*/
enabled?: boolean;
/**
* Maximum number of retry attempts
* @default 3
*/
maxRetries?: number;
/**
* Base delay between retries in milliseconds
* Will be used with exponential backoff
* @default 1000 (1 second)
*/
baseDelay?: number;
/**
* Maximum delay between retries in milliseconds
* @default 30000 (30 seconds)
*/
maxDelay?: number;
/**
* HTTP status codes that should trigger a retry
* By default, retries on rate limits (429) and server errors (500-599)
* @default [429, 500, 501, 502, 503, 504, 507, 508, 509]
*/
retryStatusCodes?: number[];
}
interface PositionCoords {
x: number;
y: number;
}
/**
* Flexible image input types
* This allows images to be passed in various formats for cross-platform compatibility
*/
type ImageInput = string | Blob | File | ArrayBuffer | Uint8Array | {
data: Uint8Array;
} | {
url: string;
} | HTMLImageElement | HTMLCanvasElement;
/**
* Result from parsing an image
*/
interface ParsedImage {
width: number;
height: number;
base64: string;
}
/**
* Character caption for V4 prompts
* Maps to char_caption and centers in the API
*/
interface CharacterCaption {
char_caption: string;
centers: PositionCoords[];
}
/**
* Character prompt for V4.5 multi-character generation
*/
interface CharacterPrompt {
prompt: string;
uc: string;
center: PositionCoords;
enabled?: boolean;
}
/**
* V4 caption format for prompts
* Maps to base_caption and char_captions in the API
*/
interface V4CaptionFormat {
base_caption: string;
char_captions: CharacterCaption[];
}
/**
* V4 prompt format with multi-character support
* Maps to caption, use_coords and use_order in the API
*/
interface V4PromptFormat {
caption: V4CaptionFormat;
use_coords: boolean;
use_order: boolean;
}
/**
* V4 format for negative prompts
* Maps to caption and legacy_uc in the API
*/
interface V4NegativePromptFormat {
caption: V4CaptionFormat;
legacy_uc: boolean;
}
interface Metadata {
prompt: string;
model: Model;
action: Action;
resPreset?: Resolution;
negative_prompt?: string;
qualityToggle?: boolean;
ucPreset?: 0 | 1 | 2 | 3;
width?: number;
height?: number;
n_samples?: number;
steps?: number;
scale?: number;
dynamic_thresholding?: boolean;
seed?: number;
extra_noise_seed?: number;
sampler?: Sampler;
sm?: boolean;
sm_dyn?: boolean;
cfg_rescale?: number;
noise_schedule?: Noise;
image?: string;
strength?: number;
noise?: number;
controlnet_strength?: number;
controlnet_condition?: string;
controlnet_model?: Controlnet;
add_original_image?: boolean;
mask?: string;
reference_image_multiple?: string[];
reference_information_extracted_multiple?: number[];
reference_strength_multiple?: number[];
params_version?: 1 | 2 | 3;
autoSmea?: boolean;
characterPrompts?: CharacterPrompt[];
v4_prompt?: V4PromptFormat;
v4_negative_prompt?: V4NegativePromptFormat;
skip_cfg_above_sigma?: number | null;
use_coords?: boolean;
legacy_uc?: boolean;
normalize_reference_strength_multiple?: boolean;
deliberate_euler_ancestral_bug?: boolean;
prefer_brownian?: boolean;
inpaintImg2ImgStrength?: number;
legacy?: boolean;
legacy_v3_extend?: boolean;
stream?: string | null;
}
interface ImageOptions {
filename: string;
data: Uint8Array;
}
interface DirectorRequestBase {
req_type: string;
width: number;
height: number;
image: string;
}
interface LineArtRequest extends DirectorRequestBase {
req_type: "lineart";
}
interface SketchRequest extends DirectorRequestBase {
req_type: "sketch";
}
interface BackgroundRemovalRequest extends DirectorRequestBase {
req_type: "bg-removal";
}
interface DeclutterRequest extends DirectorRequestBase {
req_type: "declutter";
}
interface ColorizeRequest extends DirectorRequestBase {
req_type: "colorize";
prompt: string;
defry: number;
}
interface EmotionRequest extends DirectorRequestBase {
req_type: "emotion";
prompt: string;
defry: number;
}
type DirectorRequest = LineArtRequest | SketchRequest | BackgroundRemovalRequest | DeclutterRequest | ColorizeRequest | EmotionRequest;
interface NovelAIOptions {
token: string;
host?: Host;
timeout?: number;
retry?: RetryConfig;
verbose?: boolean;
}
interface NovelAIResponse {
statusCode: number;
statusText: string;
headers: Record<string, string>;
data: ArrayBuffer | ReadableStream<Uint8Array> | null;
}
interface NovelAIError extends Error {
status?: number;
statusText?: string;
}
/**
* Represents an image generated by NovelAI
*/
declare class Image {
/**
* The filename of the image
*/
readonly filename: string;
/**
* Raw binary image data
*/
readonly data: Uint8Array;
/**
* Create a new Image instance
*
* @param options - Configuration options for the image
* @param options.filename - The filename of the image
* @param options.data - Raw binary data of the image
*/
constructor(options: ImageOptions);
/**
* Get the size of the image in bytes
*/
get size(): number;
/**
* Convert the image data to a base64 string
*
* @returns Base64 encoded image data
*/
toBase64(): string;
/**
* Create a data URL for the image
*
* @returns Data URL for the image (suitable for browser use)
*/
toDataURL(): string;
/**
* Save the image to disk (Node.js environment only)
*
* @param outputPath - Path to save the image to. If a directory is provided,
* the image is saved with its original filename in that directory.
* @returns Promise that resolves to the full path of the saved file
*/
save(outputPath: string): Promise<string>;
/**
* Create a Blob from the image data (browser environment only)
*
* @returns Blob representing the image
*/
toBlob(): Blob;
/**
* Create a File object from the image data (browser environment only)
*
* @returns File object representing the image
*/
toFile(): File;
}
/**
* Enum for event types in the msgpack event.
*/
declare enum EventType {
INTERMEDIATE = "intermediate",
FINAL = "final"
}
/**
* A single msgpack event object in the return of `generate_image` method or director tools.
*/
declare class MsgpackEvent {
/**
* The type of event (intermediate or final)
*/
readonly event_type: EventType;
/**
* Sample index
*/
readonly samp_ix: number;
/**
* Step index
*/
readonly step_ix: number;
/**
* Generation ID
*/
readonly gen_id: string;
/**
* Sigma value
*/
readonly sigma: number;
/**
* Image data (JPEG for intermediate, PNG for final)
*/
readonly image: Image;
constructor(options: {
event_type: EventType;
samp_ix: number;
step_ix: number;
gen_id: string;
sigma: number;
image: Image;
});
toString(): string;
}
/**
* NovelAI client for interacting with the NovelAI image generation API
*/
declare class NovelAI {
private token;
private host;
private timeout;
private retryConfig?;
private verbose;
private headers;
/**
* Cache of vibe tokens to avoid re-encoding the same images
* @private
*/
private vibeCache;
/**
* Create a new NovelAI client
*
* @param options - Client configuration options
* @param options.token - NovelAI access token
* @param options.host - API host to use (default: Host.WEB)
* @param options.timeout - Request timeout in milliseconds (default: 30000)
* @param options.retry - Configuration for request retries (default: enabled with 3 retries)
* @param options.verbose - Whether to log additional information (default: false)
*/
constructor(options: NovelAIOptions);
/**
* Generate images using NovelAI's API
*
* @param metadata - Generation parameters
* @param stream - Whether to stream intermediate steps for V4 models (default: false)
* @param isOpus - Whether the user has Opus subscription (for cost calculation, default: false)
* @returns Promise resolving to an array of Image objects or AsyncGenerator of MsgpackEvent objects
*/
generateImage(metadata: Metadata, stream?: boolean, isOpus?: boolean): Promise<Image[] | AsyncGenerator<MsgpackEvent, void, unknown>>;
/**
* Makes a request to the NovelAI API with appropriate headers and timeout handling
*
* @param url - The endpoint URL to send the request to
* @param payload - The request payload
* @returns Promise resolving to the API response
* @private
*/
private makeRequest;
/**
* Process V3 model response (ZIP format)
*
* @param payload - The request payload
* @returns Promise resolving to an array of Image objects
* @private
*/
private processV3Response;
/**
* Process V4 model response (msgpack format)
*
* @param payload - The request payload
* @returns Promise resolving to an array of Image objects
* @private
*/
private processV4Response;
/**
* Extract images from ZIP response (V3 models)
*
* @param apiResponse - The API response containing ZIP data
* @returns Promise resolving to an array of Image objects
* @private
*/
private extractImagesFromZip;
/**
* Extract images from msgpack response (V4 models)
*
* @param apiResponse - The API response containing msgpack data
* @returns Promise resolving to an array of Image objects
* @private
*/
private extractImagesFromMsgpack;
/**
* Get response data as ArrayBuffer
*
* @param apiResponse - The API response
* @returns Promise resolving to ArrayBuffer
* @private
*/
private getResponseBuffer;
/**
* Generate timestamp for filename
*
* @returns Formatted timestamp string
* @private
*/
private generateTimestamp;
/**
* Handle request errors with proper timeout detection
*
* @param error - The error to handle
* @returns Formatted error
* @private
*/
private handleRequestError;
/**
* Process and validate metadata before sending to API
*
* @param metadata - User-provided metadata
* @returns Processed metadata object
*/
private processMetadata;
/**
* Use a Director tool with the specified request
*
* @param request - Director tool request
* @returns Promise resolving to an Image object
*/
useDirectorTool(request: DirectorRequest): Promise<Image>;
/**
* Create a director tool request with common parameters
*
* @param image - Image input
* @param reqType - Director tool type
* @param additionalParams - Additional parameters for the request
* @returns Promise resolving to an Image object
* @private
*/
private createDirectorRequest;
/**
* Convert an image to line art
*
* @param image - Image input (path, Blob, File, URL, etc.)
* @returns Promise resolving to an Image object
*/
lineArt(image: ImageInput): Promise<Image>;
/**
* Convert an image to sketch
*
* @param image - Image input (path, Blob, File, URL, etc.)
* @returns Promise resolving to an Image object
*/
sketch(image: ImageInput): Promise<Image>;
/**
* Remove the background from an image
*
* @param image - Image input (path, Blob, File, URL, etc.)
* @returns Promise resolving to an Image object
*/
backgroundRemoval(image: ImageInput): Promise<Image>;
/**
* Declutter an image (remove noise, distractions, etc.)
*
* @param image - Image input (path, Blob, File, URL, etc.)
* @returns Promise resolving to an Image object
*/
declutter(image: ImageInput): Promise<Image>;
/**
* Colorize a sketch or line art
*
* @param image - Image input (path, Blob, File, URL, etc.)
* @param prompt - Additional prompt to add to the request
* @param defry - Defry value (0-5, default: 0)
* @returns Promise resolving to an Image object
*/
colorize(image: ImageInput, prompt?: string, defry?: number): Promise<Image>;
/**
* Change the emotion of a character in an image
*
* @param image - Image input (path, Blob, File, URL, etc.)
* @param emotion - Target emotion to change to
* @param prompt - Additional prompt to add to the request
* @param emotionLevel - Level of emotion change (0-5, optional)
* @returns Promise resolving to an Image object
*/
changeEmotion(image: ImageInput, emotion?: string, prompt?: string, emotionLevel?: EmotionLevel): Promise<Image>;
/**
* Encode images to vibe tokens using the /encode-vibe endpoint
* Uses caching to avoid unnecessary API calls for previously processed images
*
* @param metadata - Metadata object to update with vibe tokens
* @returns Promise resolving when encoding is complete
* @private
*/
private encodeVibe;
/**
* Fetch vibe token from API
*
* @param image - Base64 image data
* @param informationExtracted - Information extraction level
* @param model - Model being used
* @returns Promise resolving to vibe token
* @private
*/
private fetchVibeToken;
/**
* Create a hash for an image to use as a cache key
*
* @param base64Image - Base64 encoded image data
* @returns Promise resolving to a hash string
* @private
*/
private getImageHash;
/**
* Stream V4 events in real-time as they arrive from the server
*
* @param payload - The request payload
* @returns AsyncGenerator yielding MsgpackEvent objects in real-time
* @private
*/
private streamV4Events;
}
/**
* Creates a timestamp-based filename
* @param prefix - Optional prefix for the filename
* @param extension - File extension (default: 'png')
* @returns Formatted filename
*/
declare function createFilename(prefix?: string, extension?: string): string;
/**
* Ensures a directory exists, creating it if necessary
* @param dir - Directory path
*/
declare function ensureDirectoryExists(dir: string): void;
/**
* Save binary data to a file (Node.js environment only)
* @param data - Binary data as Uint8Array
* @param filepath - File path to save to
*/
declare function saveBinaryFile(data: Uint8Array, filepath: string): void;
/**
* Converts file size to human-readable format
* @param bytes - Size in bytes
* @returns Formatted size string
*/
declare function formatFileSize(bytes: number): string;
declare const DEFAULT_RETRY_CONFIG: Required<RetryConfig>;
/**
* Execute a function with retry logic
*
* @param fn - Async function to execute with retry logic
* @param retryConfig - Configuration for retry behavior
* @returns Promise that resolves with the result of the function
*/
declare function withRetry<T>(fn: () => Promise<T>, retryConfig?: RetryConfig): Promise<T>;
/**
* Convert a base64 string to a Uint8Array
* @param base64 - Base64 encoded string
* @returns Uint8Array of the data
*/
declare function base64ToUint8Array(base64: string): Uint8Array;
/**
* Convert a Uint8Array to a base64 string
* @param array - Uint8Array data
* @returns Base64 encoded string
*/
declare function uint8ArrayToBase64(array: Uint8Array): string;
/**
* Parse an image from various input types and return width, height, and base64 data
* Supports browser and Node.js environments with various input formats
*
* @param input - Various image input formats (path, Blob, File, ArrayBuffer, etc.)
* @returns Promise resolving to a ParsedImage object with width, height, and base64 data
*/
declare function parseImage(input: ImageInput): Promise<ParsedImage>;
/**
* Prepares metadata for API request
* @param metadata - Metadata object with parameters
* @returns Formatted API request payload
*/
declare function prepareMetadataForApi(metadata: Metadata): any;
/**
* Calculates the Anlas cost based on parameters
* @param metadata - Metadata object with parameters
* @param isOpus - Whether user has Opus subscription
* @returns Estimated Anlas cost
*/
declare function calculateCost(metadata: Metadata, isOpus?: boolean): number;
/**
* Deduplicate tags in a comma-separated string (case-insensitive)
* @param prompt - Prompt string with tags separated by commas
* @returns Deduplicated prompt string
*/
declare function deduplicateTags(prompt: string): string;
/**
* Image Metadata Parser for AI-generated images
* Extracts metadata from various AI image generation tools (Stable Diffusion WebUI, NovelAI, etc.)
* Supports multiple image formats and metadata storage methods
*/
/**
* Metadata types that can be extracted from AI-generated images
*/
declare enum MetadataType {
STABLE_DIFFUSION_WEBUI = "SD-WEBUI",
NOVELAI = "NOVELAI",
UNKNOWN = "UNKNOWN",
NONE = "NONE"
}
/**
* Structured metadata with keyword and text pairs
*/
interface MetadataEntry {
keyword: string;
text: string;
}
/**
* Result of metadata extraction including type and entries
*/
interface ImageMetadata {
type: MetadataType;
entries: MetadataEntry[];
raw?: any;
}
/**
* Summary of basic image metadata and AI generation information
*/
interface ImageSummary {
dimensions: {
width: number;
height: number;
};
hasMetadata: boolean;
metadataType: MetadataType;
generationTool?: string;
positivePrompt?: string;
negative_prompt?: string;
parameters?: Record<string, any>;
rawEntries: MetadataEntry[];
}
/**
* Extract metadata from an AI-generated image
* @param input - Various image input formats
* @returns Promise resolving to extracted image metadata
*/
declare function extractImageMetadata(input: ImageInput): Promise<ImageMetadata>;
/**
* Extract a simple summary of an AI-generated image
* @param input - Various image input formats
* @returns Promise resolving to image summary
*/
declare function getImageSummary(input: ImageInput): Promise<ImageSummary>;
export { Action, type CharacterCaption, type CharacterPrompt, Controlnet, DEFAULT_RETRY_CONFIG, type DirectorRequest, DirectorTools, EmotionLevel, EmotionOptions, EventType, Host, Image, type ImageOptions, type Metadata, Model, MsgpackEvent, Noise, NovelAI, type NovelAIError, type NovelAIOptions, type NovelAIResponse, type PositionCoords, RESOLUTION_DIMENSIONS, Resolution, type RetryConfig, Sampler, type User, type V4CaptionFormat, type V4NegativePromptFormat, type V4PromptFormat, base64ToUint8Array, calculateCost, createFilename, deduplicateTags, ensureDirectoryExists, extractImageMetadata, formatFileSize, getImageSummary, parseImage, prepareMetadataForApi, saveBinaryFile, uint8ArrayToBase64, withRetry };