@gjsify/image-generation 0.64.29 → 0.64.31

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/README.md CHANGED
@@ -82,7 +82,7 @@ const engine = sdCppImageGeneration({
82
82
  });
83
83
  ```
84
84
 
85
- `availability()` is `available` when every named file exists and `unavailable` otherwise, never `downloadable`. `create()` throws and names the missing files. `LocalImageModel` takes `modelId` and `diffusionModel` (both required), `vae`, `llm`, `backend`, `defaultWidth` / `defaultHeight` (512 when absent) and `footprintBytes`. The memory check runs only when `footprintBytes` is given.
85
+ `availability()` is `available` when every named file exists and `unavailable` otherwise, never `downloadable`. `create()` throws and names the missing files. `LocalImageModel` takes `modelId` (required), then either `diffusionModel` or `checkpoint` (exactly one; `checkpoint` is an all-in-one file with the text encoders and VAE inside, as most SD 1.x / 2.x / XL checkpoints are), and `vae`, `llm`, `backend`, `defaultWidth` / `defaultHeight` (512 when absent) and `footprintBytes`. The memory check runs only when `footprintBytes` is given.
86
86
 
87
87
  **The GPU guard.** A Vulkan driver can hang a process instead of failing. Before going to the GPU, the engine leaves a pin in `gpuGuardDir` and removes it when the work finishes. A pin left over from a crash keeps the next run on the CPU and reports it through `onGpuPinned`. `gpu: 'on'` removes the pin.
88
88
 
@@ -1 +1 @@
1
- import"./_virtual/_rolldown/runtime.js";import{encodePng as e}from"./encode.js";import{currentTarget as t,hasPrebuiltBins as n,runningOnGjs as r}from"./sdcpp-bin.js";import{existsSync as i}from"node:fs";import{checkFit as a,createGpuGuard as o,defaultGuardDir as s,onBattery as c,readMachineMemory as l,systemProbe as u}from"@gjsify/machine-fit";const d={name:`sd.cpp`,locality:`local`};var GenerationAborted=class extends Error{constructor(){super(`Aborted`),this.name=`AbortError`}};function toNativeOptions(e,t,n){let r={prompt:e.prompt,width:t,height:n,seed:e.seed??Math.floor(Math.random()*2**32),sampleParams:{sampleSteps:e.steps??20,guidance:{txtCfg:e.guidance??7.5}}};return e.negativePrompt&&(r.negativePrompt=e.negativePrompt),e.sampler&&(r.sampleParams.sampleMethod=e.sampler),e.scheduler&&(r.sampleParams.scheduler=e.scheduler),e.loras?.length&&(r.loras=e.loras.map(e=>({path:e.path,multiplier:e.multiplier}))),e.init&&(r.initImage={width:e.init.width,height:e.init.height,channel:3,data:e.init.rgb},r.strength=e.init.strength),r}function contextOptions(e){let t={diffusionModelPath:e.diffusionModel};return e.vae&&(t.vaePath=e.vae),e.llm&&(t.llmPath=e.llm),e.backend&&(t.backend=e.backend),t}function toNativeContext(e,t){return contextOptions({diffusionModel:t[e.diffusionModel],vae:e.vae?t[e.vae]:void 0,llm:e.llm?t[e.llm]:void 0,backend:e.backend})}function storeSource(e){let{store:t}=e,n=t.require(e.model);return assertModelFiles(n),{modelId:n.modelId,backend:n.backend,footprintBytes:n.footprintBytes,width:n.defaultWidth,height:n.defaultHeight,availability:async()=>t.availability(n.id),async prepare(e){let r=await t.install(n.id,{monitor:e.monitor,signal:e.signal});return{diffusionModel:r.files[n.diffusionModel],vae:n.vae?r.files[n.vae]:void 0,llm:n.llm?r.files[n.llm]:void 0,backend:n.backend}}}}function missingFiles(e){return[e.diffusionModel,e.vae,e.llm].filter(e=>e!==void 0&&e!==``&&!i(e))}function localSource(e){if(!e.modelId)throw Error(`sd.cpp local model needs a modelId`);if(!e.diffusionModel)throw Error(`sd.cpp local model '${e.modelId}' needs a diffusionModel path`);return{modelId:e.modelId,backend:e.backend,footprintBytes:e.footprintBytes,width:e.defaultWidth??512,height:e.defaultHeight??512,availability:async()=>missingFiles(e).length===0?`available`:`unavailable`,async prepare(){let t=missingFiles(e);if(t.length>0)throw Error(`sd.cpp model '${e.modelId}' has missing files: ${t.join(`, `)}`);return{diffusionModel:e.diffusionModel,vae:e.vae,llm:e.llm,backend:e.backend}}}}function resolveSize(e,t){return{width:e.width??t.width,height:e.height??t.height}}let f=!1;function defaultOnGpuPinned(e){console.error(`The GPU is switched off for image generation: process ${e.pid} died while using it (${e.at}). Turn the GPU setting on to try it again.`)}function assertModelFiles(e){let t=new Set(e.files.map(e=>e.file));if(!t.has(e.diffusionModel))throw Error(`sd.cpp model '${e.id}' names '${e.diffusionModel}' as diffusionModel, but it is not in files`);if(e.vae&&!t.has(e.vae))throw Error(`sd.cpp model '${e.id}' names '${e.vae}' as vae, but it is not in files`);if(e.llm&&!t.has(e.llm))throw Error(`sd.cpp model '${e.id}' names '${e.llm}' as llm, but it is not in files`)}function isCpuOnly(e){return e===void 0?!1:e.split(`,`).every(e=>e.trim().toLowerCase()===`cpu`)}function createSdCppEngine(i,p){let m=`local`in p?localSource(p.local):storeSource(p),h=p.gpu??`auto`,g,gpuGuard=()=>g??=o({dir:p.gpuGuardDir??s(`gjsify`)}),gpuBarred=()=>{if(isCpuOnly(m.backend)||h===`off`)return!0;if(h===`on`)return gpuGuard().clear(),!1;let e=gpuGuard().pin();return e===void 0?!1:(f||(f=!0,(p.onGpuPinned??defaultOnGpuPinned)(e)),!0)};return{info:d,async availability(){let e=p.probe??u();if(c(e)||!n(t(e),r()))return`unavailable`;if(m.footprintBytes!==void 0){let t=l(e);if(a(m.footprintBytes,t).placement===`none`)return`unavailable`}return m.availability()},async create(t={}){let n=await m.prepare(t);t.signal?.throwIfAborted();let r,a,o=!1,open=async()=>{r??=await i();let e=gpuBarred(),t=contextOptions(n);e&&!t.backend&&(t.backend=`cpu`),a??=await r.StableDiffusionContext.create(t),o=!e};if(await open(),!a)throw Error(`sd.cpp context did not open`);let generate=async t=>{let{width:n,height:r}=resolveSize(t,m),i=toNativeOptions(t,n,r),o=(await a.generateImage(i))[0];if(!o)throw Error(`The engine returned no image.`);if(o.channel!==3)throw Error(`sd.cpp returned ${o.channel} channels, expected 3`);let s=new Uint8Array(o.data);return{blob:e({width:o.width,height:o.height,rgb:s}),width:o.width,height:o.height,seed:i.seed,modelId:m.modelId}};return{info:d,modelId:m.modelId,get backend(){return o?`gpu`:`cpu`},async generate(e,t={}){let{signal:n,onProgress:i}=t;if(n?.aborted)throw new GenerationAborted;let onAbort=()=>a.abort();n?.addEventListener(`abort`,onAbort,{once:!0});let work=async()=>(r.setProgressCallback(i?({step:e,steps:t,time:n})=>i({step:e,steps:t,seconds:n}):null),await generate(e));try{return o?await gpuGuard().during(work):await work()}catch(e){throw n?.aborted?new GenerationAborted:e}finally{n?.removeEventListener(`abort`,onAbort),r.setProgressCallback(null)}},async destroy(){a?.close(),a=void 0}}}}}export{GenerationAborted,d as SDCPP_ENGINE,createSdCppEngine,toNativeContext,toNativeOptions};
1
+ import"./_virtual/_rolldown/runtime.js";import{encodePng as e}from"./encode.js";import{currentTarget as t,hasPrebuiltBins as n,runningOnGjs as r}from"./sdcpp-bin.js";import{existsSync as i}from"node:fs";import{checkFit as a,createGpuGuard as o,defaultGuardDir as s,onBattery as c,readMachineMemory as l,systemProbe as u}from"@gjsify/machine-fit";const d={name:`sd.cpp`,locality:`local`};var GenerationAborted=class extends Error{constructor(){super(`Aborted`),this.name=`AbortError`}};function toNativeOptions(e,t,n){let r={prompt:e.prompt,width:t,height:n,seed:e.seed??Math.floor(Math.random()*2**32),sampleParams:{sampleSteps:e.steps??20,guidance:{txtCfg:e.guidance??7.5}}};return e.negativePrompt&&(r.negativePrompt=e.negativePrompt),e.sampler&&(r.sampleParams.sampleMethod=e.sampler),e.scheduler&&(r.sampleParams.scheduler=e.scheduler),e.loras?.length&&(r.loras=e.loras.map(e=>({path:e.path,multiplier:e.multiplier}))),e.init&&(r.initImage={width:e.init.width,height:e.init.height,channel:3,data:e.init.rgb},r.strength=e.init.strength),r}function contextOptions(e){let t={};return e.checkpoint&&(t.modelPath=e.checkpoint),e.diffusionModel&&(t.diffusionModelPath=e.diffusionModel),e.vae&&(t.vaePath=e.vae),e.llm&&(t.llmPath=e.llm),e.backend&&(t.backend=e.backend),t}function toNativeContext(e,t){return contextOptions({diffusionModel:t[e.diffusionModel],vae:e.vae?t[e.vae]:void 0,llm:e.llm?t[e.llm]:void 0,backend:e.backend})}function storeSource(e){let{store:t}=e,n=t.require(e.model);return assertModelFiles(n),{modelId:n.modelId,backend:n.backend,footprintBytes:n.footprintBytes,width:n.defaultWidth,height:n.defaultHeight,availability:async()=>t.availability(n.id),async prepare(e){let r=await t.install(n.id,{monitor:e.monitor,signal:e.signal});return{diffusionModel:r.files[n.diffusionModel],vae:n.vae?r.files[n.vae]:void 0,llm:n.llm?r.files[n.llm]:void 0,backend:n.backend}}}}function missingFiles(e){return[e.diffusionModel,e.checkpoint,e.vae,e.llm].filter(e=>e!==void 0&&e!==``&&!i(e))}function localSource(e){if(!e.modelId)throw Error(`sd.cpp local model needs a modelId`);if(!e.diffusionModel==!e.checkpoint)throw Error(`sd.cpp local model '${e.modelId}' needs a diffusionModel or a checkpoint path, not both`);return{modelId:e.modelId,backend:e.backend,footprintBytes:e.footprintBytes,width:e.defaultWidth??512,height:e.defaultHeight??512,availability:async()=>missingFiles(e).length===0?`available`:`unavailable`,async prepare(){let t=missingFiles(e);if(t.length>0)throw Error(`sd.cpp model '${e.modelId}' has missing files: ${t.join(`, `)}`);return{diffusionModel:e.diffusionModel,checkpoint:e.checkpoint,vae:e.vae,llm:e.llm,backend:e.backend}}}}function resolveSize(e,t){return{width:e.width??t.width,height:e.height??t.height}}let f=!1;function defaultOnGpuPinned(e){console.error(`The GPU is switched off for image generation: process ${e.pid} died while using it (${e.at}). Turn the GPU setting on to try it again.`)}function assertModelFiles(e){let t=new Set(e.files.map(e=>e.file));if(!t.has(e.diffusionModel))throw Error(`sd.cpp model '${e.id}' names '${e.diffusionModel}' as diffusionModel, but it is not in files`);if(e.vae&&!t.has(e.vae))throw Error(`sd.cpp model '${e.id}' names '${e.vae}' as vae, but it is not in files`);if(e.llm&&!t.has(e.llm))throw Error(`sd.cpp model '${e.id}' names '${e.llm}' as llm, but it is not in files`)}function isCpuOnly(e){return e===void 0?!1:e.split(`,`).every(e=>e.trim().toLowerCase()===`cpu`)}function createSdCppEngine(i,p){let m=`local`in p?localSource(p.local):storeSource(p),h=p.gpu??`auto`,g,gpuGuard=()=>g??=o({dir:p.gpuGuardDir??s(`gjsify`)}),gpuBarred=()=>{if(isCpuOnly(m.backend)||h===`off`)return!0;if(h===`on`)return gpuGuard().clear(),!1;let e=gpuGuard().pin();return e===void 0?!1:(f||(f=!0,(p.onGpuPinned??defaultOnGpuPinned)(e)),!0)};return{info:d,async availability(){let e=p.probe??u();if(c(e)||!n(t(e),r()))return`unavailable`;if(m.footprintBytes!==void 0){let t=l(e);if(a(m.footprintBytes,t).placement===`none`)return`unavailable`}return m.availability()},async create(t={}){let n=await m.prepare(t);t.signal?.throwIfAborted();let r,a,o=!1,open=async()=>{r??=await i();let e=gpuBarred(),t=contextOptions(n);e&&!t.backend&&(t.backend=`cpu`),a??=await r.StableDiffusionContext.create(t),o=!e};if(await open(),!a)throw Error(`sd.cpp context did not open`);let generate=async t=>{let{width:n,height:r}=resolveSize(t,m),i=toNativeOptions(t,n,r),o=(await a.generateImage(i))[0];if(!o)throw Error(`The engine returned no image.`);if(o.channel!==3)throw Error(`sd.cpp returned ${o.channel} channels, expected 3`);let s=new Uint8Array(o.data);return{blob:e({width:o.width,height:o.height,rgb:s}),width:o.width,height:o.height,seed:i.seed,modelId:m.modelId}};return{info:d,modelId:m.modelId,get backend(){return o?`gpu`:`cpu`},async generate(e,t={}){let{signal:n,onProgress:i}=t;if(n?.aborted)throw new GenerationAborted;let onAbort=()=>a.abort();n?.addEventListener(`abort`,onAbort,{once:!0});let work=async()=>(r.setProgressCallback(i?({step:e,steps:t,time:n})=>i({step:e,steps:t,seconds:n}):null),await generate(e));try{return o?await gpuGuard().during(work):await work()}catch(e){throw n?.aborted?new GenerationAborted:e}finally{n?.removeEventListener(`abort`,onAbort),r.setProgressCallback(null)}},async destroy(){a?.close(),a=void 0}}}}}export{GenerationAborted,d as SDCPP_ENGINE,createSdCppEngine,toNativeContext,toNativeOptions};
@@ -50,8 +50,13 @@ export interface SdCppEngineOptions<E extends ImageModelEntry = ImageModelEntry>
50
50
  export interface LocalImageModel {
51
51
  /** What a gallery records next to the image (`ImageGenerator.modelId`). */
52
52
  readonly modelId: string;
53
- /** Path of the diffusion checkpoint. */
54
- readonly diffusionModel: string;
53
+ /** Path of the diffusion model, for families that ship it apart from the text encoder and VAE. */
54
+ readonly diffusionModel?: string;
55
+ /**
56
+ * Path of an all-in-one checkpoint (diffusion model, text encoders and VAE in one file, as
57
+ * SD 1.x / 2.x / XL are usually shared). Use this or `diffusionModel`, not both.
58
+ */
59
+ readonly checkpoint?: string;
55
60
  /** Path of the VAE, if the model needs a separate one. */
56
61
  readonly vae?: string;
57
62
  /** Path of the text encoder / LLM, if the model family needs one. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gjsify/image-generation",
3
- "version": "0.64.29",
3
+ "version": "0.64.31",
4
4
  "description": "Image generation in the WebML CG lifecycle shape: an ImageGenerator with availability(), create({ monitor, signal }) and destroy(), and a local stable-diffusion.cpp engine for diffusion models on Node and GJS.",
5
5
  "module": "lib/esm/index.js",
6
6
  "types": "lib/types/index.d.ts",
@@ -46,13 +46,13 @@
46
46
  "webml"
47
47
  ],
48
48
  "dependencies": {
49
- "@gjsify/buffer": "^0.64.29",
50
- "@gjsify/fs": "^0.64.29",
51
- "@gjsify/machine-fit": "^0.64.29",
52
- "@gjsify/model-store": "^0.64.29",
53
- "@gjsify/path": "^0.64.29",
54
- "@gjsify/process": "^0.64.29",
55
- "@gjsify/zlib": "^0.64.29"
49
+ "@gjsify/buffer": "^0.64.31",
50
+ "@gjsify/fs": "^0.64.31",
51
+ "@gjsify/machine-fit": "^0.64.31",
52
+ "@gjsify/model-store": "^0.64.31",
53
+ "@gjsify/path": "^0.64.31",
54
+ "@gjsify/process": "^0.64.31",
55
+ "@gjsify/zlib": "^0.64.31"
56
56
  },
57
57
  "peerDependencies": {
58
58
  "@gjsify/napi": "^0.64.19"
@@ -63,8 +63,8 @@
63
63
  }
64
64
  },
65
65
  "devDependencies": {
66
- "@gjsify/cli": "^0.64.29",
67
- "@gjsify/unit": "^0.64.29",
66
+ "@gjsify/cli": "^0.64.31",
67
+ "@gjsify/unit": "^0.64.31",
68
68
  "@types/node": "^25.9.2",
69
69
  "typescript": "^6.0.3"
70
70
  },