stable-diffusion-cpp-node-api 0.9.1
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.
- package/LICENSE +21 -0
- package/README.md +166 -0
- package/package.json +56 -0
- package/src/js/index.d.ts +345 -0
- package/src/js/index.js +170 -0
- package/src/js/load-bindings.js +40 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 node-stable-diffusion-cpp contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# node-stable-diffusion-cpp
|
|
2
|
+
|
|
3
|
+
> **WARNING: This library was vibe coded.** The entire codebase — C++ bindings, JS wrappers, build
|
|
4
|
+
> system, and documentation — was generated by an AI assistant in a single session. It compiles,
|
|
5
|
+
> passes basic smoke tests, and the API surface matches the upstream C library. However, it has
|
|
6
|
+
> **not** been battle-tested in production. Edge cases, memory leaks under unusual error paths, and
|
|
7
|
+
> platform-specific build quirks may exist. Use at your own risk, report issues, and read the source
|
|
8
|
+
> before shipping anything important.
|
|
9
|
+
|
|
10
|
+
Native Node.js bindings for [stable-diffusion.cpp](https://github.com/leejet/stable-diffusion.cpp) — a pure C/C++ implementation of Stable Diffusion, SDXL, SD3, FLUX, Wan video, and more. Built on N-API for ABI stability across Node.js versions. All heavy operations (model loading, image/video generation, upscaling) run on background threads and return Promises.
|
|
11
|
+
|
|
12
|
+
## Features
|
|
13
|
+
|
|
14
|
+
- **Image generation** — txt2img, img2img, inpainting, ControlNet, PhotoMaker, LoRA
|
|
15
|
+
- **Video generation** — Wan2.1/2.2 text-to-video and image-to-video
|
|
16
|
+
- **Upscaling** — ESRGAN-based super resolution
|
|
17
|
+
- **Model conversion** — safetensors/ckpt to GGUF quantized formats
|
|
18
|
+
- **GPU auto-detection** — Metal (macOS), CUDA (Linux/Windows), Vulkan
|
|
19
|
+
- **Async everything** — context creation, generation, upscaling all return Promises
|
|
20
|
+
- **Callbacks** — log, progress, and preview callbacks bridged to the JS event loop
|
|
21
|
+
- **Electron-ready** — N-API v8 targets Node.js 18+ / Electron 22+
|
|
22
|
+
- **TypeScript** — full type declarations included
|
|
23
|
+
|
|
24
|
+
## Requirements
|
|
25
|
+
|
|
26
|
+
- Node.js >= 18
|
|
27
|
+
- CMake >= 3.15
|
|
28
|
+
- C++17 compiler (Xcode, GCC 9+, MSVC 2019+)
|
|
29
|
+
- Platform SDK for GPU backend (Xcode for Metal, CUDA Toolkit for NVIDIA, Vulkan SDK)
|
|
30
|
+
|
|
31
|
+
## Installation
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
git clone --recursive <your-repo-url>
|
|
35
|
+
cd node-stable-diffusion-cpp
|
|
36
|
+
npm install
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
The `npm install` step compiles the native module. On macOS it automatically enables Metal; on systems with CUDA or Vulkan SDKs installed, those backends are enabled too.
|
|
40
|
+
|
|
41
|
+
### Manual build
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
npm run build # Release build
|
|
45
|
+
npm run build:debug # Debug build
|
|
46
|
+
npm run rebuild # Clean + rebuild
|
|
47
|
+
npm run clean # Remove build artifacts
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Quick start
|
|
51
|
+
|
|
52
|
+
```javascript
|
|
53
|
+
const sd = require('node-stable-diffusion-cpp');
|
|
54
|
+
const fs = require('fs');
|
|
55
|
+
|
|
56
|
+
// Optional: log what the library is doing
|
|
57
|
+
sd.setLogCallback(({ level, text }) => {
|
|
58
|
+
if (level >= 1) process.stderr.write(text);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
// Optional: track generation progress
|
|
62
|
+
sd.setProgressCallback(({ step, steps, time }) => {
|
|
63
|
+
process.stderr.write(`\rStep ${step}/${steps} (${time.toFixed(1)}s)`);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
(async () => {
|
|
67
|
+
// Load a model (runs on background thread)
|
|
68
|
+
// Use modelPath for single-file models (ckpt, safetensors, full gguf).
|
|
69
|
+
// Use diffusionModelPath only for standalone diffusion-only component files.
|
|
70
|
+
const ctx = await sd.StableDiffusionContext.create({
|
|
71
|
+
modelPath: 'path/to/model.gguf',
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// Generate an image
|
|
75
|
+
const images = await ctx.generateImage({
|
|
76
|
+
prompt: 'a photo of an astronaut riding a horse on mars',
|
|
77
|
+
width: 512,
|
|
78
|
+
height: 512,
|
|
79
|
+
sampleParams: {
|
|
80
|
+
sampleSteps: 20,
|
|
81
|
+
guidance: { txtCfg: 7.0 },
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// Save raw RGB pixels (use stb_image or sharp to encode to PNG)
|
|
86
|
+
const img = images[0];
|
|
87
|
+
console.log(`\nGenerated ${img.width}x${img.height} image (${img.data.length} bytes)`);
|
|
88
|
+
fs.writeFileSync('output.raw', img.data);
|
|
89
|
+
|
|
90
|
+
ctx.close();
|
|
91
|
+
})();
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Smoke test
|
|
95
|
+
|
|
96
|
+
A ready-made script is included:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
node smoke.js path/to/model.gguf
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Edit the constants at the top of `smoke.js` to change prompt, dimensions, steps, etc.
|
|
103
|
+
|
|
104
|
+
## API overview
|
|
105
|
+
|
|
106
|
+
| Function / Class | Description |
|
|
107
|
+
|---|---|
|
|
108
|
+
| `StableDiffusionContext.create(opts)` | Load a model, returns `Promise<StableDiffusionContext>` |
|
|
109
|
+
| `ctx.generateImage(opts)` | Text/image to image, returns `Promise<SdImage[]>` |
|
|
110
|
+
| `ctx.generateVideo(opts)` | Text/image to video frames, returns `Promise<SdImage[]>` |
|
|
111
|
+
| `ctx.getDefaultSampleMethod()` | Default sampler for loaded model |
|
|
112
|
+
| `ctx.getDefaultScheduler(method?)` | Default scheduler for sampler |
|
|
113
|
+
| `ctx.close()` | Free native resources |
|
|
114
|
+
| `UpscalerContext.create(opts)` | Load ESRGAN model, returns `Promise<UpscalerContext>` |
|
|
115
|
+
| `upscaler.upscale(image, factor?)` | Upscale an image, returns `Promise<SdImage>` |
|
|
116
|
+
| `convert(opts)` | Convert model format, returns `Promise<boolean>` |
|
|
117
|
+
| `preprocessCanny(image, opts?)` | Canny edge detection (sync, in-place) |
|
|
118
|
+
| `setLogCallback(fn)` | Set/clear log callback |
|
|
119
|
+
| `setProgressCallback(fn)` | Set/clear progress callback |
|
|
120
|
+
| `setPreviewCallback(fn, opts?)` | Set/clear preview callback |
|
|
121
|
+
| `getSystemInfo()` | Backend/hardware info string |
|
|
122
|
+
| `getNumPhysicalCores()` | CPU core count |
|
|
123
|
+
| `version()` / `commit()` | Library version and git commit |
|
|
124
|
+
|
|
125
|
+
See [docs/API.md](docs/API.md) for the full API reference with all options, types, and examples.
|
|
126
|
+
|
|
127
|
+
## Supported models
|
|
128
|
+
|
|
129
|
+
Anything stable-diffusion.cpp supports:
|
|
130
|
+
|
|
131
|
+
- Stable Diffusion 1.x, 2.x
|
|
132
|
+
- SDXL, SDXL Turbo
|
|
133
|
+
- SD3, SD3.5
|
|
134
|
+
- FLUX.1 (dev, schnell)
|
|
135
|
+
- Wan2.1, Wan2.2 (video)
|
|
136
|
+
- Chroma, Qwen-Image
|
|
137
|
+
- LoRA, ControlNet, PhotoMaker
|
|
138
|
+
- GGUF quantized models (Q4_0, Q5_0, Q8_0, F16, etc.)
|
|
139
|
+
|
|
140
|
+
## Image data format
|
|
141
|
+
|
|
142
|
+
All images are plain objects with raw pixel data:
|
|
143
|
+
|
|
144
|
+
```typescript
|
|
145
|
+
interface SdImage {
|
|
146
|
+
width: number; // pixels
|
|
147
|
+
height: number; // pixels
|
|
148
|
+
channel: number; // 3 for RGB
|
|
149
|
+
data: Buffer; // raw RGB bytes, length = width * height * channel
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
To save as PNG, use a library like [sharp](https://sharp.pixelplumbing.com/):
|
|
154
|
+
|
|
155
|
+
```javascript
|
|
156
|
+
const sharp = require('sharp');
|
|
157
|
+
await sharp(img.data, { raw: { width: img.width, height: img.height, channels: img.channel } })
|
|
158
|
+
.png()
|
|
159
|
+
.toFile('output.png');
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## License
|
|
163
|
+
|
|
164
|
+
MIT. See [LICENSE](LICENSE).
|
|
165
|
+
|
|
166
|
+
stable-diffusion.cpp is also MIT licensed. GGML is MIT licensed.
|
package/package.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "stable-diffusion-cpp-node-api",
|
|
3
|
+
"version": "0.9.1",
|
|
4
|
+
"description": "Node.js N-API bindings for stable-diffusion.cpp — run Stable Diffusion, SDXL, SD3, FLUX, and Wan video models natively",
|
|
5
|
+
"main": "src/js/index.js",
|
|
6
|
+
"types": "src/js/index.d.ts",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/sir-ingvarr/node-stable-diffusion-cpp.git"
|
|
10
|
+
},
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"scripts": {
|
|
13
|
+
"preinstall": "test -d .git && git submodule update --init --recursive || true",
|
|
14
|
+
"build": "cmake-js compile --release",
|
|
15
|
+
"build:debug": "cmake-js compile --debug",
|
|
16
|
+
"rebuild": "cmake-js rebuild --release",
|
|
17
|
+
"clean": "cmake-js clean",
|
|
18
|
+
"test": "node test/basic.js",
|
|
19
|
+
"smoke": "node smoke.js"
|
|
20
|
+
},
|
|
21
|
+
"keywords": [
|
|
22
|
+
"stable-diffusion",
|
|
23
|
+
"sdxl",
|
|
24
|
+
"sd3",
|
|
25
|
+
"flux",
|
|
26
|
+
"wan",
|
|
27
|
+
"image-generation",
|
|
28
|
+
"video-generation",
|
|
29
|
+
"diffusion",
|
|
30
|
+
"ai",
|
|
31
|
+
"machine-learning",
|
|
32
|
+
"ggml",
|
|
33
|
+
"gguf",
|
|
34
|
+
"native",
|
|
35
|
+
"napi",
|
|
36
|
+
"electron"
|
|
37
|
+
],
|
|
38
|
+
"files": [
|
|
39
|
+
"src/js/",
|
|
40
|
+
"LICENSE",
|
|
41
|
+
"README.md"
|
|
42
|
+
],
|
|
43
|
+
"engines": {
|
|
44
|
+
"node": ">=18.0.0"
|
|
45
|
+
},
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"cmake-js": "^8.0.0",
|
|
48
|
+
"node-addon-api": "^8.3.1"
|
|
49
|
+
},
|
|
50
|
+
"optionalDependencies": {
|
|
51
|
+
"@stable-diffusion-cpp-node-api/darwin-arm64": "0.9.1",
|
|
52
|
+
"@stable-diffusion-cpp-node-api/darwin-x64": "0.9.1",
|
|
53
|
+
"@stable-diffusion-cpp-node-api/linux-x64": "0.9.1",
|
|
54
|
+
"@stable-diffusion-cpp-node-api/win32-x64": "0.9.1"
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
|
|
3
|
+
// --- Enum string literal types ---
|
|
4
|
+
|
|
5
|
+
export type SampleMethod =
|
|
6
|
+
| 'euler'
|
|
7
|
+
| 'euler_a'
|
|
8
|
+
| 'heun'
|
|
9
|
+
| 'dpm2'
|
|
10
|
+
| 'dpm++2s_a'
|
|
11
|
+
| 'dpm++2m'
|
|
12
|
+
| 'dpm++2mv2'
|
|
13
|
+
| 'ipndm'
|
|
14
|
+
| 'ipndm_v'
|
|
15
|
+
| 'lcm'
|
|
16
|
+
| 'ddim_trailing'
|
|
17
|
+
| 'tcd'
|
|
18
|
+
| 'res_multistep'
|
|
19
|
+
| 'res_2s';
|
|
20
|
+
|
|
21
|
+
export type Scheduler =
|
|
22
|
+
| 'discrete'
|
|
23
|
+
| 'karras'
|
|
24
|
+
| 'exponential'
|
|
25
|
+
| 'ays'
|
|
26
|
+
| 'gits'
|
|
27
|
+
| 'sgm_uniform'
|
|
28
|
+
| 'simple'
|
|
29
|
+
| 'smoothstep'
|
|
30
|
+
| 'kl_optimal'
|
|
31
|
+
| 'lcm'
|
|
32
|
+
| 'bong_tangent';
|
|
33
|
+
|
|
34
|
+
export type SdType =
|
|
35
|
+
| 'f32'
|
|
36
|
+
| 'f16'
|
|
37
|
+
| 'q4_0'
|
|
38
|
+
| 'q4_1'
|
|
39
|
+
| 'q5_0'
|
|
40
|
+
| 'q5_1'
|
|
41
|
+
| 'q8_0'
|
|
42
|
+
| 'q8_1'
|
|
43
|
+
| 'q2_k'
|
|
44
|
+
| 'q3_k'
|
|
45
|
+
| 'q4_k'
|
|
46
|
+
| 'q5_k'
|
|
47
|
+
| 'q6_k'
|
|
48
|
+
| 'q8_k'
|
|
49
|
+
| 'iq2_xxs'
|
|
50
|
+
| 'iq2_xs'
|
|
51
|
+
| 'iq3_xxs'
|
|
52
|
+
| 'iq1_s'
|
|
53
|
+
| 'iq4_nl'
|
|
54
|
+
| 'iq3_s'
|
|
55
|
+
| 'iq2_s'
|
|
56
|
+
| 'iq4_xs'
|
|
57
|
+
| 'i8'
|
|
58
|
+
| 'i16'
|
|
59
|
+
| 'i32'
|
|
60
|
+
| 'i64'
|
|
61
|
+
| 'f64'
|
|
62
|
+
| 'iq1_m'
|
|
63
|
+
| 'bf16'
|
|
64
|
+
| 'tq1_0'
|
|
65
|
+
| 'tq2_0'
|
|
66
|
+
| 'mxfp4';
|
|
67
|
+
|
|
68
|
+
export type RngType = 'std_default' | 'cuda' | 'cpu';
|
|
69
|
+
|
|
70
|
+
export type Prediction = 'eps' | 'v' | 'edm_v' | 'flow' | 'flux_flow' | 'flux2_flow';
|
|
71
|
+
|
|
72
|
+
export type PreviewMode = 'none' | 'proj' | 'tae' | 'vae';
|
|
73
|
+
|
|
74
|
+
export type LogLevel = 0 | 1 | 2 | 3; // debug, info, warn, error
|
|
75
|
+
|
|
76
|
+
export type LoraApplyMode = 'auto' | 'immediately' | 'at_runtime';
|
|
77
|
+
|
|
78
|
+
export type CacheMode = 'disabled' | 'easycache' | 'ucache' | 'dbcache' | 'taylorseer' | 'cachedit';
|
|
79
|
+
|
|
80
|
+
// --- Data interfaces ---
|
|
81
|
+
|
|
82
|
+
export interface SdImage {
|
|
83
|
+
width: number;
|
|
84
|
+
height: number;
|
|
85
|
+
channel: number;
|
|
86
|
+
data: Buffer;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export interface LoraDefinition {
|
|
90
|
+
path: string;
|
|
91
|
+
multiplier?: number;
|
|
92
|
+
isHighNoise?: boolean;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export interface EmbeddingDefinition {
|
|
96
|
+
name: string;
|
|
97
|
+
path: string;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export interface SlgParams {
|
|
101
|
+
layers?: number[];
|
|
102
|
+
layerStart?: number;
|
|
103
|
+
layerEnd?: number;
|
|
104
|
+
scale?: number;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export interface GuidanceParams {
|
|
108
|
+
txtCfg?: number;
|
|
109
|
+
imgCfg?: number;
|
|
110
|
+
distilledGuidance?: number;
|
|
111
|
+
slg?: SlgParams;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export interface SampleParams {
|
|
115
|
+
guidance?: GuidanceParams;
|
|
116
|
+
scheduler?: Scheduler;
|
|
117
|
+
sampleMethod?: SampleMethod;
|
|
118
|
+
sampleSteps?: number;
|
|
119
|
+
eta?: number;
|
|
120
|
+
shiftedTimestep?: number;
|
|
121
|
+
flowShift?: number;
|
|
122
|
+
customSigmas?: number[];
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export interface CacheParams {
|
|
126
|
+
mode?: CacheMode;
|
|
127
|
+
reuseThreshold?: number;
|
|
128
|
+
startPercent?: number;
|
|
129
|
+
endPercent?: number;
|
|
130
|
+
errorDecayRate?: number;
|
|
131
|
+
useRelativeThreshold?: boolean;
|
|
132
|
+
resetErrorOnCompute?: boolean;
|
|
133
|
+
fnComputeBlocks?: number;
|
|
134
|
+
bnComputeBlocks?: number;
|
|
135
|
+
residualDiffThreshold?: number;
|
|
136
|
+
maxWarmupSteps?: number;
|
|
137
|
+
maxCachedSteps?: number;
|
|
138
|
+
maxContinuousCachedSteps?: number;
|
|
139
|
+
taylorseerNDerivatives?: number;
|
|
140
|
+
taylorseerSkipInterval?: number;
|
|
141
|
+
scmPolicyDynamic?: boolean;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export interface TilingParams {
|
|
145
|
+
enabled?: boolean;
|
|
146
|
+
tileSizeX?: number;
|
|
147
|
+
tileSizeY?: number;
|
|
148
|
+
targetOverlap?: number;
|
|
149
|
+
relSizeX?: number;
|
|
150
|
+
relSizeY?: number;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export interface PhotoMakerParams {
|
|
154
|
+
idImages?: SdImage[];
|
|
155
|
+
idEmbedPath?: string;
|
|
156
|
+
styleStrength?: number;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// --- Abort support ---
|
|
160
|
+
|
|
161
|
+
export interface AbortableOptions {
|
|
162
|
+
signal?: AbortSignal;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// --- Context creation options ---
|
|
166
|
+
|
|
167
|
+
export interface ContextOptions extends AbortableOptions {
|
|
168
|
+
modelPath?: string;
|
|
169
|
+
clipLPath?: string;
|
|
170
|
+
clipGPath?: string;
|
|
171
|
+
clipVisionPath?: string;
|
|
172
|
+
t5xxlPath?: string;
|
|
173
|
+
llmPath?: string;
|
|
174
|
+
llmVisionPath?: string;
|
|
175
|
+
diffusionModelPath?: string;
|
|
176
|
+
highNoiseDiffusionModelPath?: string;
|
|
177
|
+
vaePath?: string;
|
|
178
|
+
taesdPath?: string;
|
|
179
|
+
controlNetPath?: string;
|
|
180
|
+
photoMakerPath?: string;
|
|
181
|
+
tensorTypeRules?: string;
|
|
182
|
+
embeddings?: EmbeddingDefinition[];
|
|
183
|
+
vaeDecodeOnly?: boolean;
|
|
184
|
+
freeParamsImmediately?: boolean;
|
|
185
|
+
nThreads?: number;
|
|
186
|
+
wtype?: SdType;
|
|
187
|
+
rngType?: RngType;
|
|
188
|
+
samplerRngType?: RngType;
|
|
189
|
+
prediction?: Prediction;
|
|
190
|
+
loraApplyMode?: LoraApplyMode;
|
|
191
|
+
offloadParamsToCpu?: boolean;
|
|
192
|
+
enableMmap?: boolean;
|
|
193
|
+
keepClipOnCpu?: boolean;
|
|
194
|
+
keepControlNetOnCpu?: boolean;
|
|
195
|
+
keepVaeOnCpu?: boolean;
|
|
196
|
+
flashAttn?: boolean;
|
|
197
|
+
diffusionFlashAttn?: boolean;
|
|
198
|
+
taePreviewOnly?: boolean;
|
|
199
|
+
diffusionConvDirect?: boolean;
|
|
200
|
+
vaeConvDirect?: boolean;
|
|
201
|
+
circularX?: boolean;
|
|
202
|
+
circularY?: boolean;
|
|
203
|
+
forceSdxlVaeConvScale?: boolean;
|
|
204
|
+
chromaUseDitMask?: boolean;
|
|
205
|
+
chromaUseT5Mask?: boolean;
|
|
206
|
+
chromaT5MaskPad?: number;
|
|
207
|
+
qwenImageZeroCondT?: boolean;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// --- Generation options ---
|
|
211
|
+
|
|
212
|
+
export interface ImageGenerationOptions extends AbortableOptions {
|
|
213
|
+
prompt?: string;
|
|
214
|
+
negativePrompt?: string;
|
|
215
|
+
clipSkip?: number;
|
|
216
|
+
initImage?: SdImage;
|
|
217
|
+
refImages?: SdImage[];
|
|
218
|
+
autoResizeRefImage?: boolean;
|
|
219
|
+
increaseRefIndex?: boolean;
|
|
220
|
+
maskImage?: SdImage;
|
|
221
|
+
width?: number;
|
|
222
|
+
height?: number;
|
|
223
|
+
sampleParams?: SampleParams;
|
|
224
|
+
strength?: number;
|
|
225
|
+
seed?: number;
|
|
226
|
+
batchCount?: number;
|
|
227
|
+
controlImage?: SdImage;
|
|
228
|
+
controlStrength?: number;
|
|
229
|
+
photoMaker?: PhotoMakerParams;
|
|
230
|
+
vaeTiling?: TilingParams;
|
|
231
|
+
cache?: CacheParams;
|
|
232
|
+
loras?: LoraDefinition[];
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
export interface VideoGenerationOptions extends AbortableOptions {
|
|
236
|
+
prompt?: string;
|
|
237
|
+
negativePrompt?: string;
|
|
238
|
+
clipSkip?: number;
|
|
239
|
+
initImage?: SdImage;
|
|
240
|
+
endImage?: SdImage;
|
|
241
|
+
controlFrames?: SdImage[];
|
|
242
|
+
width?: number;
|
|
243
|
+
height?: number;
|
|
244
|
+
sampleParams?: SampleParams;
|
|
245
|
+
highNoiseSampleParams?: SampleParams;
|
|
246
|
+
moeBoundary?: number;
|
|
247
|
+
strength?: number;
|
|
248
|
+
seed?: number;
|
|
249
|
+
videoFrames?: number;
|
|
250
|
+
vaceStrength?: number;
|
|
251
|
+
vaeTiling?: TilingParams;
|
|
252
|
+
cache?: CacheParams;
|
|
253
|
+
loras?: LoraDefinition[];
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
export interface UpscalerOptions extends AbortableOptions {
|
|
257
|
+
esrganPath: string;
|
|
258
|
+
offloadParamsToCpu?: boolean;
|
|
259
|
+
direct?: boolean;
|
|
260
|
+
nThreads?: number;
|
|
261
|
+
tileSize?: number;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
export interface UpscaleOptions extends AbortableOptions {}
|
|
265
|
+
|
|
266
|
+
export interface ConvertOptions extends AbortableOptions {
|
|
267
|
+
inputPath: string;
|
|
268
|
+
outputPath: string;
|
|
269
|
+
vaePath?: string;
|
|
270
|
+
outputType?: SdType;
|
|
271
|
+
tensorTypeRules?: string;
|
|
272
|
+
convertName?: boolean;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
export interface CannyOptions {
|
|
276
|
+
highThreshold?: number;
|
|
277
|
+
lowThreshold?: number;
|
|
278
|
+
weak?: number;
|
|
279
|
+
strong?: number;
|
|
280
|
+
inverse?: boolean;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
export interface PreviewOptions {
|
|
284
|
+
mode?: PreviewMode;
|
|
285
|
+
interval?: number;
|
|
286
|
+
denoised?: boolean;
|
|
287
|
+
noisy?: boolean;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// --- Callback data types ---
|
|
291
|
+
|
|
292
|
+
export interface LogCallbackData {
|
|
293
|
+
level: LogLevel;
|
|
294
|
+
text: string;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
export interface ProgressCallbackData {
|
|
298
|
+
step: number;
|
|
299
|
+
steps: number;
|
|
300
|
+
time: number;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
export interface PreviewCallbackData {
|
|
304
|
+
step: number;
|
|
305
|
+
isNoisy: boolean;
|
|
306
|
+
frames: SdImage[];
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// --- Classes ---
|
|
310
|
+
|
|
311
|
+
export class StableDiffusionContext {
|
|
312
|
+
private constructor();
|
|
313
|
+
static create(options: ContextOptions): Promise<StableDiffusionContext>;
|
|
314
|
+
generateImage(options: ImageGenerationOptions): Promise<SdImage[]>;
|
|
315
|
+
generateVideo(options: VideoGenerationOptions): Promise<SdImage[]>;
|
|
316
|
+
getDefaultSampleMethod(): SampleMethod;
|
|
317
|
+
getDefaultScheduler(sampleMethod?: SampleMethod): Scheduler;
|
|
318
|
+
close(): void;
|
|
319
|
+
readonly isClosed: boolean;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
export class UpscalerContext {
|
|
323
|
+
private constructor();
|
|
324
|
+
static create(options: UpscalerOptions): Promise<UpscalerContext>;
|
|
325
|
+
upscale(image: SdImage, upscaleFactor?: number, options?: UpscaleOptions): Promise<SdImage>;
|
|
326
|
+
getUpscaleFactor(): number;
|
|
327
|
+
close(): void;
|
|
328
|
+
readonly isClosed: boolean;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// --- Free functions ---
|
|
332
|
+
|
|
333
|
+
export function abort(): void;
|
|
334
|
+
export function convert(options: ConvertOptions): Promise<boolean>;
|
|
335
|
+
export function preprocessCanny(image: SdImage, options?: CannyOptions): boolean;
|
|
336
|
+
export function setLogCallback(callback: ((data: LogCallbackData) => void) | null): void;
|
|
337
|
+
export function setProgressCallback(callback: ((data: ProgressCallbackData) => void) | null): void;
|
|
338
|
+
export function setPreviewCallback(
|
|
339
|
+
callback: ((data: PreviewCallbackData) => void) | null,
|
|
340
|
+
options?: PreviewOptions,
|
|
341
|
+
): void;
|
|
342
|
+
export function getSystemInfo(): string;
|
|
343
|
+
export function getNumPhysicalCores(): number;
|
|
344
|
+
export function version(): string;
|
|
345
|
+
export function commit(): string;
|
package/src/js/index.js
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
const loadBinding = require('./load-bindings');
|
|
2
|
+
|
|
3
|
+
const native = loadBinding();
|
|
4
|
+
|
|
5
|
+
function withAbortSignal(promise, signal) {
|
|
6
|
+
if (!signal) return promise;
|
|
7
|
+
if (signal.aborted) {
|
|
8
|
+
native.abort();
|
|
9
|
+
return Promise.reject(signal.reason ?? new DOMException('The operation was aborted.', 'AbortError'));
|
|
10
|
+
}
|
|
11
|
+
return new Promise((resolve, reject) => {
|
|
12
|
+
const onAbort = () => {
|
|
13
|
+
native.abort();
|
|
14
|
+
reject(signal.reason ?? new DOMException('The operation was aborted.', 'AbortError'));
|
|
15
|
+
};
|
|
16
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
17
|
+
promise.then(
|
|
18
|
+
(v) => { signal.removeEventListener('abort', onAbort); resolve(v); },
|
|
19
|
+
(e) => { signal.removeEventListener('abort', onAbort); reject(e); },
|
|
20
|
+
);
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
class StableDiffusionContext {
|
|
25
|
+
#native;
|
|
26
|
+
|
|
27
|
+
/** @internal — use StableDiffusionContext.create() */
|
|
28
|
+
constructor(nativeCtx) {
|
|
29
|
+
this.#native = nativeCtx;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Create a new StableDiffusion context asynchronously.
|
|
34
|
+
* Model loading happens on a background thread.
|
|
35
|
+
* @param {object} options
|
|
36
|
+
* @returns {Promise<StableDiffusionContext>}
|
|
37
|
+
*/
|
|
38
|
+
static async create(options) {
|
|
39
|
+
const { signal, ...opts } = options || {};
|
|
40
|
+
const ctx = await withAbortSignal(native.StableDiffusionContext.create(opts), signal);
|
|
41
|
+
return new StableDiffusionContext(ctx);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Generate images from a text prompt.
|
|
46
|
+
* @param {object} options
|
|
47
|
+
* @returns {Promise<Array<{width: number, height: number, channel: number, data: Buffer}>>}
|
|
48
|
+
*/
|
|
49
|
+
async generateImage(options) {
|
|
50
|
+
const { signal, ...opts } = options || {};
|
|
51
|
+
return withAbortSignal(this.#native.generateImage(opts), signal);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Generate video frames from a text prompt.
|
|
56
|
+
* @param {object} options
|
|
57
|
+
* @returns {Promise<Array<{width: number, height: number, channel: number, data: Buffer}>>}
|
|
58
|
+
*/
|
|
59
|
+
async generateVideo(options) {
|
|
60
|
+
const { signal, ...opts } = options || {};
|
|
61
|
+
return withAbortSignal(this.#native.generateVideo(opts), signal);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Get the default sample method for this model.
|
|
66
|
+
* @returns {string}
|
|
67
|
+
*/
|
|
68
|
+
getDefaultSampleMethod() {
|
|
69
|
+
return this.#native.getDefaultSampleMethod();
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Get the default scheduler for the given sample method.
|
|
74
|
+
* @param {string} [sampleMethod]
|
|
75
|
+
* @returns {string}
|
|
76
|
+
*/
|
|
77
|
+
getDefaultScheduler(sampleMethod) {
|
|
78
|
+
return this.#native.getDefaultScheduler(sampleMethod);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Free the native context resources.
|
|
83
|
+
*/
|
|
84
|
+
close() {
|
|
85
|
+
this.#native.close();
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Whether the context has been closed.
|
|
90
|
+
* @returns {boolean}
|
|
91
|
+
*/
|
|
92
|
+
get isClosed() {
|
|
93
|
+
return this.#native.isClosed;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
class UpscalerContext {
|
|
98
|
+
#native;
|
|
99
|
+
|
|
100
|
+
/** @internal — use UpscalerContext.create() */
|
|
101
|
+
constructor(nativeCtx) {
|
|
102
|
+
this.#native = nativeCtx;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Create a new upscaler context asynchronously.
|
|
107
|
+
* @param {object} options
|
|
108
|
+
* @returns {Promise<UpscalerContext>}
|
|
109
|
+
*/
|
|
110
|
+
static async create(options) {
|
|
111
|
+
const { signal, ...opts } = options || {};
|
|
112
|
+
const ctx = await withAbortSignal(native.UpscalerContext.create(opts), signal);
|
|
113
|
+
return new UpscalerContext(ctx);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Upscale an image.
|
|
118
|
+
* @param {{width: number, height: number, channel: number, data: Buffer}} image
|
|
119
|
+
* @param {number} [upscaleFactor=4]
|
|
120
|
+
* @param {{ signal?: AbortSignal }} [options]
|
|
121
|
+
* @returns {Promise<{width: number, height: number, channel: number, data: Buffer}>}
|
|
122
|
+
*/
|
|
123
|
+
async upscale(image, upscaleFactor = 4, options) {
|
|
124
|
+
const { signal } = options || {};
|
|
125
|
+
return withAbortSignal(this.#native.upscale(image, upscaleFactor), signal);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Get the upscale factor for this model.
|
|
130
|
+
* @returns {number}
|
|
131
|
+
*/
|
|
132
|
+
getUpscaleFactor() {
|
|
133
|
+
return this.#native.getUpscaleFactor();
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Free the native context resources.
|
|
138
|
+
*/
|
|
139
|
+
close() {
|
|
140
|
+
this.#native.close();
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Whether the context has been closed.
|
|
145
|
+
* @returns {boolean}
|
|
146
|
+
*/
|
|
147
|
+
get isClosed() {
|
|
148
|
+
return this.#native.isClosed;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
module.exports = {
|
|
153
|
+
StableDiffusionContext,
|
|
154
|
+
UpscalerContext,
|
|
155
|
+
|
|
156
|
+
// Free functions
|
|
157
|
+
convert(options) {
|
|
158
|
+
const { signal, ...opts } = options || {};
|
|
159
|
+
return withAbortSignal(native.convert(opts), signal);
|
|
160
|
+
},
|
|
161
|
+
preprocessCanny: native.preprocessCanny,
|
|
162
|
+
setLogCallback: native.setLogCallback,
|
|
163
|
+
setProgressCallback: native.setProgressCallback,
|
|
164
|
+
setPreviewCallback: native.setPreviewCallback,
|
|
165
|
+
abort: native.abort,
|
|
166
|
+
getSystemInfo: native.getSystemInfo,
|
|
167
|
+
getNumPhysicalCores: native.getNumPhysicalCores,
|
|
168
|
+
version: native.version,
|
|
169
|
+
commit: native.commit,
|
|
170
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
|
|
3
|
+
const PLATFORMS = {
|
|
4
|
+
'darwin-arm64': '@stable-diffusion-cpp-node-api/darwin-arm64',
|
|
5
|
+
'darwin-x64': '@stable-diffusion-cpp-node-api/darwin-x64',
|
|
6
|
+
'linux-x64': '@stable-diffusion-cpp-node-api/linux-x64',
|
|
7
|
+
'win32-x64': '@stable-diffusion-cpp-node-api/win32-x64',
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
let binding;
|
|
11
|
+
|
|
12
|
+
function loadBinding() {
|
|
13
|
+
if (binding) return binding;
|
|
14
|
+
|
|
15
|
+
const key = `${process.platform}-${process.arch}`;
|
|
16
|
+
const pkg = PLATFORMS[key];
|
|
17
|
+
|
|
18
|
+
// 1. Try platform-specific npm package
|
|
19
|
+
if (pkg) {
|
|
20
|
+
try {
|
|
21
|
+
binding = require(pkg);
|
|
22
|
+
return binding;
|
|
23
|
+
} catch (_) {}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// 2. Fallback to local build (development / unsupported platform)
|
|
27
|
+
try {
|
|
28
|
+
binding = require(path.resolve(__dirname, '..', '..', 'build', 'Release', 'node_stable_diffusion.node'));
|
|
29
|
+
return binding;
|
|
30
|
+
} catch (_) {}
|
|
31
|
+
|
|
32
|
+
throw new Error(
|
|
33
|
+
`stable-diffusion-cpp-node-api: no prebuilt binary for ${key}.\n` +
|
|
34
|
+
'Supported platforms: ' + Object.keys(PLATFORMS).join(', ') + '.\n' +
|
|
35
|
+
'To build from source, install from git instead:\n' +
|
|
36
|
+
' npm install git+https://github.com/sir-ingvarr/node-stable-diffusion-cpp.git'
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
module.exports = loadBinding;
|