@bendyline/gezel-inference 1.0.5 → 1.0.7
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/dist/cache/controller.d.ts +2 -0
- package/dist/cache/controller.js +38 -5
- package/dist/catalog/chat-models/index.json +1 -1
- package/dist/catalog/chat-models/lf/lfm3-1.7b-q4/manifest.json +114 -0
- package/dist/catalog/chat-models/lf/lfm3-1.7b-q4/versions/1.0.0/manifest.json +15 -0
- package/dist/catalog/chat-models/raw-index.json +1 -1
- package/dist/catalog/image-loras/index.json +1 -0
- package/dist/catalog/image-loras/kr/krea-2-ink-wash/manifest.json +31 -0
- package/dist/catalog/image-loras/kr/krea-2-ink-wash/versions/1.0.0/manifest.json +15 -0
- package/dist/catalog/image-loras/kr/krea-2-isometric-3d/manifest.json +32 -0
- package/dist/catalog/image-loras/kr/krea-2-isometric-3d/versions/1.0.0/manifest.json +15 -0
- package/dist/catalog/image-loras/kr/krea-2-kids-drawing/manifest.json +31 -0
- package/dist/catalog/image-loras/kr/krea-2-kids-drawing/versions/1.0.0/manifest.json +15 -0
- package/dist/catalog/image-loras/kr/krea-2-pixel-art/manifest.json +31 -0
- package/dist/catalog/image-loras/kr/krea-2-pixel-art/versions/1.0.0/manifest.json +15 -0
- package/dist/catalog/image-loras/kr/krea-2-soft-watercolor/manifest.json +31 -0
- package/dist/catalog/image-loras/kr/krea-2-soft-watercolor/versions/1.0.0/manifest.json +15 -0
- package/dist/catalog/image-loras/kr/krea-2-turbo-4step/manifest.json +31 -0
- package/dist/catalog/image-loras/kr/krea-2-turbo-4step/versions/1.0.0/manifest.json +15 -0
- package/dist/catalog/image-loras/mo/moss-image-gouache/manifest.json +29 -0
- package/dist/catalog/image-loras/mo/moss-image-gouache/versions/1.0.0/manifest.json +15 -0
- package/dist/catalog/image-loras/mo/moss-image-sft-4step/manifest.json +30 -0
- package/dist/catalog/image-loras/mo/moss-image-sft-4step/versions/1.0.0/manifest.json +15 -0
- package/dist/catalog/image-loras/qw/qwen-image-edit-2511-lightning/manifest.json +31 -0
- package/dist/catalog/image-loras/qw/qwen-image-edit-2511-lightning/versions/1.0.0/manifest.json +15 -0
- package/dist/catalog/image-loras/raw-index.json +1 -0
- package/dist/catalog/image-loras/sd/sdxl-3d-render/manifest.json +29 -0
- package/dist/catalog/image-loras/sd/sdxl-3d-render/versions/1.0.0/manifest.json +15 -0
- package/dist/catalog/image-loras/sd/sdxl-coloring-book/manifest.json +30 -0
- package/dist/catalog/image-loras/sd/sdxl-coloring-book/versions/1.0.0/manifest.json +15 -0
- package/dist/catalog/image-loras/sd/sdxl-papercut/manifest.json +29 -0
- package/dist/catalog/image-loras/sd/sdxl-papercut/versions/1.0.0/manifest.json +15 -0
- package/dist/catalog/image-loras/sd/sdxl-pixel-art/manifest.json +29 -0
- package/dist/catalog/image-loras/sd/sdxl-pixel-art/versions/1.0.0/manifest.json +15 -0
- package/dist/catalog/image-models/fl/flux-2-klein-4b-q4/manifest.json +3 -0
- package/dist/catalog/image-models/index.json +1 -1
- package/dist/catalog/image-models/qw/qwen-image-edit-2511-q4/manifest.json +44 -0
- package/dist/catalog/image-models/qw/qwen-image-edit-2511-q4/versions/1.0.0/manifest.json +36 -0
- package/dist/catalog/image-models/raw-index.json +1 -1
- package/dist/catalog/knowledge-catalogs/index.json +1 -1
- package/dist/catalog/knowledge-catalogs/raw-index.json +1 -1
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-arts/versions/2026.10.2/manifest.json +181 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-astronomy/versions/2026.10.2/manifest.json +181 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-biology/versions/2026.10.2/manifest.json +217 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-business/versions/2026.10.2/manifest.json +197 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-film-tv/versions/2026.10.2/manifest.json +197 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-food-drink/versions/2026.10.2/manifest.json +185 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-games/versions/2026.10.2/manifest.json +181 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-general/manifest.json +55 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-general/versions/2026.10.2/manifest.json +165 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-geography/versions/2026.10.2/manifest.json +217 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-history/versions/2026.10.2/manifest.json +217 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-language/versions/2026.10.2/manifest.json +181 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-law/versions/2026.10.2/manifest.json +185 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-literature/versions/2026.10.2/manifest.json +193 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-mathematics/versions/2026.10.2/manifest.json +189 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-medicine/versions/2026.10.2/manifest.json +185 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-military/versions/2026.10.2/manifest.json +189 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-music/versions/2026.10.2/manifest.json +197 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-people-arts/versions/2026.10.2/manifest.json +209 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-people-business/versions/2026.10.2/manifest.json +189 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-people-entertainment/versions/2026.10.2/manifest.json +201 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-people-military/versions/2026.10.2/manifest.json +185 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-people-other/versions/2026.10.2/manifest.json +165 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-people-politics/versions/2026.10.2/manifest.json +197 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-people-religion/versions/2026.10.2/manifest.json +201 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-people-science/versions/2026.10.2/manifest.json +205 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-people-sports/versions/2026.10.2/manifest.json +237 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-politics/versions/2026.10.2/manifest.json +197 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-religion-philosophy/versions/2026.10.2/manifest.json +189 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-science/versions/2026.10.2/manifest.json +185 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-sports/versions/2026.10.2/manifest.json +193 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-technology/versions/2026.10.2/manifest.json +185 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-transportation/versions/2026.10.2/manifest.json +189 -0
- package/dist/catalog/provenance.json +2 -1
- package/dist/engine-components.js +2 -2
- package/dist/engines/electron-native-reuse.d.ts +7 -1
- package/dist/engines/electron-native-reuse.js +12 -4
- package/dist/engines/native-file-manifest.json +546 -406
- package/dist/engines/native-manifest.d.ts +4 -4
- package/dist/engines/native-manifest.js +36 -36
- package/dist/engines/resolver.d.ts +1 -1
- package/dist/engines/resolver.js +48 -51
- package/dist/http/auth-context.d.ts +12 -0
- package/dist/http/routes/image-ensure-routes.d.ts +4 -0
- package/dist/http/routes/image-ensure-routes.js +102 -0
- package/dist/http/routes/v1-image-styles.d.ts +5 -0
- package/dist/http/routes/v1-image-styles.js +91 -0
- package/dist/http/routes/v1-images.d.ts +1 -0
- package/dist/http/routes/v1-images.js +15 -93
- package/dist/inference-service.js +2 -1
- package/dist/knowledge/worker-host.js +2 -0
- package/dist/machine-engine/inspect-runtime.js +12 -3
- package/dist/memory/embed-core.js +3 -0
- package/dist/memory/embeddings.js +2 -0
- package/dist/models/image-fit.js +2 -2
- package/dist/models/weight-facts.d.ts +5 -0
- package/dist/models/weight-facts.js +5 -2
- package/dist/providers/ds4/build-provider.js +1 -1
- package/dist/providers/ds4/residency.js +3 -3
- package/dist/providers/image/img2img-support.d.ts +36 -1
- package/dist/providers/image/img2img-support.js +30 -1
- package/dist/providers/image/local-engine.d.ts +1 -0
- package/dist/providers/image/local-engine.js +8 -2
- package/dist/providers/image/local-factory.d.ts +10 -1
- package/dist/providers/image/local-factory.js +33 -6
- package/dist/providers/image/mock.d.ts +8 -1
- package/dist/providers/image/mock.js +46 -1
- package/dist/providers/image/pull-registry.d.ts +25 -3
- package/dist/providers/image/pull-registry.js +96 -34
- package/dist/providers/image/sd-cpp.d.ts +63 -4
- package/dist/providers/image/sd-cpp.js +361 -38
- package/dist/providers/image/types.d.ts +76 -4
- package/dist/providers/image/types.js +8 -0
- package/dist/providers/llama-cpp/build-provider.js +3 -2
- package/dist/providers/llama-cpp/cache-adapter.d.ts +3 -0
- package/dist/providers/llama-cpp/cache-adapter.js +18 -4
- package/dist/providers/llama-cpp/devices.js +2 -2
- package/dist/providers/llama-cpp/engine-flags.d.ts +5 -0
- package/dist/providers/llama-cpp/engine-flags.js +5 -2
- package/dist/providers/llama-cpp/provider.js +20 -4
- package/dist/providers/mlx/provider.js +13 -4
- package/dist/providers/mlx/python/gezel_mlx_server.py +9 -2
- package/dist/providers/mlx/python/spec_decode.py +44 -5
- package/dist/providers/native/capacity-broker.d.ts +1 -1
- package/dist/providers/native/capacity-broker.js +8 -8
- package/dist/providers/native/capacity-budget.js +5 -4
- package/dist/providers/native/device-capacity-ledger.d.ts +2 -0
- package/dist/providers/native/device-capacity-ledger.js +36 -5
- package/dist/providers/native/device-capacity.js +4 -3
- package/dist/providers/native/local-engine-runtime.js +4 -4
- package/dist/providers/native/process-identity.d.ts +2 -0
- package/dist/providers/native/process-identity.js +70 -0
- package/dist/relevance/relevance-core.js +2 -0
- package/dist/relevance/relevance-model.js +2 -0
- package/dist/relevance/settings.js +2 -2
- package/dist/runtime-lock.d.ts +43 -5
- package/dist/runtime-lock.js +189 -26
- package/dist/system/host-memory.d.ts +46 -0
- package/dist/system/host-memory.js +89 -0
- package/dist/system/memory.js +5 -4
- package/dist/transformers-cache.js +2 -0
- package/dist/utils/download-common.d.ts +1 -1
- package/package.json +8 -8
|
@@ -8,22 +8,33 @@ import { deflateSync } from 'node:zlib';
|
|
|
8
8
|
export class MockImageProvider {
|
|
9
9
|
name = 'mock';
|
|
10
10
|
installed = new Map();
|
|
11
|
+
loras = new Map();
|
|
11
12
|
/** Internal counter driving deterministic seed generation on "random" seeds. */
|
|
12
13
|
nextSeed = 1;
|
|
13
14
|
constructor(opts = {}) {
|
|
14
15
|
for (const m of opts.preinstalled ?? []) {
|
|
15
16
|
this.installed.set(m.id, m);
|
|
16
17
|
}
|
|
18
|
+
for (const l of opts.loras ?? [])
|
|
19
|
+
this.loras.set(l.id, l);
|
|
17
20
|
}
|
|
18
21
|
/** Number of input images received on the most recent generate(). Tests use this. */
|
|
19
22
|
lastInputImageCount = 0;
|
|
23
|
+
/** The most recent generate() input. Tests use this. */
|
|
24
|
+
lastInput;
|
|
20
25
|
async generate(input) {
|
|
21
26
|
this.lastInputImageCount = input.inputImages?.length ?? 0;
|
|
27
|
+
this.lastInput = input;
|
|
22
28
|
const width = clamp(input.width ?? 16, 1, 512);
|
|
23
29
|
const height = clamp(input.height ?? 16, 1, 512);
|
|
24
30
|
const steps = clamp(input.steps ?? 1, 1, 100);
|
|
25
31
|
const seed = input.seed ?? this.nextSeed++;
|
|
26
32
|
const model = input.model ?? [...this.installed.keys()][0] ?? 'mock-model';
|
|
33
|
+
// Applied as the real engine applies them: installed, and fitting the model.
|
|
34
|
+
const loras = (input.loras ?? []).flatMap(({ id }) => {
|
|
35
|
+
const lora = this.loras.get(id);
|
|
36
|
+
return lora?.baseModels.includes(model) ? [{ id: lora.id, name: lora.name }] : [];
|
|
37
|
+
});
|
|
27
38
|
const rgba = solidFill(width, height, colourForPrompt(input.prompt));
|
|
28
39
|
const png = encodePng(rgba, width, height);
|
|
29
40
|
return {
|
|
@@ -35,11 +46,45 @@ export class MockImageProvider {
|
|
|
35
46
|
widthPx: width,
|
|
36
47
|
heightPx: height,
|
|
37
48
|
durationMs: 1,
|
|
49
|
+
...(loras.length > 0 ? { loras } : {}),
|
|
38
50
|
},
|
|
39
51
|
};
|
|
40
52
|
}
|
|
41
53
|
async listInstalledModels() {
|
|
42
|
-
return [...this.installed.values()]
|
|
54
|
+
return [...this.installed.values()].map((m) => {
|
|
55
|
+
const loras = [...this.loras.values()]
|
|
56
|
+
.filter((l) => l.baseModels.includes(m.id))
|
|
57
|
+
.map(({ id, name, purpose, steps }) => ({
|
|
58
|
+
id,
|
|
59
|
+
name,
|
|
60
|
+
purpose,
|
|
61
|
+
...(steps !== undefined ? { steps } : {}),
|
|
62
|
+
}));
|
|
63
|
+
return loras.length > 0 ? { ...m, loras } : m;
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
async listLoras() {
|
|
67
|
+
return [...this.loras.values()];
|
|
68
|
+
}
|
|
69
|
+
async *pullLora(id, spec) {
|
|
70
|
+
yield {
|
|
71
|
+
type: 'progress',
|
|
72
|
+
bytesWritten: spec.approxSizeBytes,
|
|
73
|
+
totalBytes: spec.approxSizeBytes,
|
|
74
|
+
};
|
|
75
|
+
this.loras.set(id, {
|
|
76
|
+
id,
|
|
77
|
+
name: spec.name,
|
|
78
|
+
purpose: spec.purpose,
|
|
79
|
+
...(spec.render?.steps !== undefined ? { steps: spec.render.steps } : {}),
|
|
80
|
+
baseModels: spec.baseModels,
|
|
81
|
+
approxSizeBytes: spec.approxSizeBytes,
|
|
82
|
+
installedAt: new Date().toISOString(),
|
|
83
|
+
});
|
|
84
|
+
yield { type: 'done', id };
|
|
85
|
+
}
|
|
86
|
+
async deleteLora(id) {
|
|
87
|
+
this.loras.delete(id);
|
|
43
88
|
}
|
|
44
89
|
async *pullModel(id, spec, signal) {
|
|
45
90
|
const total = spec.approxSizeBytes;
|
|
@@ -34,6 +34,13 @@ type Listener = (event: ImageModelPullEvent) => void;
|
|
|
34
34
|
export interface ImageProviderSource {
|
|
35
35
|
current(): Promise<Pick<ImageProvider, 'pullModel'>>;
|
|
36
36
|
}
|
|
37
|
+
/** One download the registry runs: its expected size and the event stream. */
|
|
38
|
+
export interface ImagePullJob {
|
|
39
|
+
totalBytes: number;
|
|
40
|
+
run(signal: AbortSignal): AsyncIterable<ImageModelPullEvent>;
|
|
41
|
+
}
|
|
42
|
+
/** Turns a catalog id into a pull, or `null` for an id the catalog lacks. */
|
|
43
|
+
export type ImagePullResolver = (id: string) => Promise<ImagePullJob | null>;
|
|
37
44
|
/**
|
|
38
45
|
* Owns the lifecycle of every in-flight image-model pull.
|
|
39
46
|
*
|
|
@@ -52,11 +59,18 @@ export interface ImageProviderSource {
|
|
|
52
59
|
export declare class ImageModelPullRegistry {
|
|
53
60
|
private readonly entries;
|
|
54
61
|
private readonly controllers;
|
|
55
|
-
private readonly
|
|
56
|
-
private readonly
|
|
62
|
+
private readonly resolve;
|
|
63
|
+
private readonly kind;
|
|
64
|
+
/**
|
|
65
|
+
* Image models by default; `resolve` makes it a registry for another kind
|
|
66
|
+
* of download (LoRAs), whose ids then never share keys with models.
|
|
67
|
+
*/
|
|
57
68
|
constructor(opts: {
|
|
58
69
|
imageProvider: ImageProviderSource;
|
|
59
70
|
catalog: CatalogService;
|
|
71
|
+
} | {
|
|
72
|
+
resolve: ImagePullResolver;
|
|
73
|
+
kind: string;
|
|
60
74
|
});
|
|
61
75
|
/**
|
|
62
76
|
* Start a pull for `id`. Idempotent — when a pull is already in flight
|
|
@@ -101,6 +115,14 @@ export declare class ImageModelPullRegistry {
|
|
|
101
115
|
}
|
|
102
116
|
export declare class UnknownImageModelError extends Error {
|
|
103
117
|
readonly id: string;
|
|
104
|
-
constructor(id: string);
|
|
118
|
+
constructor(id: string, kind?: string);
|
|
105
119
|
}
|
|
120
|
+
/**
|
|
121
|
+
* LoRA pulls: the catalog's `image-lora` entries, installed by whichever
|
|
122
|
+
* provider owns the engine. A provider without LoRA support fails the pull
|
|
123
|
+
* with a plain message.
|
|
124
|
+
*/
|
|
125
|
+
export declare function imageLoraPullResolver(imageProvider: {
|
|
126
|
+
current(): Promise<Pick<ImageProvider, 'pullLora'>>;
|
|
127
|
+
}, catalog: CatalogService): ImagePullResolver;
|
|
106
128
|
export {};
|
|
@@ -25,11 +25,21 @@ const FINISHED_TTL_MS = 8_000;
|
|
|
25
25
|
export class ImageModelPullRegistry {
|
|
26
26
|
entries = new Map();
|
|
27
27
|
controllers = new Map();
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
resolve;
|
|
29
|
+
kind;
|
|
30
|
+
/**
|
|
31
|
+
* Image models by default; `resolve` makes it a registry for another kind
|
|
32
|
+
* of download (LoRAs), whose ids then never share keys with models.
|
|
33
|
+
*/
|
|
30
34
|
constructor(opts) {
|
|
31
|
-
|
|
32
|
-
|
|
35
|
+
if ('resolve' in opts) {
|
|
36
|
+
this.resolve = opts.resolve;
|
|
37
|
+
this.kind = opts.kind;
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
this.resolve = imageModelPullResolver(opts.imageProvider, opts.catalog);
|
|
41
|
+
this.kind = 'image-model';
|
|
42
|
+
}
|
|
33
43
|
}
|
|
34
44
|
/**
|
|
35
45
|
* Start a pull for `id`. Idempotent — when a pull is already in flight
|
|
@@ -47,40 +57,21 @@ export class ImageModelPullRegistry {
|
|
|
47
57
|
}
|
|
48
58
|
// Resolve the catalog entry up-front so we can fail fast with a
|
|
49
59
|
// proper 404 rather than emitting a 200 + error SSE for "unknown id".
|
|
50
|
-
const
|
|
51
|
-
if (!
|
|
52
|
-
throw new UnknownImageModelError(id);
|
|
53
|
-
}
|
|
54
|
-
const manifest = item.manifest;
|
|
60
|
+
const job = await this.resolve(id);
|
|
61
|
+
if (!job)
|
|
62
|
+
throw new UnknownImageModelError(id, this.kind);
|
|
55
63
|
const snapshot = {
|
|
56
64
|
id,
|
|
57
65
|
startedAt: new Date().toISOString(),
|
|
58
66
|
bytesWritten: 0,
|
|
59
|
-
totalBytes:
|
|
60
|
-
manifest.auxiliaryFiles.reduce((n, f) => n + f.approxSizeBytes, 0),
|
|
67
|
+
totalBytes: job.totalBytes,
|
|
61
68
|
finished: false,
|
|
62
69
|
};
|
|
63
70
|
const entry = { snapshot, listeners: new Set() };
|
|
64
71
|
this.entries.set(id, entry);
|
|
65
72
|
const controller = new AbortController();
|
|
66
73
|
this.controllers.set(id, controller);
|
|
67
|
-
|
|
68
|
-
downloadUrl: manifest.downloadUrl,
|
|
69
|
-
sha256: manifest.sha256,
|
|
70
|
-
approxSizeBytes: manifest.approxSizeBytes,
|
|
71
|
-
name: manifest.name,
|
|
72
|
-
weightsKind: manifest.weightsKind,
|
|
73
|
-
...(manifest.supportsImg2Img !== undefined
|
|
74
|
-
? { supportsImg2Img: manifest.supportsImg2Img }
|
|
75
|
-
: {}),
|
|
76
|
-
auxiliaryFiles: manifest.auxiliaryFiles.map((aux) => ({
|
|
77
|
-
role: aux.role,
|
|
78
|
-
downloadUrl: aux.downloadUrl,
|
|
79
|
-
sha256: aux.sha256,
|
|
80
|
-
approxSizeBytes: aux.approxSizeBytes,
|
|
81
|
-
})),
|
|
82
|
-
};
|
|
83
|
-
void this.consume(id, spec, controller.signal);
|
|
74
|
+
void this.consume(id, job, controller.signal);
|
|
84
75
|
return { snapshot: { ...snapshot }, alreadyRunning: false };
|
|
85
76
|
}
|
|
86
77
|
list() {
|
|
@@ -148,13 +139,12 @@ export class ImageModelPullRegistry {
|
|
|
148
139
|
if (snapshot.finished && !snapshot.error)
|
|
149
140
|
listener({ type: 'done', id: snapshot.id });
|
|
150
141
|
}
|
|
151
|
-
async consume(id,
|
|
142
|
+
async consume(id, job, signal) {
|
|
152
143
|
const entry = this.entries.get(id);
|
|
153
144
|
if (!entry)
|
|
154
145
|
return;
|
|
155
146
|
try {
|
|
156
|
-
const
|
|
157
|
-
for await (const event of provider.pullModel(id, spec, signal)) {
|
|
147
|
+
for await (const event of job.run(signal)) {
|
|
158
148
|
this.applyEvent(entry, event);
|
|
159
149
|
this.broadcast(entry, event);
|
|
160
150
|
if (event.type === 'done' || event.type === 'error')
|
|
@@ -171,7 +161,8 @@ export class ImageModelPullRegistry {
|
|
|
171
161
|
finally {
|
|
172
162
|
entry.snapshot.finished = true;
|
|
173
163
|
// Drop the controller — cancel() should be a no-op once finished.
|
|
174
|
-
this.
|
|
164
|
+
if (this.entries.get(id) === entry)
|
|
165
|
+
this.controllers.delete(id);
|
|
175
166
|
// Keep the snapshot around briefly so a client mid-reconnect still
|
|
176
167
|
// sees the terminal state on `list()` / `subscribe()`. Then garbage
|
|
177
168
|
// collect.
|
|
@@ -184,6 +175,9 @@ export class ImageModelPullRegistry {
|
|
|
184
175
|
}
|
|
185
176
|
applyEvent(entry, event) {
|
|
186
177
|
const s = entry.snapshot;
|
|
178
|
+
// Publish terminal state before generator cleanup can yield to a new subscriber.
|
|
179
|
+
if (event.type === 'done' || event.type === 'error')
|
|
180
|
+
s.finished = true;
|
|
187
181
|
if (event.type === 'progress') {
|
|
188
182
|
s.bytesWritten = event.bytesWritten;
|
|
189
183
|
if (event.totalBytes !== undefined)
|
|
@@ -219,9 +213,77 @@ export class ImageModelPullRegistry {
|
|
|
219
213
|
}
|
|
220
214
|
export class UnknownImageModelError extends Error {
|
|
221
215
|
id;
|
|
222
|
-
constructor(id) {
|
|
223
|
-
super(`unknown
|
|
216
|
+
constructor(id, kind = 'image-model') {
|
|
217
|
+
super(`unknown ${kind}: ${id}`);
|
|
224
218
|
this.id = id;
|
|
225
219
|
this.name = 'UnknownImageModelError';
|
|
226
220
|
}
|
|
227
221
|
}
|
|
222
|
+
function imageModelPullResolver(imageProvider, catalog) {
|
|
223
|
+
return async (id) => {
|
|
224
|
+
const item = await catalog.get('image-model', id);
|
|
225
|
+
if (!item || item.manifest.kind !== 'image-model')
|
|
226
|
+
return null;
|
|
227
|
+
const manifest = item.manifest;
|
|
228
|
+
const spec = {
|
|
229
|
+
downloadUrl: manifest.downloadUrl,
|
|
230
|
+
sha256: manifest.sha256,
|
|
231
|
+
approxSizeBytes: manifest.approxSizeBytes,
|
|
232
|
+
name: manifest.name,
|
|
233
|
+
weightsKind: manifest.weightsKind,
|
|
234
|
+
...(manifest.supportsImg2Img !== undefined
|
|
235
|
+
? { supportsImg2Img: manifest.supportsImg2Img }
|
|
236
|
+
: {}),
|
|
237
|
+
...(manifest.referenceImages ? { referenceImages: manifest.referenceImages } : {}),
|
|
238
|
+
...(manifest.editOnly !== undefined ? { editOnly: manifest.editOnly } : {}),
|
|
239
|
+
auxiliaryFiles: manifest.auxiliaryFiles.map((aux) => ({
|
|
240
|
+
role: aux.role,
|
|
241
|
+
downloadUrl: aux.downloadUrl,
|
|
242
|
+
sha256: aux.sha256,
|
|
243
|
+
approxSizeBytes: aux.approxSizeBytes,
|
|
244
|
+
})),
|
|
245
|
+
};
|
|
246
|
+
return {
|
|
247
|
+
totalBytes: manifest.approxSizeBytes +
|
|
248
|
+
manifest.auxiliaryFiles.reduce((n, f) => n + f.approxSizeBytes, 0),
|
|
249
|
+
async *run(signal) {
|
|
250
|
+
const provider = await imageProvider.current();
|
|
251
|
+
yield* provider.pullModel(id, spec, signal);
|
|
252
|
+
},
|
|
253
|
+
};
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* LoRA pulls: the catalog's `image-lora` entries, installed by whichever
|
|
258
|
+
* provider owns the engine. A provider without LoRA support fails the pull
|
|
259
|
+
* with a plain message.
|
|
260
|
+
*/
|
|
261
|
+
export function imageLoraPullResolver(imageProvider, catalog) {
|
|
262
|
+
return async (id) => {
|
|
263
|
+
const item = await catalog.get('image-lora', id);
|
|
264
|
+
if (!item || item.manifest.kind !== 'image-lora')
|
|
265
|
+
return null;
|
|
266
|
+
const manifest = item.manifest;
|
|
267
|
+
const spec = {
|
|
268
|
+
downloadUrl: manifest.downloadUrl,
|
|
269
|
+
sha256: manifest.sha256,
|
|
270
|
+
approxSizeBytes: manifest.approxSizeBytes,
|
|
271
|
+
name: manifest.name,
|
|
272
|
+
baseModels: manifest.baseModels,
|
|
273
|
+
purpose: manifest.purpose,
|
|
274
|
+
strength: manifest.strength,
|
|
275
|
+
...(manifest.prompt ? { prompt: manifest.prompt } : {}),
|
|
276
|
+
...(manifest.render ? { render: manifest.render } : {}),
|
|
277
|
+
};
|
|
278
|
+
return {
|
|
279
|
+
totalBytes: manifest.approxSizeBytes,
|
|
280
|
+
async *run(signal) {
|
|
281
|
+
const provider = await imageProvider.current();
|
|
282
|
+
if (!provider.pullLora) {
|
|
283
|
+
throw new Error('This image engine cannot add styles or fast mode.');
|
|
284
|
+
}
|
|
285
|
+
yield* provider.pullLora(id, spec, signal);
|
|
286
|
+
},
|
|
287
|
+
};
|
|
288
|
+
};
|
|
289
|
+
}
|
|
@@ -15,10 +15,11 @@
|
|
|
15
15
|
* catalog manifest (so local state is self-describing even if the
|
|
16
16
|
* catalog entry later disappears).
|
|
17
17
|
*/
|
|
18
|
+
import { type ImageLoraPrompt, type ImageLoraPurpose, type ImageLoraRender } from '@bendyline/gezel';
|
|
18
19
|
import { type ModelStorageRoots } from '../../models/storage-roots.js';
|
|
19
20
|
import type { GpuArbiter } from '../gpu-arbiter.js';
|
|
20
21
|
import type { NativeEngineSupervisor } from '../native/supervisor.js';
|
|
21
|
-
import { type ImageEngineHealth, type ImageGenerationInput, type ImageGenerationOutput, type ImageGenerationProgress, type ImageModelPullEvent, type ImageModelPullSpec, type ImageProvider, type InstalledImageModelInfo } from './types.js';
|
|
22
|
+
import { type ImageEngineHealth, type ImageGenerationInput, type ImageGenerationOutput, type ImageGenerationProgress, type ImageLoraPullSpec, type ImageModelPullEvent, type ImageModelPullSpec, type ImageProvider, type InstalledImageLoraInfo, type InstalledImageModelInfo } from './types.js';
|
|
22
23
|
export interface StableDiffusionCppProviderOptions {
|
|
23
24
|
/** Base URL of the running sd-server (no trailing slash). */
|
|
24
25
|
baseUrl: string;
|
|
@@ -26,6 +27,13 @@ export interface StableDiffusionCppProviderOptions {
|
|
|
26
27
|
modelsRoot: string;
|
|
27
28
|
/** Optional read-only machine-store overlay. */
|
|
28
29
|
storageRoots?: ModelStorageRoots;
|
|
30
|
+
/**
|
|
31
|
+
* Where LoRAs install (`modelStorageRoots({ engine: 'sd-cpp-loras' })`).
|
|
32
|
+
* Only the writable root reaches the engine, which takes one
|
|
33
|
+
* `--lora-model-dir`. Absent when the engine is someone else's server
|
|
34
|
+
* (`GEZEL_SD_SERVER_URL`): renders then go without LoRAs, with a note.
|
|
35
|
+
*/
|
|
36
|
+
loraRoots?: ModelStorageRoots;
|
|
29
37
|
/** Awake-time budget for one render request, in ms. Defaults to 30 minutes. */
|
|
30
38
|
timeoutMs?: number;
|
|
31
39
|
/** Injected for tests. Defaults to global fetch. */
|
|
@@ -130,6 +138,8 @@ export interface SdServerLoadProfile {
|
|
|
130
138
|
* measured not to change.
|
|
131
139
|
*/
|
|
132
140
|
stepCache?: 'easycache';
|
|
141
|
+
/** `--model-args` key=value list. See MODEL_ARGS. */
|
|
142
|
+
modelArgs?: string;
|
|
133
143
|
}
|
|
134
144
|
export declare function sdServerLoadProfile(modelId: string): SdServerLoadProfile;
|
|
135
145
|
export declare class StableDiffusionCppProvider implements ImageProvider {
|
|
@@ -137,6 +147,7 @@ export declare class StableDiffusionCppProvider implements ImageProvider {
|
|
|
137
147
|
private readonly baseUrl;
|
|
138
148
|
private readonly modelsRoot;
|
|
139
149
|
private readonly storageRoots;
|
|
150
|
+
private readonly loraRoots?;
|
|
140
151
|
private readonly timeoutMs;
|
|
141
152
|
private readonly fetchImpl;
|
|
142
153
|
private readonly supervisor?;
|
|
@@ -150,6 +161,28 @@ export declare class StableDiffusionCppProvider implements ImageProvider {
|
|
|
150
161
|
generate(input: ImageGenerationInput): Promise<ImageGenerationOutput>;
|
|
151
162
|
private generateInner;
|
|
152
163
|
listInstalledModels(): Promise<InstalledImageModelInfo[]>;
|
|
164
|
+
/** Installed LoRAs, from the root the engine reads; none when this engine takes none. */
|
|
165
|
+
listLoras(): Promise<InstalledImageLoraInfo[]>;
|
|
166
|
+
/**
|
|
167
|
+
* Install one LoRA: a single safetensors file, sha256-checked like a model,
|
|
168
|
+
* beside a `manifest.json` that keeps the catalog's recipe so the engine can
|
|
169
|
+
* apply it without the catalog.
|
|
170
|
+
*/
|
|
171
|
+
pullLora(id: string, spec: ImageLoraPullSpec, signal?: AbortSignal): AsyncIterable<ImageModelPullEvent>;
|
|
172
|
+
deleteLora(id: string): Promise<void>;
|
|
173
|
+
/**
|
|
174
|
+
* The LoRAs a render asked for that this engine can apply to `modelId`:
|
|
175
|
+
* installed in the root the engine reads and checked on that model. The
|
|
176
|
+
* rest are dropped with a note, never an error.
|
|
177
|
+
*/
|
|
178
|
+
private resolveLoras;
|
|
179
|
+
/**
|
|
180
|
+
* Keep the LoRAs the running engine can see. sd-server fills its LoRA list
|
|
181
|
+
* only when it is asked for it, and answers `invalid lora path` for any LoRA
|
|
182
|
+
* named before then, so the list is read before every LoRA render.
|
|
183
|
+
*/
|
|
184
|
+
private lorasTheEngineHas;
|
|
185
|
+
private readInstalledLora;
|
|
153
186
|
pullModel(id: string, spec: ImageModelPullSpec, signal?: AbortSignal): AsyncIterable<ImageModelPullEvent>;
|
|
154
187
|
/**
|
|
155
188
|
* Stream a single URL to disk with sha256 verification, reporting cumulative
|
|
@@ -167,11 +200,13 @@ export declare class StableDiffusionCppProvider implements ImageProvider {
|
|
|
167
200
|
health(): Promise<ImageEngineHealth>;
|
|
168
201
|
shutdown(): Promise<void>;
|
|
169
202
|
/**
|
|
170
|
-
* The
|
|
171
|
-
*
|
|
172
|
-
* {@link listInstalledModels} so the default is
|
|
203
|
+
* The model a fresh picture renders with: the configured default when it
|
|
204
|
+
* is installed and draws, else the first installed model that draws. Stable
|
|
205
|
+
* ordering matches {@link listInstalledModels} so the default is
|
|
206
|
+
* deterministic.
|
|
173
207
|
*/
|
|
174
208
|
resolveDefaultModelId(): Promise<string | undefined>;
|
|
209
|
+
private chooseModel;
|
|
175
210
|
private defaultSampleSteps;
|
|
176
211
|
/**
|
|
177
212
|
* Read the installed `manifest.json` fields that steer per-model
|
|
@@ -182,6 +217,30 @@ export declare class StableDiffusionCppProvider implements ImageProvider {
|
|
|
182
217
|
*/
|
|
183
218
|
private readInstalledManifestFields;
|
|
184
219
|
}
|
|
220
|
+
/**
|
|
221
|
+
* sd-server reads `<sd_cpp_extra_args>{json}</sd_cpp_extra_args>` out of a
|
|
222
|
+
* prompt and applies the JSON as generation parameters, including LoRA and
|
|
223
|
+
* ADetailer model paths it never checks. Prompts come from gezels reading
|
|
224
|
+
* untrusted content and from other apps, so the tag never reaches the engine.
|
|
225
|
+
* Repeated until stable: removing one tag can join the pieces around it into
|
|
226
|
+
* another.
|
|
227
|
+
*/
|
|
228
|
+
export declare function withoutEngineDirectives(text: string): string;
|
|
229
|
+
/** An installed LoRA with the recipe its catalog entry gave it. */
|
|
230
|
+
export interface InstalledLora {
|
|
231
|
+
id: string;
|
|
232
|
+
name: string;
|
|
233
|
+
filename: string;
|
|
234
|
+
purpose: ImageLoraPurpose;
|
|
235
|
+
strength: number;
|
|
236
|
+
baseModels: string[];
|
|
237
|
+
prompt?: ImageLoraPrompt;
|
|
238
|
+
render?: ImageLoraRender;
|
|
239
|
+
approxSizeBytes: number;
|
|
240
|
+
installedAt: string;
|
|
241
|
+
}
|
|
242
|
+
/** Every LoRA installed under `loraRoot`, skipping malformed ones. */
|
|
243
|
+
export declare function readInstalledLoras(loraRoot: string): Promise<InstalledLora[]>;
|
|
185
244
|
/**
|
|
186
245
|
* Parse one line of sd-server's weight-load bar, which it prints inside the
|
|
187
246
|
* first request after a launch because it loads weights lazily:
|