Skip to content

Commit 36d392a

Browse files
Anil-matchaclaude
andcommitted
feat: add local model inference via sd.cpp (Electron-only)
- Scaffold full IPC bridge: preload.js exposes localAI API to renderer - electron/lib/localInference.js: binary download (Metal-enabled macOS build), model management, auxiliary file downloads (Qwen3-4B LLM + FLUX VAE for Z-Image), and generation via sd-cli with DYLD_LIBRARY_PATH + xattr quarantine stripping - electron/lib/modelCatalog.js: Z-Image Turbo/Base (featured) + SD 1.5/SDXL models - Z-Image requires 3 components loaded via --diffusion-model + --llm + --vae flags - ImageStudio: Local/API toggle (Electron-only), local model selector, progress bar - SettingsModal: tabbed UI with Local Models tab (hidden on web/hosted version) - LocalModelManager: engine status bar, per-model download cards, auxiliary component download UI (text encoder + VAE) for Z-Image models - localInferenceClient.js: safe wrapper with isLocalAIAvailable() guard - All local features gated behind isLocalAIAvailable() — web version unaffected Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 5cbcd88 commit 36d392a

10 files changed

Lines changed: 1360 additions & 75 deletions

File tree

electron/lib/localInference.js

Lines changed: 462 additions & 0 deletions
Large diffs are not rendered by default.

electron/lib/modelCatalog.js

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
// Curated local model catalog for sd.cpp
2+
// All models must be publicly available (no auth required)
3+
4+
// Shared auxiliary files needed by Z-Image type models
5+
const ZIMAGE_AUXILIARY = {
6+
llm: {
7+
id: '__llm__',
8+
filename: 'Qwen3-4B-Instruct-2507-UD-Q4_K_XL.gguf',
9+
displayName: 'Qwen3-4B Text Encoder',
10+
sizeGB: 2.4,
11+
downloadUrl: 'https://huggingface.co/unsloth/Qwen3-4B-Instruct-2507-GGUF/resolve/main/Qwen3-4B-Instruct-2507-UD-Q4_K_XL.gguf',
12+
},
13+
vae: {
14+
id: '__vae__',
15+
filename: 'ae.safetensors',
16+
displayName: 'FLUX VAE',
17+
sizeGB: 0.33,
18+
downloadUrl: 'https://huggingface.co/Comfy-Org/z_image_turbo/resolve/main/split_files/vae/ae.safetensors',
19+
},
20+
};
21+
22+
const LOCAL_MODEL_CATALOG = [
23+
// ── Z-Image (Tongyi-MAI) — native sd.cpp GGUF support ──────────────────
24+
// Requires auxiliary files: Qwen3-4B LLM text encoder + FLUX VAE
25+
{
26+
id: 'z-image-turbo',
27+
name: 'Z-Image Turbo',
28+
description: 'WaveSpeed\'s featured local model — ultra-fast 8-step generation. No API key needed. Requires text encoder + VAE (~2.7 GB extra).',
29+
type: 'z-image',
30+
filename: 'z_image_turbo-Q4_K.gguf',
31+
sizeGB: 2.5,
32+
downloadUrl: 'https://huggingface.co/leejet/Z-Image-Turbo-GGUF/resolve/main/z_image_turbo-Q4_K.gguf',
33+
aspectRatios: ['1:1', '4:3', '3:4', '16:9', '9:16'],
34+
defaultWidth: 1024,
35+
defaultHeight: 1024,
36+
defaultSteps: 8,
37+
defaultGuidance: 1.0,
38+
sampler: 'euler',
39+
scheduler: 'simple',
40+
tags: ['turbo', 'fast', 'local', 'featured'],
41+
featured: true,
42+
requiresAuxiliary: true,
43+
},
44+
{
45+
id: 'z-image-base',
46+
name: 'Z-Image Base',
47+
description: 'Full-quality model from Tongyi-MAI — higher detail, 50-step generation. Requires text encoder + VAE (~2.7 GB extra).',
48+
type: 'z-image',
49+
filename: 'Z-Image-Q4_K_M.gguf',
50+
sizeGB: 3.5,
51+
downloadUrl: 'https://huggingface.co/unsloth/Z-Image-GGUF/resolve/main/Z-Image-Q4_K_M.gguf',
52+
aspectRatios: ['1:1', '4:3', '3:4', '16:9', '9:16'],
53+
defaultWidth: 1024,
54+
defaultHeight: 1024,
55+
defaultSteps: 50,
56+
defaultGuidance: 7.5,
57+
sampler: 'euler',
58+
scheduler: 'simple',
59+
tags: ['high-quality', 'local', 'detailed'],
60+
featured: true,
61+
requiresAuxiliary: true,
62+
},
63+
// ── Classic SD 1.5 models ───────────────────────────────────────────────
64+
{
65+
id: 'dreamshaper-8',
66+
name: 'Dreamshaper 8',
67+
description: 'Versatile SD 1.5 model — great for portraits, landscapes, and artistic styles.',
68+
type: 'sd1',
69+
filename: 'DreamShaper_8_pruned.safetensors',
70+
sizeGB: 2.1,
71+
downloadUrl: 'https://huggingface.co/Lykon/dreamshaper-8/resolve/main/DreamShaper_8_pruned.safetensors',
72+
aspectRatios: ['1:1', '4:3', '3:4', '16:9', '9:16'],
73+
defaultWidth: 512,
74+
defaultHeight: 512,
75+
defaultSteps: 20,
76+
defaultGuidance: 7.5,
77+
sampler: 'euler_a',
78+
tags: ['photorealistic', 'artistic', 'versatile'],
79+
},
80+
{
81+
id: 'realistic-vision-v51',
82+
name: 'Realistic Vision v5.1',
83+
description: 'Highly photorealistic people and scenes, based on SD 1.5.',
84+
type: 'sd1',
85+
filename: 'realisticVisionV51_v51VAE.safetensors',
86+
sizeGB: 2.1,
87+
downloadUrl: 'https://huggingface.co/SG161222/Realistic_Vision_V5.1_noVAE/resolve/main/Realistic_Vision_V5.1_fp16-no-ema.safetensors',
88+
aspectRatios: ['1:1', '4:3', '3:4', '16:9', '9:16'],
89+
defaultWidth: 512,
90+
defaultHeight: 768,
91+
defaultSteps: 25,
92+
defaultGuidance: 7,
93+
sampler: 'euler_a',
94+
tags: ['photorealistic', 'portraits', 'people'],
95+
},
96+
{
97+
id: 'anything-v5',
98+
name: 'Anything v5',
99+
description: 'High quality anime and illustration style image generation.',
100+
type: 'sd1',
101+
filename: 'anything-v5-PrtRE.safetensors',
102+
sizeGB: 2.1,
103+
downloadUrl: 'https://huggingface.co/stablediffusionapi/anything-v5/resolve/main/anything-v5-PrtRE.safetensors',
104+
aspectRatios: ['1:1', '4:3', '3:4', '16:9', '9:16'],
105+
defaultWidth: 512,
106+
defaultHeight: 768,
107+
defaultSteps: 20,
108+
defaultGuidance: 7,
109+
sampler: 'euler_a',
110+
tags: ['anime', 'illustration', 'artistic'],
111+
},
112+
// ── SDXL ───────────────────────────────────────────────────────────────
113+
{
114+
id: 'stable-diffusion-xl-base',
115+
name: 'SDXL Base 1.0',
116+
description: 'Official Stable Diffusion XL base model — higher resolution, excellent quality.',
117+
type: 'sdxl',
118+
filename: 'sd_xl_base_1.0.safetensors',
119+
sizeGB: 6.9,
120+
downloadUrl: 'https://huggingface.co/stabilityai/stable-diffusion-xl-base-1.0/resolve/main/sd_xl_base_1.0.safetensors',
121+
aspectRatios: ['1:1', '4:3', '3:4', '16:9', '9:16'],
122+
defaultWidth: 1024,
123+
defaultHeight: 1024,
124+
defaultSteps: 30,
125+
defaultGuidance: 7.5,
126+
sampler: 'dpmpp2m',
127+
tags: ['sdxl', 'high-quality', 'versatile'],
128+
},
129+
];
130+
131+
module.exports = { LOCAL_MODEL_CATALOG, ZIMAGE_AUXILIARY };

electron/main.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
const { app, BrowserWindow, shell } = require('electron');
22
const path = require('path');
3+
const { register: registerLocalInference } = require('./lib/localInference');
34

45
// Ubuntu 24.04+ sets kernel.apparmor_restrict_unprivileged_userns=1 which
56
// blocks Chromium's user namespace sandbox. The .deb package ships an AppArmor
@@ -24,6 +25,7 @@ function createWindow() {
2425
webSecurity: false,
2526
contextIsolation: true,
2627
nodeIntegration: false,
28+
preload: path.join(__dirname, 'preload.js'),
2729
},
2830
...(isMac ? { titleBarStyle: 'hiddenInset' } : {}),
2931
backgroundColor: '#0d0d0d',
@@ -57,6 +59,7 @@ function createWindow() {
5759

5860
app.whenReady().then(() => {
5961
createWindow();
62+
registerLocalInference();
6063

6164
app.on('activate', () => {
6265
if (BrowserWindow.getAllWindows().length === 0) {

electron/preload.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
const { contextBridge, ipcRenderer } = require('electron');
2+
3+
contextBridge.exposeInMainWorld('localAI', {
4+
isElectron: true,
5+
6+
// Binary management
7+
getBinaryStatus: () => ipcRenderer.invoke('local-ai:binary-status'),
8+
downloadBinary: () => ipcRenderer.invoke('local-ai:download-binary'),
9+
10+
// Model management
11+
listModels: () => ipcRenderer.invoke('local-ai:list-models'),
12+
downloadModel: (modelId) => ipcRenderer.invoke('local-ai:download-model', modelId),
13+
downloadAuxiliary: (auxKey) => ipcRenderer.invoke('local-ai:download-auxiliary', auxKey),
14+
deleteModel: (modelId) => ipcRenderer.invoke('local-ai:delete-model', modelId),
15+
cancelDownload: (modelId) => ipcRenderer.invoke('local-ai:cancel-download', modelId),
16+
17+
// Generation
18+
generate: (params) => ipcRenderer.invoke('local-ai:generate', params),
19+
cancelGeneration: () => ipcRenderer.invoke('local-ai:cancel-generation'),
20+
21+
// Progress events — returns an unsubscribe function
22+
onProgress: (callback) => {
23+
const listener = (_, data) => callback(data);
24+
ipcRenderer.on('local-ai:progress', listener);
25+
return () => ipcRenderer.removeListener('local-ai:progress', listener);
26+
},
27+
onDownloadProgress: (callback) => {
28+
const listener = (_, data) => callback(data);
29+
ipcRenderer.on('local-ai:download-progress', listener);
30+
return () => ipcRenderer.removeListener('local-ai:download-progress', listener);
31+
}
32+
});

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
"setup": "npm install && npm run build:studio",
1818
"vite:dev": "vite",
1919
"vite:build": "vite build",
20+
"electron:dev": "npm run vite:build && electron .",
2021
"electron:build": "vite build && electron-builder --mac",
2122
"electron:build:win": "vite build && electron-builder --win",
2223
"electron:build:linux": "vite build && electron-builder --linux",

0 commit comments

Comments
 (0)